DIY编程器网

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1141|回复: 0
打印 上一主题 下一主题

[待整理] 嵌入式Linux设备驱动开发之:块设备驱动编程

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-10 07:33:17 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
        11.4  块设备驱动编程

        块设备通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。它的驱动程序的编写过程与字符型设备驱动程序的编写有很大的区别。
         
        块设备驱动编程接口相对复杂,不如字符设备明晰易用。块设备驱动程序对整个系统的性能影响较大,速度和效率是设计块设备驱动程要重点考虑的问题。系统中使用缓冲区与访问请求的优化管理(合并与重新排序)来提高系统性能。
         
        1.编程流程说明

        块设备驱动程序的编写流程同字符设备驱动程序的编写流程很类似,也包括了注册和使用两部分。但与字符驱动设备所不同的是,块设备驱动程序包括一个request请求队列。它是当内核安排一次数据传输时在列表中的一个请求队列,以最大化系统性能为原则进行排序。在后面的读写操作时会详细讲解这个函数,图11.5为块设备驱动程序的流程图,请读者注意与字符设备驱动程序的区别。
       

        图11.5  块设备驱动程序流程图

         
        2.重要数据结构

        每个块设备物理实体由一个gendisk结构体来表示(在</linux/genhd.h>中定义),每个gendisk可以支持多个分区。
         
        每个gendisk中包含了本物理实体的全部信息以及操作函数接口。整个块设备的注册过程是围绕gendisk来展开的。在驱动程序中需要初始化的gendisk的一些成员如下所示。
         
        struct gendisk
        {
            int major;            /* 主设备号 */
            int first_minor;    /* 第一个次设备号 */
            int minors;          /* 次设备号个数,一个块设备至少需要使用一个次设备号,而且块设
                                备的每个分区都需要一个次设备号,因此这个成员等于1,则表明该块
                                设备是不可被分区的,否则可以包含minors &ndash; 1    个分区。*/
            char disk_name[32];        /* 块设备名称,在/proc/partions中显示 */
            struct hd_struct **part;    /* 分区表 */
            struct block_device_operations *fops;        /* 块设备操作接口,与字符设备的
                                                         file_operations结构对应*/
            struct request_queue *queue;    /* I/O请求队列 */
            void *private_data;        /* 指向驱动程序私有数据 */
            sector_t capacity;    /* 块设备可包含的扇区数 */
            …… /* 其他省略 */
        };
         
        与字符设备驱动程序一样,块设备驱动程序也包含一个在<linux/fs.h>中定义的block_device_operations结构,其定义如下所示。
         
        struct block_device_operations
        {
            int (*open) (struct inode *, struct file *);
            int (*release) (struct inode *, struct file *);
            int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
            long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
            long (*compat_ioctl) (struct file *, unsigned, unsigned long);
            int (*direct_access) (struct block_device *, sector_t, unsigned long *);
            int (*media_changed) (struct gendisk *);
            int (*revalidate_disk) (struct gendisk *);
            int (*getgeo)(struct block_device *, struct hd_geometry *);
            struct module *owner;
        };
         
        从该结构的定义中,可以看出块设备并不提供read()、write()等函数接口。对块设备的读写请求都是以异步方式发送到设备相关的request 队列之中。
         
        3.块设备注册和初始化

        块设备的初始化过程要比字符设备复杂,它既需要像字符设备一样在加载内核时完成一定的工作,还需要在内核编译时增加一些内容。块设备驱动程序初始化时,由驱动程序的init()完成。
        块设备的初始化过程如图11.6所示。
       

        图11.6  块设备驱动程序初始化过程

         
        (1)向内核注册。
        使用register_blkdev() 函数对设备进行注册。
         
        int register_blkdev(unsigned int major, const char *name);
         
        其中参数major为要注册的块设备的主设备号,如果其值等于0,则系统动态分配并返回主设备号。参数name为设备名,在/proc/devices中显示。如果出错,则该函数返回负值。
        与其对应的块设备的注销函数为unregister_blkdev(),其格式如下所示。
         
        int unregister_blkdev(unsigned int major, const char *name);
         
        其参数必须与注册函数中的参数相同。如果出错则返回负值。
         
        (2)申请并初始化请求队列。
        这一步要调用blk_init_queue()函数来申请并初始化请求队列,其格式如下所示。
         
        struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
         
        其中参数rfn是请求队列的处理函数指针,它负责执行块设备的读、写请求。参数lock为自旋锁,用于控制对所分配的队列的访问。
         
        (3)初始化并注册gendisk结构。
        内核提供的gendisk结构相关函数如表11-16所示。
        表11-16 gendisk结构相关函数
                                                                        函数格式
                       
                                                                        说明
                       
                                                                        struct gendisk *alloc_disk(int minors)
                       
                                                                        动态分配gendisk结构,参数为次设备号的个数
                       
                                                                        void add_disk(struct gendisk *disk)
                       
                                                                        向系统注册gendisk结构
                       
                                                                        void del_gendisk(struct gendisk *disk)
                       
                                                                        从系统注销gendisk结构
                       
         
        首先使用alloc_disk()函数动态分配gendisk结构,接下来,对gendisk结构的主设备号(major)、次设备号相关成员(first_minor和minors)、块设备操作函数(fops)、请求队列(queue)、可包含的扇区数(capacity)以及设备名称(disk_name)等成员进行初始化。
         
        在完成对gendisk的分配和初始化之后,调用add_disk()函数向系统注册块设备。在卸载gendisk结构的时候,要调用del_gendisk()函数。
         
        4.块设备请求处理

        块设备驱动中一般要实现一个请求队列处理函数来处理队列中的请求。从块设备的运行流程,可知请求处理是块设备的基本处理单位,也是最核心的部分。对块设备的读写操作被封装到了每一个请求中。
         
        已经提过调用blk_init_queue()函数来申请并初始化请求队列。表11-17列出了一些与请求处理相关的函数。
        表11-17 请求处理相关函数
                                                                        函数格式
                       
                                                                        说明
                       
                                                                        request_queue_t *blk_alloc_queue(int gfp_mask)
                       
                                                                        分配请求队列
                       
                                                                        request_queue_t *blk_init_queue
                                        (request_fn_proc *rfn, spinlock_t *lock)
                       
                                                                        分配并初始化请求队列
                       
                                                                        struct request *blk_get_request
                                        (request_queue_t *q, int rw, int gfp_mask)
                       
                                                                        从队列中获取一个请求
                       
                                                                        void blk_requeue_request(request_queue_t *q, struct request *rq)
                       
                                                                        将请求再次加入队列
                       
                                                                        void blk_queue_max_sectors
                                        (request_queue_t *q, unsigned short max_sectors)
                       
                                                                        设置最大访问扇区数
                       
                                                                        void blk_queue_max_phys_segments
                                        (request_queue_t *q, unsigned short max_segments)
                       
                                                                        设置最大物理段数
                       
                                                                        void end_request(struct request *req, int uptodate)
                       
                                                                        结束本次请求处理
                       
                                                                        void blk_queue_hardsect_size
                                        (request_queue_t *q, unsigned short size)
                       
                                                                        设置物理扇区大小
                       
        以上简单地介绍了块设备驱动编程的最基本的概念和流程。更深入的内容不是本书的重点,有兴趣的读者可以参考其他书籍。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|文字版|手机版|DIY编程器网 ( 桂ICP备14005565号-1 )

GMT+8, 2024-11-27 12:33 , 耗时 0.116299 秒, 22 个查询请求 , Gzip 开启.

各位嘉宾言论仅代表个人观点,非属DIY编程器网立场。

桂公网安备 45031202000115号

DIY编程器群(超员):41210778 DIY编程器

DIY编程器群1(满员):3044634 DIY编程器1

diy编程器群2:551025008 diy编程器群2

QQ:28000622;Email:libyoufer@sina.com

本站由桂林市临桂区技兴电子商务经营部独家赞助。旨在技术交流,请自觉遵守国家法律法规,一旦发现将做封号删号处理。

快速回复 返回顶部 返回列表