一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

read 系統(tǒng)調(diào)用剖析

 ydzhang 2009-11-19
大部分程序員可能會有這樣的疑問:當(dāng)在程序中調(diào)用庫函數(shù) read 時(shí),這個(gè)請求是經(jīng)過哪些處理最終到達(dá)磁盤的呢,數(shù)據(jù)又是怎么被拷貝到用戶緩存區(qū)的呢?本文介紹了從 read 系統(tǒng)調(diào)用發(fā)出到結(jié)束處理的全過程。該過程包括兩個(gè)部分:用戶空間的處理、核心空間的處理。用戶空間處理部分是系統(tǒng)調(diào)用從用戶態(tài)切到核心態(tài)的過程。核心空間處理部分則是 read 系統(tǒng)調(diào)用在 linux 內(nèi)核中處理的整個(gè)過程。

Read 系統(tǒng)調(diào)用在用戶空間中的處理過程

Linux 系統(tǒng)調(diào)用(SCI,system call interface)的實(shí)現(xiàn)機(jī)制實(shí)際上是一個(gè)多路匯聚以及分解的過程,該匯聚點(diǎn)就是 0x80 中斷這個(gè)入口點(diǎn)(X86 系統(tǒng)結(jié)構(gòu))。也就是說,所有系統(tǒng)調(diào)用都從用戶空間中匯聚到 0x80 中斷點(diǎn),同時(shí)保存具體的系統(tǒng)調(diào)用號。當(dāng) 0x80 中斷處理程序運(yùn)行時(shí),將根據(jù)系統(tǒng)調(diào)用號對不同的系統(tǒng)調(diào)用分別處理(調(diào)用不同的內(nèi)核函數(shù)處理)。系統(tǒng)調(diào)用的更多內(nèi)容,請參見參考資料。

Read 系統(tǒng)調(diào)用也不例外,當(dāng)調(diào)用發(fā)生時(shí),庫函數(shù)在保存 read 系統(tǒng)調(diào)用號以及參數(shù)后,陷入 0x80 中斷。這時(shí)庫函數(shù)工作結(jié)束。Read 系統(tǒng)調(diào)用在用戶空間中的處理也就完成了。





回頁首


Read 系統(tǒng)調(diào)用在核心空間中的處理過程

0x80 中斷處理程序接管執(zhí)行后,先檢察其系統(tǒng)調(diào)用號,然后根據(jù)系統(tǒng)調(diào)用號查找系統(tǒng)調(diào)用表,并從系統(tǒng)調(diào)用表中得到處理 read 系統(tǒng)調(diào)用的內(nèi)核函數(shù) sys_read ,最后傳遞參數(shù)并運(yùn)行 sys_read 函數(shù)。至此,內(nèi)核真正開始處理 read 系統(tǒng)調(diào)用(sys_read 是 read 系統(tǒng)調(diào)用的內(nèi)核入口)。

在講解 read 系統(tǒng)調(diào)用在核心空間中的處理部分中,首先介紹了內(nèi)核處理磁盤請求的層次模型,然后再按該層次模型從上到下的順序依次介紹磁盤讀請求在各層的處理過程。

Read 系統(tǒng)調(diào)用在核心空間中處理的層次模型

圖1顯示了 read 系統(tǒng)調(diào)用在核心空間中所要經(jīng)歷的層次模型。從圖中看出:對于磁盤的一次讀請求,首先經(jīng)過虛擬文件系統(tǒng)層(vfs layer),其次是具體的文件系統(tǒng)層(例如 ext2),接下來是 cache 層(page cache 層)、通用塊層(generic block layer)、IO 調(diào)度層(I/O scheduler layer)、塊設(shè)備驅(qū)動層(block device driver layer),最后是物理塊設(shè)備層(block device layer)


