当前位置: 首页 > news >正文

Linux .ko字符串驱动模块编写

Linux分为内核态和用户态

实则就是分为了用户操作空间和内核操作空间

Linux驱动开发分为两种,可以将驱动编译到内核kernel中即image,或者module中,即.ko文件,内核文件编译比较繁杂,通常编译到.ko文件中

//在工程文件中查找module_init()函数,可以看见很多模块都是在里面
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("You");
MODULE_DESCRIPTION("Simple test character device module");static int __init mymodule_init(void){    printk("mymodule_init\n");    return 0;}static void __exit mymodule_exit(void){    printk("mymodule_exit\n");}
/*
*   模块的出口与入口函数
*/
module_init(mymodule_init);
module_exit(mymodule_exit);

这是一个最简单的字符串设备的驱动注册程序,这个代码的编写并没有难度,完全按照Linux官方的格式编写。难点在于这个程序的编译,Linux的编译多数使用Makefile文件,发展到今天已经形成了标准化的格式。对于模块驱动(.ko)的编译也不例外,使用标准格式即可。以下是Makefile文件的编写

KERNEL := /home/pro/prj/k230_linux_sdk/output/k230_canmv_lckfb_defconfig/build/linux-7d4e1f444f461dbe3833bd99a4640e7b6c2cd529
INC := /opt/toolchain/Xuantie-900-gcc-linux-6.6.0-glibc-x86_64-V3.0.2/include
CURRENT_PATH := $(shell pwd)
obj-m := mymodule.o
# Cross-compiler prefix (no trailing gcc) — used by kernel build system
CROSS_COMPILE := /opt/toolchain/Xuantie-900-gcc-linux-6.6.0-glibc-x86_64-V3.0.2/bin/riscv64-unknown-linux-gnu-
# Target architecture
ARCH ?= riscvbuild: kernel_moduleskernel_modules:
    $(MAKE) -C $(KERNEL) M=$(CURRENT_PATH) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modulesclean:
    $(MAKE) -C $(KERNEL),$(INC) M=$(CURRENT_PATH) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) clean

字符串设备

注册设备我们需要使用一个函数register_chrdev,并且在卸载设备的时候也需要先注销一个注册的设备使用函数unregister_chrdev。
register_chrdev函数用于注册字符设备,此函数一共有三个参数,这三个参数的含义如下:

  • major:主设备号,Linux下每个设备都有一个设备号,设备号分为主设备号和次设备号两 部分,关于设备号后面会详细讲解。
  • name:设备名字,指向一串字符串。
  • fops:结构体file_operations类型指针,指向设备的操作函数集合变量。
    unregister_chrdev函数用户注销字符设备,此函数有两个参数,这两个参数含义如下:
  • major:要注销的设备对应的主设备号。
  • name:要注销的设备对应的设备名。
//设备号的原始类型,即一个无符号的32位整型
typedef u32 __kernel_dev_t;typedef __kernel_fd_set     fd_set;
typedef __kernel_dev_t      dev_t;

Linux内核将设备号分为两类,主设备号和次设备号,故主设备号会占用高12位,次设备号占用低20位。

register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);
unregister_chrdev(unsigned int major, const char *name);

这两个函数的注册方式在现在看来过于片面,可以在第一个参数中看出,在注册设备时只能填写major,但在一个设备注册时应该有主设备和次设备号互相作用的。这样就导致了注册到一个MAJOR时会直接忽略掉次设备号的全部字段, 2^12 = 4096个设备号被浪费。

const struct file_operations

这是Linux设备的属性结构体,其中定义了许多设备功能,就比如一个设备只有在注册的时候拥有open、close、read和write等等,这样在C语言中调用open函数这些函数时才有效。在内核文件Linux/include/fs.h中有着完整的结构体定义。

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
    int (*iopoll)(struct kiocb *kiocb, struct io_comp_batch *,
            unsigned int flags);
    int (*iterate_shared) (struct file *, struct dir_context *);
    __poll_t (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    unsigned long mmap_supported_flags;
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    void (*splice_eof)(struct file *file);
    int (*setlease)(struct file *, int, struct file_lock **, void **);
    long (*fallocate)(struct file *file, int mode, loff_t offset,
              loff_t len);
    void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
    unsigned (*mmap_capabilities)(struct file *);
#endif
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
            loff_t, size_t, unsigned int);
    loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
                   struct file *file_out, loff_t pos_out,
                   loff_t len, unsigned int remap_flags);
    int (*fadvise)(struct file *, loff_t, loff_t, int);
    int (*uring_cmd)(struct io_uring_cmd *ioucmd, unsigned int issue_flags);
    int (*uring_cmd_iopoll)(struct io_uring_cmd *, struct io_comp_batch *,
                unsigned int poll_flags);
} __randomize_layout;

字符设备驱动的函数实现

上面了解到一个字符串设备是否有功能,完全取决于动作结构体的实现。


待更新

http://www.rkmt.cn/news/193657.html

相关文章:

  • 东方博宜OJ 2142:福布斯富豪排行榜 ← 结构体 + 结构体排序
  • 2025年度盘点:国内喷淋塔除尘器口碑排行榜,静电除尘器/水帘除尘器/滤筒除尘器喷淋塔除尘器直销厂家排行 - 品牌推荐师
  • YOLOFuse API封装示例:构建RESTful接口供前端调用
  • YOLOFuse CSDN博客同步更新:中文开发者首选平台
  • YOLOFuse实战教程:如何在复杂环境下提升检测精度?
  • YOLOFuse高并发处理能力:支持千级请求同时响应
  • YOLOFuse培训课程预告:线上直播讲解高级用法
  • 导师推荐10个AI论文软件,自考毕业论文格式规范必备!
  • ‌智能测试预言机在金融系统的落地实践
  • 半挂汽车列车横向稳定性控制:基于TruckSim与Simulink联合仿真 - 详解
  • 三星电视整合Google Photos功能:AI照片管理与专属应用集成
  • YOLOFuse伦理准则声明:拒绝用于侵犯隐私的监控
  • YOLOFuse无人机巡检应用案例:电力线路夜间故障识别
  • VSCode Lite Edit 主题使用记录
  • 导师严选2025 TOP10 AI论文网站:专科生毕业论文写作全测评
  • YOLOFuse边缘计算适配进展:轻量化版本正在开发中
  • YOLOFuse推理脚本infer_dual.py实战应用技巧分享
  • YOLOFuse离线部署方案:支持内网环境下的镜像导入与运行
  • 一键永久关闭windows自动更新,让你再也见不到烦人的自动更新了。win10/win11系统永久禁止自动更新。
  • YOLOFuse B站视频频道上线:手把手教学视频发布
  • YOLOFuse搭配FastStone Capture注册码?截图工具推荐替代方案
  • YOLOFuse掘金社区合作:前端后端AI全栈开发者覆盖
  • YOLOFuse企业版推出:专属技术支持与SLA保障
  • YOLOFuse与原版YOLOv8的区别:为什么需要专为双模态设计?
  • YOLOFuse镜像版本管理:如何获取最新版与历史版本?
  • YOLOFuse创业项目起点:基于此镜像开发SaaS检测服务
  • 仅剩3%误差空间!顶尖工程师分享TinyML模型C部署精度调优秘技
  • YOLOFuse Discord服务器邀请:全球开发者即时沟通
  • YOLOFuse release版本命名规则解释:v1.0.0含义解析
  • YOLOFuse与JavaScript结合:前端调用Python后端API设想