中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

linux塊設(shè)備讀寫的示例分析-創(chuàng)新互聯(lián)

這篇文章主要為大家展示了“l(fā)inux塊設(shè)備讀寫的示例分析”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“l(fā)inux塊設(shè)備讀寫的示例分析”這篇文章吧。

我們是于2013年成立的成都網(wǎng)站建設(shè)公司,提供網(wǎng)站建設(shè),電商網(wǎng)站設(shè)計(jì)開發(fā),外貿(mào)網(wǎng)站制作,響應(yīng)式網(wǎng)頁設(shè)計(jì),小程序定制開發(fā)、等服務(wù)。為客戶創(chuàng)造有價(jià)值的品牌營(yíng)銷體驗(yàn),讓互聯(lián)網(wǎng)提升企業(yè)的競(jìng)爭(zhēng)力!

1、 用戶態(tài)程序通過open()打開指定的塊設(shè)備,通過systemcall機(jī)制陷入內(nèi)核,執(zhí)行blkdev_open()函數(shù),該函數(shù)注冊(cè)到文件系統(tǒng)方法(file_operations)中的open上。在blkdev_open函數(shù)中調(diào)用bd_acquire()函數(shù),bd_acquire函數(shù)完成文件系統(tǒng)inode到塊設(shè)備bdev的轉(zhuǎn)換,具體的轉(zhuǎn)換方法通過hash查找實(shí)現(xiàn)。得到具體塊設(shè)備的bdev之后,調(diào)用do_open()函數(shù)完成設(shè)備打開的操作。在do_open函數(shù)中會(huì)調(diào)用到塊設(shè)備驅(qū)動(dòng)注冊(cè)的open方法,具體調(diào)用如下:gendisk->fops->open(bdev->bd_inode, file)。

2、 用戶程序通過read、write函數(shù)對(duì)設(shè)備進(jìn)行讀寫,文件系統(tǒng)會(huì)調(diào)用相應(yīng)的方法,通常會(huì)調(diào)用如下兩個(gè)函數(shù):generic_file_read和blkdev_file_write。在讀寫過程中采用了多種策略,首先分析讀過程。

3、 用戶態(tài)調(diào)用了read函數(shù),內(nèi)核執(zhí)行g(shù)eneric_file_read,如果不是direct io方式,那么直接調(diào)用do_generic_file_read->do_generic_mapping_read()函數(shù),在do_generic_mapping_read(函數(shù)位于filemap.c)函數(shù)中,首先查找數(shù)據(jù)是否命中Cache,如果命中,那么直接將數(shù)據(jù)返回給用戶態(tài);否則通過address_space->a_ops->readpage函數(shù)發(fā)起一個(gè)真實(shí)的讀請(qǐng)求。在readpage函數(shù)中,構(gòu)造一個(gè)buffer_head,設(shè)置bh回調(diào)函數(shù)end_buffer_async_read,然后調(diào)用submit_bh發(fā)起請(qǐng)求。在submit_bh函數(shù)中,根據(jù)buffer_head構(gòu)造bio,設(shè)置bio的回調(diào)函數(shù)end_bio_bh_io_sync,最后通過submit_bio將bio請(qǐng)求發(fā)送給指定的快設(shè)備。

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

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

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

7、 submit_bio函數(shù)通過generic_make_request轉(zhuǎn)發(fā)bio,generic_make_request是一個(gè)循環(huán),其通過每個(gè)塊設(shè)備下注冊(cè)的q->make_request_fn函數(shù)與塊設(shè)備進(jìn)行交互。如果訪問的塊設(shè)備是一個(gè)有queue的設(shè)備,那么會(huì)將系統(tǒng)的__make_request函數(shù)注冊(cè)到q->make_request_fn中;否則塊設(shè)備會(huì)注冊(cè)一個(gè)私有的方法。在私有的方法中,由于不存在queue隊(duì)列,所以不會(huì)處理具體的請(qǐng)求,而是通過修改bio中的方法實(shí)現(xiàn)bio的轉(zhuǎn)發(fā),在私有make_request方法中,往往會(huì)返回1,告訴generic_make_request繼續(xù)轉(zhuǎn)發(fā)比bio。Generic_make_request的執(zhí)行上下文可能有兩種,一種是用戶上下文,另一種為pdflush所在的內(nèi)核線程上下文。

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

