scull字符设备源代码完全解析
基础篇
首先是模块装载到内核时调用的初始化部分:int scull_init_module(void);
首先对于字符设备的访问时通过/dev下的设备名称进行的,在Linux操作系统的/dev下利用ls -l命令可以查看文件的详细信息,开头字母为c的即为字符设备文件,在日期前的两个数字就是相应设备的主设备号和次设备号。在本函数中如果scull_major不为0,则利用MKDEV(scull_major, scull_minor)函数获得设备号的dev_t类型,利用register_chrdev_region(dev, scull_nr_devs,
"scull")函数分配设备编号,参数scull_nr_devs为申请设备编号的个数。如果scull_major为0,则利用alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull")函数动态分配设备编号,利用MAJOR宏获得主设备编号。接下来632-635行是动态分配设备号失败情况的处理。
在讲解下面的函数之前要了解一些驱动程序中重要的数据结构:
主要是file_operations结构,里面主要存放一些设备方法的函数指针。
file结构,系统为每个打开的文件在内核中对应一个file结构,其中一个重要的成员就是一个file_operation结构的指针。
inode结构,需要注意的是对于单个文件也许有多个file结构,但只会有一个inode结构。
在scull中,利用scull_dev结构表示每个设备,其定义如下:
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
好了,接着回到代码
程序的641行scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL)就是为了设备申请空间,大小为设备编号数目和表示每个设备的scull_dev结构大小。642行到644行是申请失败时的处理。下面memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev))函数来清空申请的内存。
接下来的初始化可以帮助我们理解scull内存使用的结构
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);
scull_setup_cdev(&scull_devices[i], i);
}
可以通过下面这张图来理解scull中的内存使用结构:
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);
scull_setup_cdev(&scull_devices[i], i);
}
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);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
我们一共申请了4个设备编号,要对四个设备的内存进行初始化,所以最外层的for循环很好理解
quantum表示量子的大小
qset表示数组的大小,即图中指向每个量子的指针数组的大小。
下面是信号互斥量的定义,以后再说
之后也是结构体的一些简单填充,在这里只是简单的赋值,只要熟悉即可,在下篇中的read函数中会详细讲到此结构体。
下面的部分函数可以暂时不看,今天只是刚刚接触到Linux驱动程序的开始,就先写这么多,还不太习惯,以后会常常更新,争取和大家共同进步。