圖1 read 系統(tǒng)調(diào)用在核心空間中的處理層次

  • 虛擬文件系統(tǒng)層的作用:屏蔽下層具體文件系統(tǒng)操作的差異,為上層的操作提供一個(gè)統(tǒng)一的接口。正是因?yàn)橛辛诉@個(gè)層次,所以可以把設(shè)備抽象成文件,使得操作設(shè)備就像操作文件一樣簡單。
  • 在具體的文件系統(tǒng)層中,不同的文件系統(tǒng)(例如 ext2 和 NTFS)具體的操作過程也是不同的。每種文件系統(tǒng)定義了自己的操作集合。關(guān)于文件系統(tǒng)的更多內(nèi)容,請參見參考資料。
  • 引入 cache 層的目的是為了提高 linux 操作系統(tǒng)對磁盤訪問的性能。 Cache 層在內(nèi)存中緩存了磁盤上的部分?jǐn)?shù)據(jù)。當(dāng)數(shù)據(jù)的請求到達(dá)時(shí),如果在 cache 中存在該數(shù)據(jù)且是最新的,則直接將數(shù)據(jù)傳遞給用戶程序,免除了對底層磁盤的操作,提高了性能。
  • 通用塊層的主要工作是:接收上層發(fā)出的磁盤請求,并最終發(fā)出 IO 請求。該層隱藏了底層硬件塊設(shè)備的特性,為塊設(shè)備提供了一個(gè)通用的抽象視圖。
  • IO 調(diào)度層的功能:接收通用塊層發(fā)出的 IO 請求,緩存請求并試圖合并相鄰的請求(如果這兩個(gè)請求的數(shù)據(jù)在磁盤上是相鄰的)。并根據(jù)設(shè)置好的調(diào)度算法,回調(diào)驅(qū)動層提供的請求處理函數(shù),以處理具體的 IO 請求。
  • 驅(qū)動層中的驅(qū)動程序?qū)?yīng)具體的物理塊設(shè)備。它從上層中取出 IO 請求,并根據(jù)該 IO 請求中指定的信息,通過向具體塊設(shè)備的設(shè)備控制器發(fā)送命令的方式,來操縱設(shè)備傳輸數(shù)據(jù)。
  • 設(shè)備層中都是具體的物理設(shè)備。定義了操作具體設(shè)備的規(guī)范。

相關(guān)的內(nèi)核數(shù)據(jù)結(jié)構(gòu):

  • Dentry : 聯(lián)系了文件名和文件的 i 節(jié)點(diǎn)
  • inode : 文件 i 節(jié)點(diǎn),保存文件標(biāo)識、權(quán)限和內(nèi)容等信息
  • file : 保存文件的相關(guān)信息和各種操作文件的函數(shù)指針集合
  • file_operations :操作文件的函數(shù)接口集合
  • address_space :描述文件的 page cache 結(jié)構(gòu)以及相關(guān)信息,并包含有操作 page cache 的函數(shù)指針集合
  • address_space_operations :操作 page cache 的函數(shù)接口集合
  • bio : IO 請求的描述

數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系:

圖2示意性地展示了上述各個(gè)數(shù)據(jù)結(jié)構(gòu)(除了 bio)之間的關(guān)系??梢钥闯觯河?dentry 對象可以找到 inode 對象,從 inode 對象中可以取出 address_space 對象,再由 address_space 對象找到 address_space_operations 對象。

File 對象可以根據(jù)當(dāng)前進(jìn)程描述符中提供的信息取得,進(jìn)而可以找到 dentry 對象、 address_space 對象和 file_operations 對象。


圖2 數(shù)據(jù)結(jié)構(gòu)關(guān)系圖:

前提條件:

對于具體的一次 read 調(diào)用,內(nèi)核中可能遇到的處理情況很多。這里舉例其中的一種情況:

  • 要讀取的文件已經(jīng)存在
  • 文件經(jīng)過 page cache
  • 要讀的是普通文件
  • 磁盤上文件系統(tǒng)為 ext2 文件系統(tǒng),有關(guān) ext2 文件系統(tǒng)的相關(guān)內(nèi)容,參見參考資料

準(zhǔn)備:

注:所有清單中代碼均來自 linux2.6.11 內(nèi)核原代碼

讀數(shù)據(jù)之前,必須先打開文件。處理 open 系統(tǒng)調(diào)用的內(nèi)核函數(shù)為 sys_open 。
所以我們先來看一下該函數(shù)都作了哪些事。清單1顯示了 sys_open 的代碼(省略了部分內(nèi)容,以后的程序清單同樣方式處理)


清單1 sys_open 函數(shù)代碼
            asmlinkage long sys_open(const char __user * filename, int flags, int mode)
            {
            ……
            fd = get_unused_fd();
            if (fd >= 0) {
            struct file *f = filp_open(tmp, flags, mode);
            fd_install(fd, f);
            }
            ……
            return fd;
            ……
            }
            

