LINUX内核字符设备驱动模型

时间:2022-07-28 17:54:27

转载请注明出处玮璘博客:http://www.wangweilin.name/qrx_455.html
字符设备设备号分配

关键结构体:

static structchar_device_struct {

         struct char_device_struct *next;     //冲突链表指针

         unsigned int major;               //主设备号

         unsigned int baseminor;           //最小的从设备号

         int minorct;                     //从设备数,即同一个驱动下的设备文件个数

         const char *name;               //设备名

         struct file_operations *fops;       //文件操作,未使用

         struct cdev *cdev;                  /* will die */  //内核需要的cdev结构,未使用

}*chrdevs[MAX_PROBE_HASH];

#defineMAX_PROBE_HASH 255        /* random */   //散列表大小

 

intregister_chrdev_region(dev_t from, unsigned count, const char *name)

intalloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char*name)

两个都是通过调用static struct char_device_struct*__register_chrdev_region(unsigned int major, unsigned int baseminor, intminorct, const char *name)来获得设备号。

 LINUX内核字符设备驱动模型

1.  定义一个255个元素的散列表,每个元素保存struct char_device_struct类型结构指针的头结点

2.  然后通过此结构中的next成员来处理冲突问题(链式解决冲突法)

3.  这个链式的冲突链表是按照主设备号递增,当主设备相同按从设备号递增,例如chrdevs单元1为major % 255 = 0,此元素格中存放的是major % 255 = 0,major号最小的,0 . 0-->255 . 1-->510 . 1-->510 . 5…,小数点后面的为从设备号

 

字符设备模型

关键结构体:

struct kobj_map {

         struct probe {

                   struct probe *next;     //对象模式的冲突链表指针

                   dev_t dev;            //设备号

                   unsigned long range;   //dev+range为有效,见下面

                   struct module *owner;  // THIS_MODULE

                   kobj_probe_t *get;     // data为cdev结构,get(data)得到cdev结构的成员kobj

                   int (*lock)(dev_t, void *);  //锁

                   void *data;           //在字符设备中就为cdev

         } *probes[255];

         struct semaphore *sem;

};

LINUX内核字符设备驱动模型

1.  内核启动时通过调用void __init chrdev_init(void)对cdev_map即kobj_map进行初始化,共为255个struct probe类型的项(初始255大小的散列表)

2.   probes[255]中的第一项存放着和major %255得到的值相关的struct probe类型的元素指针,structprobe中的next是用来处理链表冲突

3.  冲突链表是通过struct probe的成员range,按递减的方式进行连接的,与主设备号无关。

4.  struct probe中的dev号用作可用设备起始号,range为区间,即dev~dev+range这个范围。kobj_lookup时inode的i_rdev号在此范围内才能正确的进行分配。

 

CHAR型设备OPEN系统调用

1.  应用程序的open反汇编后,可以看到是通过int 80 系统指令来进入内核的。其中eax中存放着调用号,即是要运行open,read还是write。

2.  在kernel_init()时,内核最开始的初始化,会调用trap_init()在其中会设中断号80为系统调用,并传入一个系统调用预处理函数地址,此函数能过传入的eax来确实执行具体哪个系统调用,此处为sys_open()(fs/open.c)

3.  在sys_open()中会调用到flip_open(),其中又会调用到dentry_open(),里面有一条f->f_op=fops_get(inode->i_fop)用来返回此i节点上的文件操作函数指针。

4.  然后调用f->f_op->open()即最终调用了chrdev_open()函数。

提示:insmod module时,设备的inode节点的成员i_fop在init_special_inode()函数中被初始化为&def_chr_foprs,def_chr_foprs结构体中存放的是chrdev_open()函数的地址。

 

1.  对于应用程序在open中传入的O_RW, O_RDONLY等,都是在VFS层中处理,所以字符设备驱动本身中的open,read,write等不需要做处理。

2.  在chrdev_open()中,inode的成员值被完善,所以执行完第一个open之后应用程序中的read, write还有open在VFS层中便可以被直接调用到,即f->f_op->open()已经指向了字符设备的open()

3.  对于open中的mode(NOBLOCK..),flag(O_RW, O_RDONLY)参数,mode与默认的i_fop在insmod期间被设置,i_flags在open系统调用到真正运行到chrdev_open()期间被检查,即驱动程序不需要检查O_RW,O_RDONLY等

 

intchrdev_open(struct inode * inode, struct file * filp)

1.  通过cdv_map找到inode->i_rdev

2.  通过kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);找到kobj

3.  然后通过此kobj找到和它相关的cdev,inode->i_cdev =cdev(赋值到inode中,scullc_open()会将此inode->icdev赋值到filp->private_data中,方便scullc_read(), scullc_write()使用)

4.  ret = filp->f_op->open(inode,filp);调用cdev中的open函数,即字符驱动的open()

 

扩展

insmod module major =516,主设备号可以大于255,但是在mknod中的主设备号,要写此516 % 255的值。