国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

【轉載】塊設備驅動編寫總結一

發布時間:2009-8-27 22:13    發布者:原野之狼
關鍵詞: 編寫 , 驅動 , 設備

前一段時間一直在Linux下搞塊設備驅動的開發,實現了多個塊設備模塊,現將塊設備驅動開發的一些方法總結一下。

 

塊設備與字符設備的區別

1、  從字面上理解,塊設備和字符設備最大的區別在于讀寫數據的基本單元不同。塊設備讀寫數據的基本單元為塊,例如磁盤通常為一個sector,而字符設備的基本單元為字節。所以Linux中塊設備驅動往往為磁盤設備的驅動,但是由于磁盤設備的IO性能與CPU相比很差,因此,塊設備的數據流往往會引入文件系統的Cache機制。

2、 從實現角度來看,Linux為塊設備和字符設備提供了兩套機制。字符設備實現的比較簡單,內核例程和用戶態API一一對應,用戶層的Read函數直接對應了內核中的Read例程,這種映射關系由字符設備的file_operations維護。塊設備接口相對于字符設備復雜,readwrite API沒有直接到塊設備層,而是直接到文件系統層,然后再由文件系統層發起讀寫請求。

 

塊設備讀寫流程

 

在學習塊設備原理的時候,我最關系塊設備的數據流程,從應用程序調用Read或者Write開始,數據在內核中到底是如何流通、處理的呢?然后又如何抵達具體的物理設備的呢?下面對一個帶Cache功能的塊設備數據流程進行分析。

 

1、  用戶態程序通過open()打開指定的塊設備,通過systemcall機制陷入內核,執行blkdev_open()函數,該函數注冊到文件系統方法(file_operations)中的open上。在blkdev_open函數中調用bd_acquire()函數,bd_acquire函數完成文件系統inode到塊設備bdev的轉換,具體的轉換方法通過hash查找實現。得到具體塊設備的bdev之后,調用do_open()函數完成設備打開的操作。在do_open函數中會調用到塊設備驅動注冊的open方法,具體調用如下:gendisk->fops->open(bdev->bd_inode, file)

2、  用戶程序通過readwrite函數對設備進行讀寫,文件系統會調用相應的方法,通常會調用如下兩個函數:generic_file_readblkdev_file_write。在讀寫過程中采用了多種策略,首先分析讀過程。

3、 用戶態調用了read函數,內核執行generic_file_read,如果不是direct io方式,那么直接調用do_generic_file_read->do_generic_mapping_read()函數,在do_generic_mapping_read(函數位于filemap.c)函數中,首先查找數據是否命中Cache,如果命中,那么直接將數據返回給用戶態;否則通過address_space->a_ops->readpage函數發起一個真實的讀請求。在readpage函數中,構造一個buffer_head,設置bh回調函數end_buffer_async_read,然后調用submit_bh發起請求。在submit_bh函數中,根據buffer_head構造bio,設置bio的回調函數end_bio_bh_io_sync,最后通過submit_biobio請求發送給指定的快設備。

4、  如果用戶態調用了一個write函數,內核執行blkdev_file_write函數,如果不是direct io操作方式,那么執行buffered write操作過程,直接調用generic_file_buffered_write函數。Buffered write操作方法會將數據直接寫入Cache,并進行Cache的替換操作,在替換操作過程中需要對實際的快設備進行操作,address_space->a_ops提供了塊設備操作的方法。當數據被寫入到Cache之后,write函數就可以返回了,后繼異步寫入的任務絕大部分交給了pdflush daemon(有一部分在替換的時候做了)