代碼解釋:

  • get_unuesed_fd() :取回一個(gè)未被使用的文件描述符(每次都會選取最小的未被使用的文件描述符)。
  • filp_open() :調(diào)用 open_namei() 函數(shù)取出和該文件相關(guān)的 dentry 和 inode (因?yàn)榍疤嶂该髁宋募呀?jīng)存在,所以 dentry 和 inode 能夠查找到,不用創(chuàng)建),然后調(diào)用 dentry_open() 函數(shù)創(chuàng)建新的 file 對象,并用 dentry 和 inode 中的信息初始化 file 對象(文件當(dāng)前的讀寫位置在 file 對象中保存)。注意到 dentry_open() 中有一條語句:

f->f_op = fops_get(inode->i_fop);

這個(gè)賦值語句把和具體文件系統(tǒng)相關(guān)的,操作文件的函數(shù)指針集合賦給了 file 對象的 f _op 變量(這個(gè)指針集合是保存在 inode 對象中的),在接下來的 sys_read 函數(shù)中將會調(diào)用 file->f_op 中的成員 read 。

  • fd_install() :以文件描述符為索引,關(guān)聯(lián)當(dāng)前進(jìn)程描述符和上述的 file 對象,為之后的 read 和 write 等操作作準(zhǔn)備。
  • 函數(shù)最后返回該文件描述符。

圖3顯示了 sys_open 函數(shù)返回后, file 對象和當(dāng)前進(jìn)程描述符之間的關(guān)聯(lián)關(guān)系,以及 file 對象中操作文件的函數(shù)指針集合的來源(inode 對象中的成員 i_fop)。


圖3 file 對象和當(dāng)前進(jìn)程描述符之間的關(guān)系
file 對象和當(dāng)前進(jìn)程描述符之間的關(guān)系

到此為止,所有的準(zhǔn)備工作已經(jīng)全部結(jié)束了,下面開始介紹 read 系統(tǒng)調(diào)用在圖1所示的各個(gè)層次中的處理過程。

虛擬文件系統(tǒng)層的處理:

內(nèi)核函數(shù) sys_read() 是 read 系統(tǒng)調(diào)用在該層的入口點(diǎn),清單2顯示了該函數(shù)的代碼。


清單2 sys_read 函數(shù)的代碼
            asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
            {
            struct file *file;
            ssize_t ret = -EBADF;
            int fput_needed;
            file = fget_light(fd, &fput_needed);
            if (file) {
            loff_t pos = file_pos_read(file);
            ret = vfs_read(file, buf, count, &pos);
            file_pos_write(file, pos);
            fput_light(file, fput_needed);
            }
            return ret;
            }
            

代碼解析:

  • fget_light() :根據(jù) fd 指定的索引,從當(dāng)前進(jìn)程描述符中取出相應(yīng)的 file 對象(見圖3)。
  • 如果沒找到指定的 file 對象,則返回錯(cuò)誤
  • 如果找到了指定的 file 對象:
  • 調(diào)用 file_pos_read() 函數(shù)取出此次讀寫文件的當(dāng)前位置。
  • 調(diào)用 vfs_read() 執(zhí)行文件讀取操作,而這個(gè)函數(shù)最終調(diào)用 file->f_op.read() 指向的函數(shù),代碼如下:

if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);

  • 調(diào)用 file_pos_write() 更新文件的當(dāng)前讀寫位置。
  • 調(diào)用 fput_light() 更新文件的引用計(jì)數(shù)。
  • 最后返回讀取數(shù)據(jù)的字節(jié)數(shù)。

到此,虛擬文件系統(tǒng)層所做的處理就完成了,控制權(quán)交給了 ext2 文件系統(tǒng)層。

在解析 ext2 文件系統(tǒng)層的操作之前,先讓我們看一下 file 對象中 read 指針來源。

File 對象中 read 函數(shù)指針的來源:

從前面對 sys_open 內(nèi)核函數(shù)的分析來看, file->f_op 來自于 inode->i_fop 。那么 inode->i_fop 來自于哪里呢?在初始化 inode 對象時(shí)賦予的。見清單3。