9、 到目前為止,文件系統(tǒng)(pdflush或者address_space_operations)發(fā)下來的bio已經(jīng)merge到request queue中,如果為sync bio,那么直接調(diào)用__generic_unplug_device,否則需要在unplug timer的軟中斷上下文中執(zhí)行q->unplug_fn。后繼request的處理方法應(yīng)該和具體的物理設(shè)備相關(guān),但是在標(biāo)準(zhǔn)的塊設(shè)備上如何體現(xiàn)不同物理設(shè)備的差異性呢?這種差異性就體現(xiàn)在queue隊(duì)列的方法上,不同的物理設(shè)備,queue隊(duì)列的方法是不一樣的。舉例中的sda是一個(gè)scsi設(shè)備,在scsi middle level將scsi_request_fn函數(shù)注冊(cè)到了queue隊(duì)列的request_fn方法上。在q->unplug_fn(具體方法為:generic_unplug_device)函數(shù)中會(huì)調(diào)用request隊(duì)列的具體處理函數(shù)q->request_fn。Ok,到這一步實(shí)際上已經(jīng)將塊設(shè)備層與scsi總線驅(qū)動(dòng)層聯(lián)系在了一起,他們的接口方法為request_fn(具體函數(shù)為scsi_request_fn)。

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

11、 在scsi_dispatch_cmd函數(shù)中,通過scsi host的接口方法queuecommand將scsi command發(fā)送給scsi host。通常scsi host的queuecommand方法會(huì)將接收到的scsi command掛到自己維護(hù)的隊(duì)列中,然后再啟動(dòng)DMA過程將scsi command中的數(shù)據(jù)發(fā)送給具體的磁盤。DMA完畢之后,DMA控制器中斷CPU,告訴CPU DMA過程結(jié)束,并且在中斷上下文中設(shè)置DMA結(jié)束的中斷下半部。DMA中斷服務(wù)程序返回之后觸發(fā)軟中斷,執(zhí)行SCSI中斷下半部。

12、     在SCSi中斷下半部中,調(diào)用scsi command結(jié)束的回調(diào)函數(shù),這個(gè)函數(shù)往往為scsi_done,在scsi_done函數(shù)調(diào)用blk_complete_request函數(shù)結(jié)束請(qǐng)求request,每個(gè)請(qǐng)求維護(hù)了一個(gè)bio鏈,所以在結(jié)束請(qǐng)求過程中回調(diào)每個(gè)請(qǐng)求中的bio回調(diào)函數(shù),結(jié)束具體的bio。Bio又有文件系統(tǒng)的buffer head生成,所以在結(jié)束bio時(shí),回調(diào)buffer_head的回調(diào)處理函數(shù)bio->bi_end_io(注冊(cè)為end_bio_bh_io_sync)。自此,由中斷引發(fā)的一系列回調(diào)過程結(jié)束,總結(jié)一下回調(diào)過程如下:scsi_done->end_request->end_bio->end_bufferhead。

13、  回調(diào)結(jié)束之后,文件系統(tǒng)引發(fā)的讀寫操作過程結(jié)束。

以上是“l(fā)inux塊設(shè)備讀寫的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

文章名稱:linux塊設(shè)備讀寫的示例分析-創(chuàng)新互聯(lián)
本文路徑:http://www.rwnh.cn/article32/epjpc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、Google靜態(tài)網(wǎng)站、域名注冊(cè)、網(wǎng)站收錄網(wǎng)站營(yíng)銷

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作
金堂县| 万荣县| 承德县| 延长县| 望城县| 蚌埠市| 舞阳县| 庆城县| 正定县| 澜沧| 茶陵县| 龙江县| 城市| 城口县| 灵台县| 龙岩市| 婺源县| 准格尔旗| 宁陵县| 田东县| 陵川县| 永清县| 阿瓦提县| 密山市| 禹城市| 哈尔滨市| 蒙自县| 松江区| 康定县| 鹤岗市| 承德市| 沈阳市| 临洮县| 枝江市| 岫岩| 新乐市| 改则县| 紫阳县| 凤翔县| 嘉黎县| 诸暨市|