简图记录学习~
参考:linux设备驱动第3版
一、概念:
设备驱动 :内核中负责控制硬件设备的内核模块,隐藏硬件操作细节,提供内部定义良好的编程接口。
设备驱动分类:
1、字符设备(通过字符设备节点 按数据流 访问设备,一般不可寻址)
2、块设备(通过块设备节点 按一个或多个数据块如512Byte访问的设备)
3、网络接口 ( 不存在设备节点、由系统分配名称如eth0 通过专用接口访问)
设备节点:用于访问设备驱动的特殊文件、用户通过操作设备节点控制驱动设备(一般在创建在/dev下)
设备号:linux通过设备号dev_t标示设备,实际为u32类型,其中高12bit标示主设备号(指定设备驱动或设备类型),低20bit为次设备号(指定具体设备)。
(可通过ls-l查看设备节点看设备号,也可通过cat /proc/devices查看系统分配的主设备号)
可卸载内核模块:linux提供系统运行时向内核添加模块的功能(不限于设备驱动 lsmod查看 insmod加载 rmmod卸载)
二、字符设备驱动基本流程
1、申请设备号(可静态注册register_chrdev_region或动态申请alloc_chrdev_region)
2、创建字符设备节点(可手动通过mknod创建 或 通过内核类动态创建class_create、device_create)
3、定义文件操作集进行字符设备初始化cdev_init
4、 注册字符设备cdev_add
/*****使用设备驱动*****/
卸载流程:注销字符设备cdev_del、删除设备节点device_destroy class_destroy(若自动创建)、注销设备号unregister_chrdev_region
三、关键数据结构
1、 文件操作集struct file_operations 相关重要成员原型:
打开int (*open) (struct inode *, struct file *);
关闭int (*release) (struct inode *, struct file *);
读取ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
写入ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
操作int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
2、struct file文件描述符(对应进程中一个打开的文件、open时创建、close时销毁)
3、struct inode索引节点(对应系统中一个文件)
四、扩展讲解
1、防止多进程驱动资源溢出的技巧:在open设备节点时为file->private_data申请一个结构体挂上、用于记录所有操作过程申请的资源。当close时逐一检查释放(即使是 被ctrl+C系统也会关闭打开文件,所以不用担心进程被杀死时资源问题)
2、用户与内核态数据交换要求使用专用接口copy_to_user/copy_from_user而不用memcpy,原因在于进程分配的虚拟地址可能并未真正映射到物理内存,因此可能产生缺页异常导致memcpy发生opps。
3、proc调试功能添加。proc是内核提供的特殊虚拟文件系统,用于向外导出内核信息和接收信息进行调试。相关接口proc_create/remove_proc_entry
4、iotcl参数讲解:第三个参数cmd表示命令号,由dir数据传输方向、type设备驱动类型标示(用户定义)、nr命令序号(用户定义)、size 接口第四个入参实际结构大小(用于校验,一般入参都是指针,这里标明对应结构体大小)
内核提供专用接口(宏)帮助生成cmd:无数据传输_IO(type,nr) 读_IOR(type,nr,size),写_IOW(type,nr,size)、读写_IOWR(type,nr,size)