标题:
Linux字符设备驱动程序工作机理分析
[打印本页]
作者:
yuyang911220
时间:
2017-6-17 09:58
标题:
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
里的实现看到这个做法:
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;
...
}
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0