设备驱动程序(二)

来源:互联网 发布:产品数据分析指标 编辑:程序博客网 时间:2024/06/08 15:17

三、块设备

             1、申请与释放设备编号

                   块设备驱动程序在使用前,必须向内核注册设备号,内核函数原型:

                   int register_blkdev(unsigned int major,const char *name);

                  major是需要注册的主设备号;name为驱动程序在/proc/devices中显示的名字。在驱动程序卸载时,原先注册的主设备号需要归还组内核,

                  int unregister_blkdev(unsinged int major,const char *name);

             2、注册磁盘

                 驱动程序通过内核函数register_bkldev()注册主设备号后,并不能让系统使用任何磁盘。内核中由gendisk结构表示一个独立的磁盘设备,块设备驱动程序在注册主设备号之后,需要设置gendisk结构,并将该结构添加到内核中。

                 

                   若磁盘设备是可被分区,每个分区都需要分配一个次设备号。minors成员常取16.。fops是块设备提供给内核的接口   内核通过这些接口完成用户对设备的驱动操作。 queue成员是块设备所用到的请求队列。capacity是以512B为一个扇区时,该设备所包含的扇区数;驱动程序不能直接设置该值而要通过set_capacity()内核函数来设置。

                 gendisk是一个动态分配的结构它需要内核的特殊处理来进行初始化,驱动程序不能分配该结构 ,须执行内核函数:

                  struct gendisk *alloc_disk(int minors);

                   参数minors是该磁盘使用的次设备号的数量。gendisk申请后,其值是不能改变的。用户不再需要使用gendisk时,必须通过del_gendisl()函数将gendisk结构删除。

驱动程序将gendisk设置完后,需要将gendisk注册进内核。:

                   void add_disk(struct gendisk *gd);

                  一旦调用add_disk,内核就有可能随时操作gendisk。因此gendisk只有在初始化完毕后,才能调用add_disk进行注册。

                  在gendisk结构中,fops的类型为block_devic_operations,该结构主要成员:

                 

          int(*open)(struct inode*,struct file*);          int (*release)(struct inode*,struct file*);          int(*ioctl)(struct inode*,stuct file*,unsigned,unsigned long);          int(*media_changed)(struct gendisk*);          int(*revalidate_disk)(struct gendisk*);


                 3、bio结构体

                   自v2.5内核版本起,为块设备引入一种新型、灵活的容器bio结构体来实现I/O。该结构体代表正在活动的、以片段链表形式组织的块I/Or操作,一个片段是需要传输的一小块连续的主存缓冲区。这样,就不需要保证单个 缓冲区一定结。

                   

                       






                 使用bio结构体的目的主要是代表正在现场执行的I/O操作,该结构体中的主要域都是用来管理相关信息的。

                                      

                 bi_io_vec域指向一个bio_vec结构体数组,该结构体链表包含一个特定I/O操作所需要使用的所有的片段。每个bio_vec结构都是一个形式为<page,offset,len>的向量,描述为:片段所在的页框、块在页框中的偏移位置、从给定偏移量开始的块长度。bio_vec 结构描述I/O缓冲区,整个bio_io_vec结构体数组表示一个完整的缓冲区。

                  

                     在每次启动一个新的I/Or操作时,需要通过内核函数bio_alloc()申请一个新的bio结构。操作bio结构体的内核函数:

                     bio_alloc()、bio_get()和bio_out()管理bio结构体的分配、引用计数和bio结构体的释放。

                 4、  请求队列

                     扶起的块I/O请求保存在请求队列中,该队列由request_queue结构体表示。请求队列表中的每一项都是一个单独的请求,由request结构体表示 。

                     请求结构体request:

                      

                   

                       请求队列结构定义:

                        

                          块的读写操作是通过由内核经调用的generic_file_read()和blkdev_file_write()内核函数完成对设备的读写。

                          即当用户对设备发出读操作时,generic_file_read()将一个读请求发送给设备驱动程序,并积存在该设备的请求队列中。内核对这些请求进行调度,并将请求交给驱动程序执行。

                             创建和初始化请求队列的内核函数是:

                           request_queue_t *blk_init_queue(queue_fn_proc *rfn,spinlock_t*lock);

                          其中,rfn是驱动程序提供的一个函数指针,该函数是真正用于实现设备的按读写请求;lock是驱动程序提供的一个自旋锁,内核通过该自旋锁实现请求队列的同步保护;函数的返回值是一个请求队列的指针。当请求队列不再使用时,驱动程序需要将请求队列归还:

                          void blk_cleanup_queue(request_queue_t*q);

                         请求队列中,的成员queuedata,它是供驱动程序自行使用的,若驱动程序用该成员指向一段动态分配的内存,则在卸载驱动程序时,要负责将该段主存回收。请求队列的每一个读写请求在内核中用request结构定义,request结构与驱动程序相关的成员:

                              struct request{

                                  sector_t sector;//请求的开始扇区的索引号

                                  unsigned  long nr_sectors;//传输的扇区数

                                 /*buffer是要传输或要接受数据的缓冲区指针,该指针在内核 的虚拟地址中,驱动程序可以直接引用它。*/

                             }

                       当创建请求队列时,需要绑定一个request()内核函数,用于处理请求队列中请求:

                         void request(request_queue_t*queue);

                         块设备的读、写操作都是由request()完成 的。

                      5、页高速缓存

                        Linux页高速缓冲由address_space结构体描述:

                         

                            address_space结构与某些对象相关联。其中a_ops指向地址空间对象中的操作函数表,它由 address_space_operations结构表示:

                                   

0 0
原创粉丝点击