完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
Linux字符设备驱动程序工作机理分析 1 本文主题 本文主要分析Linux字符设备驱动程序的工作机理。主要内容以及代码片段来源于《LDD3》,俺只是从另外一个角度来讲述。 见过很多关于驱动程序的书,基本上都是告诉你怎么做,然后你STEP BY STEP,然后运行完后结果就出来了,可是其背后到底是如何工作的呢?虽说《LDD3》也讲了很多原理性的东西,但是我觉得这个问题其描述得并不明确。 2 关于scull scull是《LDD3》的一个字符设备驱动程序,其加载之后会在文件系统下生成/dev/scull文件,在shell下可以对其进行一系列的操作,例如可以使用cp、dd或者输入输出重定向等命令来访问这个文件,也就是访问这个字符设备。 3 /dev/scull是如何生成的 该文件是在驱动程序模块加载时生成的,具体实现是在scull_load(可以到www.oreilly.com下载,或者《LDD3_中文》的P51)这个脚本里。 在shell下insmod了scull.ko之后,系统会在/proc/devices文件里生成设备名以及与之对应的主设备号信息,scull_load脚本根据该信息mknod了/dev/scull文件节点。 注意下mknod时给的参数信息,其中注明了主设备号以及次设备号信息。 4 shell下操作/dev/scull是如何关联到我们自己编写的scull模块的 这个问题一直困扰了我很久,不过现在我基本上算是弄明白了。 4.1 先回顾下scull模块程序的初始化操作里做了什么事情。 搞驱动的都知道,初始化的时候需要进行设备注册,核心代码如下: static void scull_setup_cdev(struct scull_dev *dev, int index) { int err, devno = MKDEV(scull_major, scull_minor + index); cdev_init(&dev->cdev, &scull_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &scull_fops; err = cdev_add(&dev->cdev, devno, 1); ... } scull_fops是一个数组,其中保存了我们的scull实际的操作: struct file_operation scull_fops = { ... .open = scull_open, .read = scull_read, .write = scull_write, .release = scull_release, ... } cdev是struct cdev类型(该类型由系统定义)的变量,其嵌入在struct scull_dev(该类型由scull模块定义)中: struct scull_dev{ ... struct cdev cdev; ... } 从这句代码 cdev_add(&dev->cdev, devno, 1); 我们可以发现,设备号devno已经和内核内部结构cdev关联起来了。 4.2 /dev/scull设备的打开操作 /dev/scull是文件系统中的一个设备文件,其在内核中由sturct inode结构表示。 一般说来,inode结构下由两个元素和驱动程序有关: dev_t i_rdev; 这个是设备号 struct cdev *i_cdev; 这里是一个指针,struct cdev是字符设备在内核里的表示,注意我们在4.1小节里也提到了这个结构。 也就是说,系统在打开/dev/scull之前仅知道inode信息。在4.1小节里已经说过了,设备号和内核内部结构cdev关联起来了,因此cdev下的ops也和设备号关联起来了,ops已经初始化为scull_fops。所以我们才能够通过inode中的设备号i_rdev定位到此时打开的是哪一个设备,从而去调用相应设备的open操作。 看看open函数的原型吧: int (*open)(struct inode*, struct file*); 第一个参数是struct inode类型的指针,该指针由内核传递过来,是设备文件在内核中的表示。 第二个参数struct file类型的指针,open之后,设备文件在内核中就以struct file结构来表示了。 看看open操作在scull里的实现吧: int scull_open(struct inode *inode, struct file *filp) { struct scull_dev *dev; dev = container_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; ... } 第一句代码通过宏container_of把包裹inode->i_cdev的结构提取出来了,该结构就是在scull模块里定以的struct scull_dev,其中包含了scull模块所需要的私有信息。 第二句代码把该私有信息加入内核数据结构struct file,之前说过了,open后的文件在内核里以struct file结构表示。 4.3 /dev/scull的读写操作 先看下读写操作的函数原型: ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); ssize_t (*write)(struct file*, char __user *, size_t, loff_t *); 其参数内容我不做过多解释,注意其第一个参数是struct file类型的指针,该指针由内核传入,也就是open之后的那个文件指针。 通过这个指针,能够找到scull模块的私有数据结构struct scull_dev,并对其操作,我们可以从read/write操作在scull里的实现看到这个做法:更多学习交流可以加Q群 310341439 ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { sturct scull_dev *dev = filp->private_data; int quantum = dev->quantum, qset = dev->qset; ... } ssize_t scull_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { sturct scull_dev *dev = filp->private_data; int quantum = dev->quantum, qset = dev->qset; |
|
|
|
只有小组成员才能发言,加入小组>>
「含关键代码」基于AM3352/AM3354/AM3359的Linux开发案例分享
4894 浏览 0 评论
87411 浏览 0 评论
【高手问答】如何做到精通linux技术?资深工程师带你突破难点
4725 浏览 2 评论
3620 浏览 2 评论
解读Linux :先从创建一个文件夹用来存放jdk压缩文件开始
2483 浏览 0 评论
1999浏览 3评论
1286浏览 1评论
求解:aarch64交叉编译工具已经安装成功,环境变量已经配置,怎么将系统架构切换为ARM的架构
1344浏览 0评论
电脑和虚拟机可以互ping,电脑和开发板也可以互ping,但是虚拟机和开发板ping不通是什么原因
1236浏览 0评论
1174浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 07:16 , Processed in 1.053271 second(s), Total 68, Slave 51 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号