有不收費(fèi)的網(wǎng)站seo引擎優(yōu)化培訓(xùn)
文件系統(tǒng)的讀寫(xiě),其實(shí)就是調(diào)用系統(tǒng)函數(shù) read 和 write。下面的代碼就是 read 和 write 的系統(tǒng)調(diào)用,在內(nèi)核里面的定義。
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{struct fd f = fdget_pos(fd);
......loff_t pos = file_pos_read(f.file);ret = vfs_read(f.file, buf, count, &pos);
......
}SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,size_t, count)
{struct fd f = fdget_pos(fd);
......loff_t pos = file_pos_read(f.file);ret = vfs_write(f.file, buf, count, &pos);
......
}
對(duì)于 read 來(lái)講,里面調(diào)用 vfs_read->__vfs_read。對(duì)于 write 來(lái)講,里面調(diào)用 vfs_write->__vfs_write。下面是 __vfs_read 和 __vfs_write 的代碼。
ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,loff_t *pos)
{if (file->f_op->read)return file->f_op->read(file, buf, count, pos);else if (file->f_op->read_iter)return new_sync_read(file, buf, count, pos);elsereturn -EINVAL;
}ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,loff_t *pos)
{if (file->f_op->write)return file->f_op->write(file, p, count, pos);else if (file->f_op->write_iter)return new_sync_write(file, p, count, pos);elsereturn -EINVAL;
}
緩存其實(shí)就是內(nèi)存中的一塊空間。因?yàn)閮?nèi)存比硬盤(pán)快得多,Linux 為了改進(jìn)性能,有時(shí)候會(huì)選擇不直接操作硬盤(pán),而是讀寫(xiě)都在內(nèi)存中,然后批量讀取或者寫(xiě)入硬盤(pán)。一旦能夠命中內(nèi)存,讀寫(xiě)效率就會(huì)大幅度提高。
根據(jù)是否使用內(nèi)存做緩存,我們可以把文件的 I/O 操作分為兩種類型。
第一種類型是緩存 I/O。大多數(shù)文件系統(tǒng)的默認(rèn) I/O 操作都是緩存 I/O。對(duì)于讀操作來(lái)講,操作系統(tǒng)會(huì)先檢查,內(nèi)核的緩沖區(qū)有沒(méi)有需要的數(shù)據(jù)。如果已經(jīng)緩存了,那就直接從緩存中返回;否則從磁盤(pán)中讀取,然后緩存在操作系統(tǒng)的緩存中。對(duì)于寫(xiě)操作來(lái)講,操作系統(tǒng)會(huì)先將數(shù)據(jù)從用戶空間復(fù)制到內(nèi)核空間的緩存中。
第二種類型是直接 IO,就是應(yīng)用程序直接訪問(wèn)磁盤(pán)數(shù)據(jù),而不經(jīng)過(guò)內(nèi)核緩沖區(qū),從而減少了在內(nèi)核緩存和用戶程序之間數(shù)據(jù)復(fù)制。
ext4 是一種日志文件系統(tǒng),是為了防止突然斷電的時(shí)候的數(shù)據(jù)丟失,引入了日志**(Journal)**模式。日志文件系統(tǒng)比非日志文件系統(tǒng)多了一個(gè) Journal 區(qū)域。文件在 ext4 中分兩部分存儲(chǔ),一部分是文件的元數(shù)據(jù),另一部分是數(shù)據(jù)。元數(shù)據(jù)和數(shù)據(jù)的操作日志 Journal 也是分開(kāi)管理的。你可以在掛載 ext4 的時(shí)候,選擇 Journal 模式。這種模式在將數(shù)據(jù)寫(xiě)入文件系統(tǒng)前,必須等待元數(shù)據(jù)和數(shù)據(jù)的日志已經(jīng)落盤(pán)才能發(fā)揮作用。這樣性能比較差,但是最安全。
另一種模式是 order 模式。這個(gè)模式不記錄數(shù)據(jù)的日志,只記錄元數(shù)據(jù)的日志,但是在寫(xiě)元數(shù)據(jù)的日志前,必須先確保數(shù)據(jù)已經(jīng)落盤(pán)。這個(gè)折中,是默認(rèn)模式。
還有一種模式是 writeback,不記錄數(shù)據(jù)的日志,僅記錄元數(shù)據(jù)的日志,并且不保證數(shù)據(jù)比元數(shù)據(jù)先落盤(pán)。這個(gè)性能最好,但是最不安全。
每一個(gè)打開(kāi)的文件都有一個(gè) struct file 結(jié)構(gòu),每個(gè) struct file 結(jié)構(gòu)都有一個(gè) struct address_space 用于關(guān)聯(lián)文件和內(nèi)存,就是在這個(gè)結(jié)構(gòu)里面,有一棵樹(shù),用于保存所有與這個(gè)文件相關(guān)的的緩存頁(yè)。
直接 I/O 讀寫(xiě)的流程是一樣的,調(diào)用 ext4_direct_IO,再往下就調(diào)用塊設(shè)備層了。緩存 I/O 讀寫(xiě)的流程不一樣。對(duì)于讀,從塊設(shè)備讀取到緩存中,然后從緩存中拷貝到用戶態(tài)。對(duì)于寫(xiě),從用戶態(tài)拷貝到緩存,設(shè)置緩存頁(yè)為臟,然后啟動(dòng)一個(gè)線程寫(xiě)入塊設(shè)備。
此文章為11月Day10學(xué)習(xí)筆記,內(nèi)容來(lái)源于極客時(shí)間《趣談Linux操作系統(tǒng)》,推薦該課程。