Linux Device Driver study: scull解析

历时将近两个月,终于化零为整,对《Linux设备驱动程序》(3rd edition)中第三章scull驱动有了详细的了解,下面对scull的驱动进行解析,方便以后的查看。


一、创建字符驱动的步骤:

1、获得设备号:

int register_chrdev_region(dev_t first, unsigned int count, char *name);

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

上述两个函数,前者为在确定主设备号(first)的时候,进行设备号的注册,后者为自动获得主设备号并注册(获得的设备号存入dev中)。

2、将设备操作函数与设备号联系到一起:

初始化:void cdev_init(struct cdev *dev, struct file_operations *fops);

添加到内核中:int cdev_add(struct cdev *dev, dev_t num, unsigned int count)


注意:步骤2用到的fops已经初始化完成,设计设备驱动的open,read,write,release等函数;而需要定义全局变量struct cdev my_cdev,或在驱动代码中利用kalloc对其分配空间。


二、代码例程:

可参考本书自带的例程,附件中的main.c中scull_init_module的实现。


三、scull驱动代码分析以及应用程序调用scull

1、scull驱动代码中的全局变量:struct scull_dev *scull_devices;           //通过在scull_init_module中为其分配空间;

scull_major, scull_minor:设备号

2、当scull驱动编译完成,且安装到内核中后,可利用应用程序进行读取来验证。如:file1=fopen("/dev/scull0", "rb+");  stringlen = fwrite(stringBuffer, 1, sizeof(stringBuffer), file1)  

3、关于fopen的实现:

a)应用程序在调用fopen库函数后,会执行底层应用函数接口int open(const char *path, int oflags),进而会执行到内核代码  long do_sys_open(int dfd, const char __user *filename, int flags, int mode)  (内核代码open.c文件中)。do_sys_open实现通过filename得到struct file *f,并把f与int fd绑定,返回fd。得到的fd即为open函数的返回值。

b)scull驱动中的open函数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; /* for other methods */

在scull驱动中主要是通过filp->private_data(指向dev),进行数据的write和read。

在看这段代码时,有很多疑问,如inode->i_cdev应该是指向的之前在scull驱动中定义的scull_devices.dev部分,那么是在什么时候jiangscull_devices.dev的地址赋值给的inode->i_cdev;再有就是scull_open与内核代码中的do_sys_open之间的关系。自己的理解如下:

(1)scull_init_module调用scull_setup_cdev函数,其中scull_setup_cdev实现cdev_init和cdev_add的功能,cdev_add实现告诉内核,对应的设备代码对应特定的设备文件和操作函数(scull_devices以及其中的f_ops),即在内核中进行注册,之后通过设备号就可找到对应的scull_devices。在进行节点创建时(mknod),会用到设备号(如:mknod /dev/scull0 -C $majorNumber 0),此时创建的inode,其i_cdev已经指向了对应特定设备号的scull_devices。

(2)内核源码do_sys_open主要实现通过文件路径path得到file指针f,并与对应的句柄fd绑定。

do_sys_open调用struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);do_filp_open函数中实质性的打开工作是do_last函数。

在do_last函数中,无论open_flag中的标志位判断该文件是不是需要被创建,最后都会调用函数nameidata_to_filp,nameidata_to_filp会调用__dentry_open,在__dentry_open函数中会调用到f->f_op->open也就是scull_open。

关于文件节点是否需要创建在调用f_op 的时刻会有不同,需要创建节点时,在__open_namei_create之后调用nameidata_to_filp;不需要创建节点时,在finish_open中调用nameidata_to_filp

(3)在scull驱动过程中,通过file->private_data来传递scull_devices的地址(主要在scull_open代码中实现),进而对scull_devices的data数据去进行读写。


4、scull_devices指向的数据空间(可参考,第三章61页的图片):

在进行scull_write和scull_read时,调用scull_follow函数,实现对数据空间的分配(kmalloc)

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 */
};

struct scull_qset {
void **data;
struct scull_qset *next;
};

struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};




参考资料:

1、《LInux设备驱动程序》第三章

2、Linux内核源码

3、关于内核代码open的解析:http://www.cublog.cn/u3/119372/showart_2518539.html

4、关于file->private_data   http://linux.chinaunix.net/techdoc/develop/2007/09/09/967423.shtml


你可能感兴趣的:(linux,struct,Semaphore,File,Module,linux内核)