清單3 ext2_read_inode() 函數(shù)部分代碼
            void ext2_read_inode (struct inode * inode)
            {
            ……
            if (S_ISREG(inode->i_mode)) {
            inode->i_op = &ext2_file_inode_operations;
            inode->i_fop = &ext2_file_operations;
            if (test_opt(inode->i_sb, NOBH))
            inode->i_mapping->a_ops = &ext2_nobh_aops;
            else
            inode->i_mapping->a_ops = &ext2_aops;
            }
            ……
            }
            

從代碼中可以看出,如果該 inode 所關(guān)聯(lián)的文件是普通文件,則將變量 ext2_file_operations 的地址賦予 inode 對象的 i_fop 成員。所以可以知道: inode->i_fop.read 函數(shù)指針?biāo)赶虻暮瘮?shù)為 ext2_file_operations 變量的成員 read 所指向的函數(shù)。下面來看一下 ext2_file_operations 變量的初始化過程,如清單4。


清單4 ext2_file_operations 的初始化
            struct file_operations ext2_file_operations = {
            .llseek = generic_file_llseek,
            .read        = generic_file_read,
            .write       = generic_file_write,
            .aio_read  	= generic_file_aio_read,
            .aio_write	= generic_file_aio_write,
            .ioctl       = ext2_ioctl,
            .mmap        = generic_file_mmap,
            .open        = generic_file_open,
            .release     = ext2_release_file,
            .fsync       = ext2_sync_file,
            .readv       = generic_file_readv,
            .writev      = generic_file_writev,
            .sendfile    = generic_file_sendfile,
            };
            

該成員 read 指向函數(shù) generic_file_read 。所以, inode->i_fop.read 指向 generic_file_read 函數(shù),進(jìn)而 file->f_op.read 指向 generic_file_read 函數(shù)。最終得出結(jié)論: generic_file_read 函數(shù)才是 ext2 層的真實(shí)入口。

Ext2 文件系統(tǒng)層的處理


圖4 read 系統(tǒng)調(diào)用在 ext2 層中處理時(shí)函數(shù)調(diào)用關(guān)系

由圖 4 可知,該層入口函數(shù) generic_file_read 調(diào)用函數(shù) __generic_file_aio_read ,后者判斷本次讀請求的訪問方式,如果是直接 io (filp->f_flags 被設(shè)置了 O_DIRECT 標(biāo)志,即不經(jīng)過 cache)的方式,則調(diào)用 generic_file_direct_IO 函數(shù);如果是 page cache 的方式,則調(diào)用 do_generic_file_read 函數(shù)。函數(shù) do_generic_file_read 僅僅是一個(gè)包裝函數(shù),它又調(diào)用 do_generic_mapping_read 函數(shù)。

在講解 do_generic_mapping_read 函數(shù)都作了哪些工作之前,我們再來看一下文件在內(nèi)存中的緩存區(qū)域是被怎么組織起來的。

文件的 page cache 結(jié)構(gòu)

圖5顯示了一個(gè)文件的 page cache 結(jié)構(gòu)。文件被分割為一個(gè)個(gè)以 page 大小為單元的數(shù)據(jù)塊,這些數(shù)據(jù)塊(頁)被組織成一個(gè)多叉樹(稱為 radix 樹)。樹中所有葉子節(jié)點(diǎn)為一個(gè)個(gè)頁幀結(jié)構(gòu)(struct page),表示了用于緩存該文件的每一個(gè)頁。在葉子層最左端的第一個(gè)頁保存著該文件的前4096個(gè)字節(jié)(如果頁的大小為4096字節(jié)),接下來的頁保存著文件第二個(gè)4096個(gè)字節(jié),依次類推。樹中的所有中間節(jié)點(diǎn)為組織節(jié)點(diǎn),指示某一地址上的數(shù)據(jù)所在的頁。此樹的層次可以從0層到6層,所支持的文件大小從0字節(jié)到16 T 個(gè)字節(jié)。樹的根節(jié)點(diǎn)指針可以從和文件相關(guān)的 address_space 對象(該對象保存在和文件關(guān)聯(lián)的 inode 對象中)中取得(更多關(guān)于 page cache 的結(jié)構(gòu)內(nèi)容請參見參考資料)。


圖5 文件的 page cache 結(jié)構(gòu)