5、  數據流操作到這一步,我們已經很清楚用戶的數據是如何到內核了。與用戶最接近的方法是file_operations,每種設備類型都定義了這一方法(由于Linux將所有設備都看成是文件,所以為每類設備都定義了文件操作方法,例如,字符設備的操作方法為def_chr_fops,塊設備為def_blk_fops,網絡設備為bad_sock_fops)。每種設備類型底層操作方法是不一樣的,但是通過file_operations方法將設備類型的差異化屏蔽了,這就是Linux能夠將所有設備都理解為文件的緣由。到這里,又提出一個問題:既然這樣,那設備的差異化又該如何體現呢?在文件系統層定義了文件系統訪問設備的方法,該方法就是address_space_operations,文件系統通過該方法可以訪問具體的設備。對于字符設備而言,沒有實現address_space_operations方法,也沒有必要,因為字符設備的接口與文件系統的接口是一樣的,在字符設備open操作的過程中,將inode所指向的file_operations替換成cdev所指向的file_operations就可以了。這樣用戶層讀寫字符設備可以直接調用cdevfile_operations方法了。

6、  截至到步驟(4),讀操作在沒有命中Cache的情況下通過address_space_operations方法中的readpage函數發起塊設備讀請求;寫操作在替換Cache或者Pdflush喚醒時發起塊設備請求。發起塊設備請求的過程都一樣,首先根據需求構建bio結構,bio結構中包含了讀寫地址、長度、目的設備、回調函數等信息。構造完bio之后,通過簡單的submit_bio函數將請求轉發給具體的塊設備。從這里可以看出,塊設備接口很簡單,接口方法為submit_bio(更底層函數為generic_make_request),數據結構為struct bio

7、  submit_bio函數通過generic_make_request轉發biogeneric_make_request是一個循環,其通過每個塊設備下注冊的q->make_request_fn函數與塊設備進行交互。如果訪問的塊設備是一個有queue的設備,那么會將系統的__make_request函數注冊到q->make_request_fn中;否則塊設備會注冊一個私有的方法。在私有的方法中,由于不存在queue隊列,所以不會處理具體的請求,而是通過修改bio中的方法實現bio的轉發,在私有make_request方法中,往往會返回1,告訴generic_make_request繼續轉發比bioGeneric_make_request的執行上下文可能有兩種,一種是用戶上下文,另一種為pdflush所在的內核線程上下文。

8、 通過generic_make_request的不斷轉發,最后請求一定會到一個存在queue隊列的塊設備上,假設最終的那個塊設備是某個scsi disk/dev/sda)。generic_make_request將請求轉發給sda時,調用__make_request,該函數是Linux提供的塊設備請求處理函數。在該函數中實現了極其重要的操作,通常所說的IO Schedule就在該函數中實現。在該函數中試圖將轉發過來的bio merge到一個已經存在的request中,如果可以合并,那么將新的bio請求掛載到一個已經存在request中。如果不能合并,那么分配一個新的request,然后將bio添加到其中。這一切搞定之后,說明通過generic_make_request轉發的bio已經抵達了內核的一個站點——request,找到了一個臨時歸宿。此時,還沒有真正啟動物理設備的操作。在__make_request退出之前,會判斷一個bio中的sync標記,如果該標記有效,說明請求的bio是一個是實時性很強的操作,不能在內核中停留,因此調用了__generic_unplug_device函數,該函數將觸發下一階段的操作;如果該標記無效的話,那么該請求就需要在queue隊列中停留一段時間,等到queue隊列觸發鬧鐘響了之后,再觸發下一階段的操作。__make_request函數返回0,告訴generic_make_request無需再轉發bio了,bio轉發結束。

9、  到目前為止,文件系統(pdflush或者address_space_operations)發下來的bio已經mergerequest queue中,如果為sync bio,那么直接調用__generic_unplug_device,否則需要在unplug timer的軟中斷上下文中執行q->unplug_fn。后繼request的處理方法應該和具體的物理設備相關,但是在標準的塊設備上如何體現不同物理設備的差異性呢?這種差異性就體現在queue隊列的方法上,不同的物理設備,queue隊列的方法是不一樣的。舉例中的sda是一個scsi設備,在scsi middle levelscsi_request_fn函數注冊到了queue隊列的request_fn方法上。在q->unplug_fn(具體方法為:generic_unplug_device)函數中會調用request隊列的具體處理函數q->request_fnOk,到這一步實際上已經將塊設備層與scsi總線驅動層聯系在了一起,他們的接口方法為request_fn(具體函數為scsi_request_fn)。

