生活随笔
收集整理的這篇文章主要介紹了
Linux块设备驱动程序原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
顧名思義,塊設備驅動程序就是支持以塊的方式進行讀寫的設備。塊設備和字符設備最大的區別在于讀寫數據的基本單元不同。塊設備讀寫數據的基本單元為塊,例如磁盤通常為一個sector,而字符設備的基本單元為字節。從實現角度來看,字符設備的實現比較簡單,內核例程和用戶態API一一對應,這種映射關系由字符設備的file_operations維護。塊設備接口則相對復雜,讀寫API沒有直接到塊設備層,而是直接到文件系統層,然后再由文件系統層發起讀寫請求。
block_device結構代表了內核中的一個塊設備。它可以表示整個磁盤或一個特定的分區。當這個結構代表一個分區時,它的bd_contains成員指向包含這個分區的設備,bd_part成員指向設備的分區結構。當這個結構代表一個塊設備時,bd_disk成員指向設備的gendisk結構。
struct?block_device?{ ?????dev_t???????????bd_dev; ?????struct?inode?*??bd_inode;???/*分區結點*/ ?????int?????????bd_openers; ?????struct?semaphore????bd_sem;?/*打開/關閉鎖*/ ?????struct?semaphore????bd_mount_sem;???/*?加載互斥鎖*/ ?????struct?list_head????bd_inodes; ?????void?*??????bd_holder; ?????int?????????bd_holders; ?????struct?block_device?*???bd_contains; ?????unsigned????????bd_block_size;//分區塊大小 ?????struct?hd_struct?*??bd_part; ?????unsigned????????bd_part_count;//打開次數 ?????int?????????bd_invalidated; ?????struct?gendisk?*????bd_disk; ?????struct?list_head????bd_list; ?????struct?backing_dev_info?*bd_inode_backing_dev_info; ?????unsigned?long???bd_private; ?}; ?
gendisk是一個單獨的磁盤驅動器的內核表示。內核還使用gendisk來表示分區。
struct?gendisk?{ ?????int?major;??????????//主設備號 ?????int?first_minor;??? ?????int?minors;?????????//最大的次設備號數量,如果設備不能分區,該值為1????????????????????????????????? ?????char?disk_name[32];?//主設備名 ?????struct?hd_struct?**part;????//分區信息,有minors個 ?????struct?block_device_operations?*fops;//設備操作 ?????struct?request_queue?*queue;????//設備管理I/O請求 ?????void?*private_data; ?????sector_t?capacity; ?????int?flags; ?????char?devfs_name[64]; ?????int?number; ?????struct?device?*driverfs_dev; ?????struct?kobject?kobj; ?????struct?timer_rand_state?*random; ?????int?policy; ?????atomic_t?sync_io;??? ?????unsigned?long?stamp,?stamp_idle; ?????int?in_flight; ?#ifdef??CONFIG_SMP ?????struct?disk_stats?*dkstats; ?#else ?????struct?disk_stats?dkstats; ?#endif ?}; ?
gendisk結構的操作函數包括以下幾個:
struct?gendisk?*alloc_disk(int?minors);?????//分配磁盤 ?void?add_disk(struct?gendisk?*disk);????????//增加磁盤信息 ?void?unlink_gendisk(struct?gendisk?*disk)???//刪除磁盤信息 ?void?delete_partition(struct?gendisk?*disk,?int?part);??//刪除分區 ?void?add_partition(struct?gendisk?*disk,?int?part,?sector_t?start,?sector_t?len,?int?flags);//添加分區 ?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; ?}; ?block_device_operations并不能完全提供文件操作全部的API,實際上只提供了open、release等函數,其他的文件操作依賴于def_blk_fops:const?struct?file_operations?def_blk_fops?=?{ ?????.open???=?blkdev_open, ?????.release????=?blkdev_close, ?????.llseek?=?block_llseek, ?????.read???????=?do_sync_read, ?????.write??=?do_sync_write, ?????.aio_read???=?generic_file_aio_read, ?????.aio_write=?generic_file_aio_write_nolock, ?????.mmap???=?generic_file_mmap, ?????.fsync??=?block_fsync, ?????.unlocked_ioctl?=?block_ioctl, ?#ifdef?CONFIG_COMPAT ?????.compat_ioctl???=?compat_blkdev_ioctl, ?#endif ?????.splice_read????????=?generic_file_splice_read, ?????.splice_write???=?generic_file_splice_write, ?}; ?系統對塊設備進行讀寫操作時,通過塊設備通用的讀寫操作函數將一個請求保存在該設備的操作請求隊列(request queue)中,然后調用這個塊設備的底層處理函數,對請求隊列中的操作請求進行逐一執行。request_queue結構描述了塊設備的請求隊列,該結構定義如下:struct?request_queue ?{ ?????struct?list_head????queue_head; ?????struct?request??????*last_merge; ?????elevator_t??????elevator; ?????/*請求隊列列表*/ ?????struct?request_list?????rq; ?????request_fn_proc?????*request_fn; ?????merge_request_fn????*back_merge_fn; ?????merge_request_fn????*front_merge_fn; ?????merge_requests_fn???*merge_requests_fn; ?????make_request_fn?????*make_request_fn; ?????prep_rq_fn??????????*prep_rq_fn; ?????unplug_fn???????????*unplug_fn; ?????merge_bvec_fn???????*merge_bvec_fn; ?????activity_fn?????????*activity_fn; ?????/*自動卸載狀態*/ ?????struct?timer_list???unplug_timer; ?????int?????????unplug_thresh;?? ?????unsigned?long???????unplug_delay;???/*自動卸載延時*/ ?????struct?work_struct??unplug_work; ?????struct?backing_dev_info?backing_dev_info; ?????void????????????????*queuedata; ?????void????????????????*activity_data; ?????unsigned?long???????bounce_pfn; ?????int?????????????bounce_gfp; ?????unsigned?long???????queue_flags;//各種隊列標志 ?????/*保護隊列結構,避免重入*/ ?????spinlock_t??????????*queue_lock; ?????/*?請求的核心結構*/ ?????struct?kobject?kobj; ?????/*請求的配置*/ ?????unsigned?long???????nr_requests;????/*?請求的最大數*/ ?????unsigned?int????????nr_congestion_on; ?????unsigned?int????????nr_congestion_off; ?????unsigned?short??????max_sectors; ?????unsigned?short??????max_phys_segments; ?????unsigned?short??????max_hw_segments; ?????unsigned?short??????hardsect_size; ?????unsigned?int????????max_segment_size; ?????unsigned?long???????seg_boundary_mask; ?????unsigned?int????????dma_alignment; ?????struct?blk_queue_tag????*queue_tags; ?????atomic_t????????refcnt; ?????unsigned?int????????in_flight; ?????/*sg?參數配置*/ ?????unsigned?int????????sg_timeout; ?????unsigned?int????????sg_reserved_size; ?}; ?請求隊列相關的處理函數包括://創建隊列時提供了一個自旋鎖。 ?request_queue_t?*blk_init_queue(request_fn_proc?*rfn,?spinlock_t?*lock); ?//獲得隊列中第一個未完成的請求。 ?struct?request?*elv_next_request(request_queue_t?*q); ?void?end_request(struct?request?*req,?int?uptodate);//請求完成 ?void?blk_stop_queue(request_queue_t?*queue);?//停止請求 ?void?blk_start_queue(request_queue_t?*queue);?//開始請求 ?void?blk_cleanup_queue(request_queue_t?*);//清除請求隊列 ?簡單的塊設備驅動程序實例
向內核注冊和注銷一個塊設備可使用如下函數:
int?register_blkdev(unsigned?int?major,?const?char?*name); ?int?unregister_blkdev(unsigned?int?major,?const?char?*name);?例1.10? 簡單的塊設備驅動程序實例
代碼見光盤\src\1drivermodel\1-10block。核心代碼如下所示:
static?struct?request_queue?*Queue; ?//自定義塊設備結構 ?static?struct?simpleblockdevice? ?{ ?????unsigned?long?size; ?????spinlock_t?lock; ?????u8?*data; ?????struct?gendisk?*gd; ?}?Device; ?//處理I/O請求 ?static?void?simpleblocktransfer(struct?simpleblockdevice?*dev,?unsigned?long?sector, ?????????????????????????????????unsigned?long?nsect,?char?*buffer,?int?write) ?{ ?????unsigned?long?offset?=?sector*hardsect_size; ?????unsigned?long?nbytes?=?nsect*hardsect_size; ?????//判斷I/O請求是否超出范圍 ?????if?((offset?+?nbytes)?>?dev->size) ?????{ ?????????printk?(KERN_NOTICE?"sbd:?Beyond-end?write?(%ld?%ld)\n",?offset,?nbytes); ?????????return; ?????} ?????if?(write) ?????????memcpy(dev->data?+?offset,?buffer,?nbytes); ?????else ?????????memcpy(buffer,?dev->data?+?offset,?nbytes); ?} ?//簡單請求處理 ?static?void?simpleblockrequest(struct?request_queue?*q) ?{ ?????struct?request?*req; ?????//獲取下一個請求 ?????while?((req?=?elv_next_request(q))?!=?NULL)? ?????{ ?????????if?(!?blk_fs_request(req))? ?????????{ ?????????????printk?(KERN_NOTICE?"Skip?non-CMD?request\n"); ?????????????end_request(req,?0); ?????????????continue; ?????????} ?????????simpleblocktransfer(&Device,?req->sector,?req->current_nr_sectors, ?????????????req->buffer,?rq_data_dir(req)); ?????????end_request(req,?1); ?????} ?} ?//簡單的塊設備ioctl函數 ?int?simpleblockioctl?(struct?inode?*inode,?struct?file?*filp,unsigned?int?cmd,?unsigned?long?arg) ?{ ?????long?size; ?????struct?hd_geometry?geo; ?????switch(cmd)? ?????{ ?????//獲取磁盤信息 ?????case?HDIO_GETGEO: ?????????size?=?Device.size*(hardsect_size/KERNEL_SECTOR_SIZE); ?????????geo.cylinders?=?(size?&?~0x3f)?>>?6; ?????????geo.heads?=?4; ?????????geo.sectors?=?16; ?????????geo.start?=?4; ?????????if?(copy_to_user((void?*)?arg,?&geo,?sizeof(geo))) ?????????????return?-EFAULT; ?????????return?0; ?????} ?????return?-ENOTTY;?/*?未知命令?*/ ?} ?//設備操作結構 ?static?struct?block_device_operations?simpleblockops?=?{ ?????.owner???????????=?THIS_MODULE, ?????.ioctl???????=?simpleblockioctl?}; ?static?int?__init?simpleblockinit(void) ?{ ?????Device.size?=?nsectors*hardsect_size; ?????spin_lock_init(&Device.lock); ?????Device.data?=?vmalloc(Device.size); ?????if?(Device.data?==?NULL) ?????????return?-ENOMEM; ?????//初始化請求隊列,配置處理函數為sbd_request ?????Queue?=?blk_init_queue(simpleblockrequest,?&Device.lock); ?????if?(Queue?==?NULL) ?????????goto?out; ?????blk_queue_hardsect_size(Queue,?hardsect_size); ?????//注冊塊設備 ?????major_num?=?register_blkdev(major_num,?"sbd"); ?????if?(major_num?<=?0)?{ ?????????printk(KERN_WARNING?"sbd:?unable?to?get?major?number\n"); ?????????goto?out; ?????} ?????Device.gd?=?alloc_disk(16); ?????if?(!?Device.gd) ?????????goto?out_unregister; ?????Device.gd->major?=?major_num; ?????Device.gd->first_minor?=?0; ?????Device.gd->fops?=?&simpleblockops; ?????Device.gd->private_data?=?&Device; ?????strcpy?(Device.gd->disk_name,?"sbd0"); ?????//配置容量 ?????set_capacity(Device.gd,?nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); ?????Device.gd->queue?=?Queue; ?????add_disk(Device.gd); ?????return?0; ?out_unregister: ?????unregister_blkdev(major_num,?"sbd"); ?out: ?????vfree(Device.data); ?????return?-ENOMEM; ?} ?static?void?__exit?simpleblockexit(void) ?{ ?????del_gendisk(Device.gd); ?????put_disk(Device.gd); ?????unregister_blkdev(major_num,?"sbd"); ?????blk_cleanup_queue(Queue); ?????vfree(Device.data); ?} ?module_init(simpleblockinit); ?module_exit(simpleblockexit); ?運行結果如下:[root@/home]#cat?/proc/filesystems ?nodev???sysfs ?nodev???rootfs ?nodev???bdev ?nodev???proc ?nodev???binfmt_misc ?nodev???debugfs ?nodev???securityfs ?nodev???sockfs ?nodev???usbfs ?nodev???pipefs ?nodev???anon_inodefs ?nodev???futexfs ?nodev???tmpfs ?nodev???inotifyfs ?????????ext3 ?????????cramfs ?nodev???ramfs ?????????msdos ?????????vfat ?????????iso9660 ?nodev???nfs ?nodev???nfs4 ?nodev???mqueue ?nodev???rpc_pipefs ?[root@/home]#insmod?demo.ko ??sbd0:?unknown?partition?table ?[root@/home]#mknod?/dev/sbd?b?253?0 ?[root@/home]#./mkfs.ext3?/dev/sbd ?mke2fs?1.40.9?(27-Apr-2008) ?Filesystem?label= ?OS?type:?Linux ?Block?size=1024?(log=0) ?Fragment?size=1024?(log=0) ?1280?inodes,?5120?blocks ?256?blocks?(5.00%)?reserved?for?the?super?user ?First?data?block=1?Maximum?filesystem?blocks=5242880?1?block?group ?8192?blocks?per?group,?8192?fragments?per?group ?1280?inodes?per?group ??Writing?inode?tables:?done ?Creating?journal?(1024?blocks):?done ?Writing?superblocks?and?filesystem?accounting?information:?done ??This?filesystem?will?be?automatically?checked?every?39?mounts?or ?180?days,?whichever?comes?first.??Use?tune2fs?-c?or?-i?to?override. ?[root@/home]#mount?-t?ext3?/dev/sbd?/mnt/u ?kjournald?starting.??Commit?interval?5?seconds ?EXT3?FS?on?sbd0,?internal?journal ?EXT3-fs:?mounted?filesystem?with?ordered?data?mode. ?[root@/home]#df ?Filesystem???????????1k-blocks??????Used?Available?Use%?Mounted?on ?rootfs?????????????????2063504???1191136????767548??61%?/ ?/dev/root??????????????2063504???1191136????767548??61%?/ ?/dev/sbd??????????????????4955??????1063??????3636??23%?/mnt/u ?[root@/home]#cd?/mnt/u ?[root@/mnt/u]#ls ?lost+found ?
與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的Linux块设备驱动程序原理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。