現(xiàn)在,我們來看看函數(shù) do_generic_mapping_read 都作了哪些工作, do_generic_mapping_read 函數(shù)代碼較長,本文簡要介紹下它的主要流程:

  • 根據(jù)文件當(dāng)前的讀寫位置,在 page cache 中找到緩存請求數(shù)據(jù)的 page
  • 如果該頁已經(jīng)最新,將請求的數(shù)據(jù)拷貝到用戶空間
  • 否則, Lock 該頁
  • 調(diào)用 readpage 函數(shù)向磁盤發(fā)出添頁請求(當(dāng)下層完成該 IO 操作時(shí)會解鎖該頁),代碼:
	error = mapping->a_ops->readpage(filp, page);
            

  • 再一次 lock 該頁,操作成功時(shí),說明數(shù)據(jù)已經(jīng)在 page cache 中了,因?yàn)橹挥?IO 操作完成后才可能解鎖該頁。此處是一個(gè)同步點(diǎn),用于同步數(shù)據(jù)從磁盤到內(nèi)存的過程。
  • 解鎖該頁
  • 到此為止數(shù)據(jù)已經(jīng)在 page cache 中了,再將其拷貝到用戶空間中(之后 read 調(diào)用可以在用戶空間返回了)

到此,我們知道:當(dāng)頁上的數(shù)據(jù)不是最新的時(shí)候,該函數(shù)調(diào)用 mapping->a_ops->readpage 所指向的函數(shù)(變量 mapping 為 inode 對象中的 address_space 對象),那么這個(gè)函數(shù)到底是什么呢?

Readpage 函數(shù)的由來

address_space 對象是嵌入在 inode 對象之中的,那么不難想象: address_space 對象成員 a_ops 的初始化工作將會在初始化 inode 對象時(shí)進(jìn)行。如清單3中后半部所顯示。

if (test_opt(inode->i_sb, NOBH))
            inode->i_mapping->a_ops = &ext2_nobh_aops;
            else
            inode->i_mapping->a_ops = &ext2_aops;
            

可以知道 address_space 對象的成員 a_ops 指向變量 ext2_aops 或者變量 ext2_nobh_aops 。這兩個(gè)變量的初始化如清單5所示。


清單5 變量 ext2_aops 和變量 ext2_nobh_aops 的初始化
            struct address_space_operations ext2_aops = {
            .readpage          =
            ext2_readpage,
            .readpages         = ext2_readpages,
            .writepage         = ext2_writepage,
            .sync_page         = block_sync_page,
            .prepare_write     = ext2_prepare_write,
            .commit_write       = generic_commit_write,
            .bmap                 = ext2_bmap,
            .direct_IO           = ext2_direct_IO,
            .writepages          = ext2_writepages,
            };
            struct address_space_operations ext2_nobh_aops = {
            .readpage
            = ext2_readpage,
            .readpages           = ext2_readpages,
            .writepage	         = ext2_writepage,
            .sync_page           = block_sync_page,
            .prepare_write      = ext2_nobh_prepare_write,
            .commit_write       = nobh_commit_write,
            .bmap                 = ext2_bmap,
            .direct_IO           = ext2_direct_IO,
            .writepages          = ext2_writepages,
            };
            

從上述代碼中可以看出,不論是哪個(gè)變量,其中的 readpage 成員都指向函數(shù) ext2_readpage 。所以可以斷定:函數(shù) do_generic_mapping_read 最終調(diào)用 ext2_readpage 函數(shù)處理讀數(shù)據(jù)請求。

到此為止, ext2 文件系統(tǒng)層的工作結(jié)束。

Page cache 層的處理

從上文得知:ext2_readpage 函數(shù)是該層的入口點(diǎn)。該函數(shù)調(diào)用 mpage_readpage 函數(shù),清單6顯示了 mpage_readpage 函數(shù)的代碼。


清單6 mpage_readpage 函數(shù)的代碼
            int mpage_readpage(struct page *page, get_block_t get_block)
            {
            struct bio *bio = NULL;
            sector_t last_block_in_bio = 0;
            bio = do_mpage_readpage(bio, page, 1,
            &last_block_in_bio, get_block);
            if (bio)
            mpage_bio_submit(READ, bio);
            return 0;
            }
            