10、明白了第(9)點之后,接下來的過程實際上和具體的scsi總線操作相關了。在scsi_request_fn函數中會掃描request隊列,通過elv_next_request函數從隊列中獲取一個request。在elv_next_request函數中通過scsi總線層注冊的q->prep_rq_fnscsi層注冊為scsi_prep_fn)函數將具體的request轉換成scsi驅動所能認識的scsi command。獲取一個request之后,scsi_request_fn函數直接調用scsi_dispatch_cmd函數將scsi command發送給一個具體的scsi host。到這一步,有一個問題:scsi command具體轉發給那個scsi host呢?秘密就在于q->queuedata中,在為sda設備分配queue隊列時,已經指定了sda塊設備與底層的scsi設備(scsi device)之間的關系,他們的關系是通過request queue維護的。

11、 scsi_dispatch_cmd函數中,通過scsi host的接口方法queuecommandscsi command發送給scsi host。通常scsi hostqueuecommand方法會將接收到的scsi command掛到自己維護的隊列中,然后再啟動DMA過程將scsi command中的數據發送給具體的磁盤。DMA完畢之后,DMA控制器中斷CPU,告訴CPU DMA過程結束,并且在中斷上下文中設置DMA結束的中斷下半部。DMA中斷服務程序返回之后觸發軟中斷,執行SCSI中斷下半部。

12、     SCSi中斷下半部中,調用scsi command結束的回調函數,這個函數往往為scsi_done,在scsi_done函數調用blk_complete_request函數結束請求request,每個請求維護了一個bio鏈,所以在結束請求過程中回調每個請求中的bio回調函數,結束具體的bioBio又有文件系統的buffer head生成,所以在結束bio時,回調buffer_head的回調處理函數bio->bi_end_io(注冊為end_bio_bh_io_sync)。自此,由中斷引發的一系列回調過程結束,總結一下回調過程如下:scsi_done->end_request->end_bio->end_bufferhead

13、  回調結束之后,文件系統引發的讀寫操作過程結束。

。。。待續。。。
本文地址:http://m.qingdxww.cn/thread-3215-1-1.html     【打印本頁】

本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
您需要登錄后才可以發表評論 登錄 | 立即注冊

廠商推薦

  • Microchip視頻專區
  • 基于CEC1712實現的處理器SPI FLASH固件安全彈性方案培訓教程
  • 了解一下Microchip強大的PIC18-Q24 MCU系列
  • 安靜高效的電機控制——這才是正確的方向!
  • 無線充電基礎知識及應用培訓教程2
  • 貿澤電子(Mouser)專區

相關視頻

關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 不卡中文一二三区 | 视频播放在线观看精品视频 | 天堂毛片 | 日韩国产第一页 | 亚洲精品男人天堂 | 亚洲欧美日本另类 | 在线免费黄色片 | 成人国产亚洲欧美成人综合网 | aⅴ免费在线观看 | 午夜免费看片 | 日韩精品中文字幕视频一区 | 99热福利 | jizz日本老师 | 亚洲欧洲尹人香蕉综合 | 二区国产| 欧美在线一区二区三区欧美 | 黄色成人毛片 | www欧美在线观看 | 久草视频播放 | 榴莲app下载网址进入i | 夜夜骚视频 | 国产护士一级毛片高清 | 在线99| 欧美激情一区二区三区在线 | 亚洲人成网站在线观看播放动漫 | 四虎影院免费 | 成人国产一区二区 | 超级极品白嫩美女在线 | 精品国产香蕉在线播出 | 青青草原伊人 | 日韩在线高清 | 免费人成在线观看网站视频 | 玖玖在线国产精品 | 亚洲国产成人久久99精品 | 日韩毛片大全免费高清 | 亚洲一区 欧美 | 在线观看黄色网页 | 久久久香蕉 | 国产无限制自拍 | 国产第一第二第三第四第五 | 国产一区二区三区毛片 |