转载请注明出处玮璘博客: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)来获得设备号。
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;
};
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的值。