該函數(shù)首先調(diào)用函數(shù) do_mpage_readpage 函數(shù)創(chuàng)建了一個(gè) bio 請求,該請求指明了要讀取的數(shù)據(jù)塊所在磁盤的位置、數(shù)據(jù)塊的數(shù)量以及拷貝該數(shù)據(jù)的目標(biāo)位置——緩存區(qū)中 page 的信息。然后調(diào)用 mpage_bio_submit 函數(shù)處理請求。 mpage_bio_submit 函數(shù)則調(diào)用 submit_bio 函數(shù)處理該請求,后者最終將請求傳遞給函數(shù) generic_make_request ,并由 generic_make_request 函數(shù)將請求提交給通用塊層處理。

到此為止, page cache 層的處理結(jié)束。

通用塊層的處理

generic_make_request 函數(shù)是該層的入口點(diǎn),該層只有這一個(gè)函數(shù)處理請求。清單7顯示了函數(shù)的部分代碼


清單7 generic_make_request 函數(shù)部分代碼
            void generic_make_request(struct bio *bio)
            {
            ……
            do {
            char b[BDEVNAME_SIZE];
            q = bdev_get_queue(bio->bi_bdev);
            ……
            block_wait_queue_running(q);
            /*
            * If this device has partitions, remap block n
            * of partition p to block n+start(p) of the disk.
            */
            blk_partition_remap(bio);
            ret = q->make_request_fn(q, bio);
            } while (ret);
            }
            

主要操作:

  • 根據(jù) bio 中保存的塊設(shè)備號取得請求隊(duì)列 q
  • 檢測當(dāng)前 IO 調(diào)度器是否可用,如果可用,則繼續(xù);否則等待調(diào)度器可用
  • 調(diào)用 q->make_request_fn 所指向的函數(shù)將該請求(bio)加入到請求隊(duì)列中

到此為止,通用塊層的操作結(jié)束。

IO 調(diào)度層的處理

對 make_request_fn 函數(shù)的調(diào)用可以認(rèn)為是 IO 調(diào)度層的入口,該函數(shù)用于向請求隊(duì)列中添加請求。該函數(shù)是在創(chuàng)建請求隊(duì)列時(shí)指定的,代碼如下(blk_init_queue 函數(shù)中):

q->request_fn	= rfn;
            blk_queue_make_request(q, __make_request);
            

函數(shù) blk_queue_make_request 將函數(shù) __make_request 的地址賦予了請求隊(duì)列 q 的 make_request_fn 成員,那么, __make_request 函數(shù)才是 IO 調(diào)度層的真實(shí)入口。

__make_request 函數(shù)的主要工作為:

  1. 檢測請求隊(duì)列是否為空,若是,延緩驅(qū)動程序處理當(dāng)前請求(其目的是想積累更多的請求,這樣就有機(jī)會對相鄰的請求進(jìn)行合并,從而提高處理的性能),并跳到3,否則跳到2
  2. 試圖將當(dāng)前請求同請求隊(duì)列中現(xiàn)有的請求合并,如果合并成功,則函數(shù)返回,否則跳到3
  3. 該請求是一個(gè)新請求,創(chuàng)建新的請求描述符,并初始化相應(yīng)的域,并將該請求描述符加入到請求隊(duì)列中,函數(shù)返回

將請求放入到請求隊(duì)列中后,何時(shí)被處理就由 IO 調(diào)度器的調(diào)度算法決定了(有關(guān) IO 調(diào)度器的算法內(nèi)容請參見參考資料)。一旦該請求能夠被處理,便調(diào)用請求隊(duì)列中成員 request_fn 所指向的函數(shù)處理。這個(gè)成員的初始化也是在創(chuàng)建請求隊(duì)列時(shí)設(shè)置的:

q->request_fn	= rfn;
            blk_queue_make_request(q, __make_request);
            

第一行是將請求處理函數(shù) rfn 指針賦給了請求隊(duì)列的 request_fn 成員。而 rfn 則是在創(chuàng)建請求隊(duì)列時(shí)通過參數(shù)傳入的。

對請求處理函數(shù) request_fn 的調(diào)用意味著 IO 調(diào)度層的處理結(jié)束了。

塊設(shè)備驅(qū)動層的處理

request_fn 函數(shù)是塊設(shè)備驅(qū)動層的入口。它是在驅(qū)動程序創(chuàng)建請求隊(duì)列時(shí)由驅(qū)動程序傳遞給 IO 調(diào)度層的。

IO 調(diào)度層通過回調(diào) request_fn 函數(shù)的方式,把請求交給了驅(qū)動程序。而驅(qū)動程序從該函數(shù)的參數(shù)中獲得上層發(fā)出的 IO 請求,并根據(jù)請求中指定的信息操作設(shè)備控制器(這一請求的發(fā)出需要依據(jù)物理設(shè)備指定的規(guī)范進(jìn)行)。

到此為止,塊設(shè)備驅(qū)動層的操作結(jié)束。

塊設(shè)備層的處理

接受來自驅(qū)動層的請求,完成實(shí)際的數(shù)據(jù)拷貝工作等等。同時(shí)規(guī)定了一系列規(guī)范,驅(qū)動程序必須按照這個(gè)規(guī)范操作硬件。

后續(xù)工作

當(dāng)設(shè)備完成了 IO 請求之后,通過中斷的方式通知 cpu ,而中斷處理程序又會調(diào)用 request_fn 函數(shù)進(jìn)行處理。

當(dāng)驅(qū)動再次處理該請求時(shí),會根據(jù)本次數(shù)據(jù)傳輸?shù)慕Y(jié)果通知上層函數(shù)本次 IO 操作是否成功,如果成功,上層函數(shù)解鎖 IO 操作所涉及的頁面(在 do_generic_mapping_read 函數(shù)中加的鎖)。

該頁被解鎖后, do_generic_mapping_read() 函數(shù)就可以再次成功獲得該鎖(數(shù)據(jù)的同步點(diǎn)),并繼續(xù)執(zhí)行程序了。之后,函數(shù) sys_read 可以返回了。最終 read 系統(tǒng)調(diào)用也可以返回了。

至此, read 系統(tǒng)調(diào)用從發(fā)出到結(jié)束的整個(gè)處理過程就全部結(jié)束了。





回頁首


總結(jié)

本文介紹了 linux 系統(tǒng)調(diào)用 read 的處理全過程。該過程分為兩個(gè)部分:用戶空間的處理和核心空間的處理。在用戶空間中通過 0x80 中斷的方式將控制權(quán)交給內(nèi)核處理,內(nèi)核接管后,經(jīng)過6個(gè)層次的處理最后將請求交給磁盤,由磁盤完成最終的數(shù)據(jù)拷貝操作。在這個(gè)過程中,調(diào)用了一系列的內(nèi)核函數(shù)。如圖 6


圖6 read 系統(tǒng)調(diào)用在內(nèi)核中所經(jīng)歷的函數(shù)調(diào)用層次
 

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧洲一级片一区二区三区| 国产精品一区欧美二区| 国产欧美另类激情久久久| 亚洲男女性生活免费视频| 五月综合婷婷在线伊人| 91福利视频日本免费看看| 国产成人一区二区三区久久| 久久中文字幕中文字幕中文| 成人精品一区二区三区综合| 欧美日韩国产自拍亚洲| 国产精品免费精品一区二区| 欧美精品亚洲精品日韩专区| 在线日本不卡一区二区| 久久91精品国产亚洲| 97人妻精品免费一区二区| 99热在线播放免费观看| 日本在线高清精品人妻| 日韩人妻中文字幕精品| 亚洲综合色婷婷七月丁香| 少妇人妻中出中文字幕| 女人精品内射国产99| 美女被后入视频在线观看| 国产午夜精品亚洲精品国产| 亚洲国产婷婷六月丁香| 色狠狠一区二区三区香蕉蜜桃| 五月婷婷六月丁香狠狠| 日本高清视频在线播放| 91香蕉视频精品在线看| 91人妻人澡人人爽人人精品| 国产综合欧美日韩在线精品| 欧美大胆美女a级视频| 亚洲色图欧美另类人妻| 老司机这里只有精品视频| 国产乱久久亚洲国产精品| 好东西一起分享老鸭窝| 亚洲中文字幕有码在线观看| 亚洲欧美精品伊人久久| 亚洲av日韩一区二区三区四区| 一区二区日韩欧美精品| 天堂av一区一区一区| 欧美日韩综合在线第一页 |