在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux I/O 原理解析

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-09 11:24 ? 次閱讀

梅開二度

在 C 語法下就早已知悉基礎(chǔ) IO ,其實(shí)就是耳熟能詳?shù)奈募僮鳎f到文件操作腦子里又是一堆耳熟能詳?shù)暮瘮?shù)接口:

圖片

以一個(gè)簡單的寫入操作為例,運(yùn)行程序后當(dāng)前路徑下會(huì)生成對應(yīng)文件,文件當(dāng)中就是我們寫入的內(nèi)容:

#include
int main()
{
FILE* fp = fopen("log.txt", "w");
if (fp == NULL){
perror("fopen");
return 1;
}
int count = 5;
while (count){
fputs("hello worldn", fp);
count--;
}
fclose(fp);
return 0;
}

圖片

當(dāng)前路徑

文件操作我們打開文件時(shí),如果 fopen 對象是一個(gè)未創(chuàng)建的對象,那么就會(huì)自動(dòng)在當(dāng)前路徑生成一個(gè)該文件,這里就牽涉到一個(gè)當(dāng)前路徑 color{red} {當(dāng)前路徑}當(dāng)前路徑的概念。

比如我們在剛剛寫入后的 log.txt 文件進(jìn)行讀取:

#include
int main()
{
FILE* fp = fopen("log.txt", "r");
if (fp == NULL){
perror("fopen");
return 1;
}
char buffer[64];
for (int i = 0; i < 5; i++){
fgets(buffer, sizeof(buffer), fp);
printf("%s", buffer);
}
fclose(fp);
return 0;
}

圖片

該情況下,我們在總目錄下運(yùn)行可執(zhí)行程序 myproc,那么該可執(zhí)行程序創(chuàng)建的 log.txt 文件會(huì)出現(xiàn)在總目錄下:

這是否意味著 “當(dāng)前路徑” 就是指的 “當(dāng)前可執(zhí)行程序所處的路徑”?

我們不妨直接去查看他的路徑對吧,我們用ps -axj | head -1&&ps -axj | grep myproc | grep -v grep可以查看可執(zhí)行程序的 PID :

圖片

然后我們再利用 PID 來查看執(zhí)行路徑sudo ls /proc/8189 -al,因?yàn)槲以诳偰夸?~ 下,因此這里我使用弄了 sudo 命令進(jìn)行管理員權(quán)限查找:

圖片

這里的cwd和exe是軟鏈接,我們下文細(xì)談,所以實(shí)際上,當(dāng)前路徑不是指可執(zhí)行程序所處的路徑,而是指該可執(zhí)行程序運(yùn)行成為進(jìn)程時(shí)我們所處的路徑

三大輸入輸出流

我們一直貫徹一個(gè)理念就是 Linux 下 一切皆文件,我們?nèi)庋劭梢姷娘@示屏輸出的數(shù)據(jù),本質(zhì)是電腦讀取鍵入的字符,電腦從“電腦文件” 讀取字符,電腦再對“顯示器文件”進(jìn)行輸出

那么問題來了,在我們對這些“文件”進(jìn)行讀寫之前,為什么我們沒有一個(gè)文件打開的操作呢?

要知道打開文件一定是進(jìn)程運(yùn)行的時(shí)候打開的,而任何進(jìn)程在運(yùn)行的時(shí)候都會(huì)默認(rèn)打開三個(gè)輸入輸出流,即標(biāo)準(zhǔn)輸入流、標(biāo)準(zhǔn)輸出流、標(biāo)準(zhǔn)錯(cuò)誤流,就是 C 當(dāng)中的 stdin、stdout、stderr;C++當(dāng)中的 cin、cout、cerr,其他所有語言都有類似的概念。實(shí)際上這種特性并不是某種語言所特有的,而是由操作系統(tǒng)所支持的

其中,標(biāo)準(zhǔn)輸入流對應(yīng)的設(shè)備就是鍵盤,標(biāo)準(zhǔn)輸出流和標(biāo)準(zhǔn)錯(cuò)誤流對應(yīng)的設(shè)備都是顯示器。查看 man 手冊我們不難發(fā)現(xiàn),stdin、stdout、stderr 這仨 byd 其實(shí)就是 FILE* 類型的

extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

我們之所以可以調(diào)用 scanf 、printf 這類的函數(shù)向鍵盤顯示器進(jìn)行輸入輸出操作,其實(shí)就是程序運(yùn)行時(shí),操作系統(tǒng)默認(rèn)使用 C 的接口將這三個(gè)輸入輸出流打開。試想我們使用 fputs 函數(shù)時(shí),將其第二個(gè)參數(shù)設(shè)置為 stdout,此時(shí) fputs 函數(shù)會(huì)不會(huì)直接將數(shù)據(jù)顯示到顯示器上呢?

fputs("hello stdinn", stdout);

答案是肯定的,因?yàn)榇藭r(shí)就是用 fputs 向顯示器文件進(jìn)行了寫入操作

系統(tǒng)文件 I/O

相比 C,C++ 這些語言的接口,操作系統(tǒng)也有一套文件操作的接口,而且操作系統(tǒng)的接口更加貼近底層,而其他語言的接口本質(zhì)上也是對操作系統(tǒng)的接口的封裝,我們在 Linux、Windows 平臺(tái)下運(yùn)行 C 代碼時(shí),C 庫函數(shù)就是對 Linux、Windows 系統(tǒng)調(diào)用接口進(jìn)行的封裝,這樣做使得語言有了跨平臺(tái)性,也方便進(jìn)行二次開發(fā)

open

函數(shù)原型:

int open(const char *pathname, int flags, mode_t mode);

1.pathname 表示要打開或創(chuàng)建的目標(biāo)文件。

若pathname以路徑的方式給出,則當(dāng)需要?jiǎng)?chuàng)建該文件時(shí),就在pathname路徑下進(jìn)行創(chuàng)建。 若pathname以文件名的方式給出,則當(dāng)需要?jiǎng)?chuàng)建該文件時(shí),默認(rèn)在當(dāng)前路徑下進(jìn)行創(chuàng)建,注意當(dāng)前路徑的含義

2.flags 表示打開文件的方式。

flags 的可調(diào)用參數(shù)有如下這些:

flags 可以同時(shí)傳入多個(gè)參數(shù)選項(xiàng),這些選項(xiàng)用 “或” 運(yùn)算符連接。例如以只寫的方式打開文件時(shí),文件不存在就應(yīng)該自動(dòng)創(chuàng)建文件,則參數(shù)設(shè)置如下

O_WRONLY | O_CREAT

我們基于與運(yùn)算的最根本原因是因?yàn)椋?這些宏定義選項(xiàng)的共同點(diǎn)就是它們的二進(jìn)制序列當(dāng)中有且只有一個(gè)比特位是 1 color{red} {這些宏定義選項(xiàng)的共同點(diǎn)就是它們的二進(jìn)制序列當(dāng)中有且只有一個(gè)比特位是 1}這些宏定義選項(xiàng)的共同點(diǎn)就是它們的二進(jìn)制序列當(dāng)中有且只有一個(gè)比特位是1,除了 O_RDONLY 序列為全 0,表示他為默認(rèn)選項(xiàng),且為 1 的比特位是各不相同的,這樣一來函數(shù)內(nèi)部就可以通過使用與運(yùn)算來判斷是否設(shè)置了某一選項(xiàng)

int open(arg1, arg2, arg3){
if (arg2&O_RDONLY){
//設(shè)置了O_RDONLY選項(xiàng)
}
if (arg2&O_WRONLY){
//設(shè)置了O_WRONLY選項(xiàng)
}
if (arg2&O_RDWR){
//設(shè)置了O_RDWR選項(xiàng)
}
if (arg2&O_CREAT){
//設(shè)置了O_CREAT選項(xiàng)
}
//...
}

3.mode,表示創(chuàng)建文件的默認(rèn)權(quán)限,在不創(chuàng)建文件時(shí),此選項(xiàng)可以不設(shè)置。

我們將mode設(shè)置為 0666,則文件創(chuàng)建出來的權(quán)限如下,按理說本來應(yīng)該是 :

圖片

但是不要忘了,Linux 系統(tǒng)設(shè)有 umask 權(quán)限掩碼,文件的真正權(quán)限計(jì)算方法是:mode &( ~umask),umask 的默認(rèn)值應(yīng)該是 0002,所以在我們自己設(shè)置的權(quán)限下應(yīng)該減去 umask 得到 0664,即:

當(dāng)然,如果想繞開 umask ,直接使用我們第一手的設(shè)置,那么我們可以直接將 umask 進(jìn)行置 0 操作

umask(0);

open 返回值

open 的返回值其實(shí)是新打開文件的文件描述符 fd,我們這里嘗試一次打開多個(gè)文件,然后分別打印它們的文件描述符:

#include
#include
#include
#include
int main()
{
umask(0);
int fd1 = open("log1.txt", O_RDONLY | O_CREAT, 0666);
int fd2 = open("log2.txt", O_RDONLY | O_CREAT, 0666);
int fd3 = open("log3.txt", O_RDONLY | O_CREAT, 0666);
int fd4 = open("log4.txt", O_RDONLY | O_CREAT, 0666);
int fd5 = open("log5.txt", O_RDONLY | O_CREAT, 0666);
printf("fd1:%dn", fd1);
printf("fd2:%dn", fd2);
printf("fd3:%dn", fd3);
printf("fd4:%dn", fd4);
printf("fd5:%dn", fd5);
return 0;
}

圖片

我們又知道系統(tǒng)是無法打開一個(gè)不存在的文件 fd 會(huì)返回 -1,打開成功時(shí)如圖所示每個(gè)文件的 fd 從 3 開始且都是連續(xù)遞增的,那么問題來了:0~2 哪里去了?

所謂的文件描述符本質(zhì)上是一個(gè)指針數(shù)組的下標(biāo),指針數(shù)組當(dāng)中的每一個(gè)指針都指向一個(gè)被打開文件的文件信息,通過對應(yīng)文件的文件描述符就可以找到對應(yīng)的文件信息

open函數(shù)打開文件成功時(shí)數(shù)組當(dāng)中的指針個(gè)數(shù)增加,然后返回該指針在數(shù)組中的下標(biāo),而當(dāng)文件打開失敗時(shí)直接返回 -1,因此,成功打開多個(gè)文件時(shí)所獲得的文件描述符就是連續(xù)且遞增的

而 Linux 進(jìn)程默認(rèn)情況下會(huì)有 3 個(gè)缺省打開的文件描述符,分別就是標(biāo)準(zhǔn)輸入0、標(biāo)準(zhǔn)輸出1、標(biāo)準(zhǔn)錯(cuò)誤2,這就是為什么成功打開文件時(shí)所得到的文件描述符會(huì)從3開始

close

系統(tǒng)接口中使用close函數(shù)關(guān)閉文件,close函數(shù)的函數(shù)原型如下:

int close(int fd);

若關(guān)閉文件成功則返回 0,若關(guān)閉文件失敗則返回 -1

write

系統(tǒng)接口中使用write函數(shù)向文件寫入信息,write函數(shù)的函數(shù)原型如下:

ssize_t write(int fd, const void *buf, size_t count);

write函數(shù)將 buf 位置開始向后 count 字節(jié)的數(shù)據(jù)寫入文件描述符為 fd 的文件當(dāng)中;如果數(shù)據(jù)寫入成功,返回寫入數(shù)據(jù)的字節(jié)個(gè)數(shù),如果數(shù)據(jù)寫入失敗,返回 -1。

read

系統(tǒng)接口中使用read函數(shù)從文件讀取信息,read函數(shù)的函數(shù)原型如下:

ssize_t read(int fd, void *buf, size_t count);

read 函數(shù)從文件描述符為 fd 的文件讀取 count 字節(jié)的數(shù)據(jù)到 buf 位置當(dāng)中。如果數(shù)據(jù)讀取成功,實(shí)際讀取數(shù)據(jù)的字節(jié)個(gè)數(shù)被返回;如果數(shù)據(jù)讀取失敗,返回 -1

文件描述符fd

我們知道文件只能在進(jìn)程執(zhí)行時(shí)才能打開,且一個(gè)進(jìn)程可打開多個(gè)文件,系統(tǒng)中存在大量的進(jìn)程,這就表示系統(tǒng)可以在任何時(shí)刻存在大量已經(jīng)打開的文件

Linux 思想面對批量的處理時(shí)總會(huì)采取 “先描述后組織” 的思想,系統(tǒng)會(huì)為大量的文件描述一個(gè) file struct 的結(jié)構(gòu)體,里面存放著這些文件的主要信息,然后將結(jié)構(gòu)體以雙鏈表的形式進(jìn)行組織,相當(dāng)于將文件的管理具象成對雙鏈表的增刪查改。

但是在大量進(jìn)程和大量已打開的文件里,我們要找到每個(gè)文件的歸屬進(jìn)程系統(tǒng)就應(yīng)該建立對應(yīng)關(guān)系

對應(yīng)關(guān)系

當(dāng)一個(gè)程序運(yùn)行起來時(shí),操作系統(tǒng)會(huì)將該程序的代碼和數(shù)據(jù)加載到內(nèi)存,然后為其創(chuàng)建對應(yīng)的task_struct、mm_struct、頁表等相關(guān)的數(shù)據(jù)結(jié)構(gòu),并通過頁表建立虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系

圖片

首先在 task_struct 里有一個(gè)指針,他指向一個(gè)名為 file_struct 的結(jié)構(gòu)體,在這個(gè)結(jié)構(gòu)體里面又有一個(gè) fd_array 的指針數(shù)組,這個(gè)數(shù)組的下標(biāo)就是我們所謂的文件描述符。比如進(jìn)程打開 log.txt 時(shí)會(huì)先加載進(jìn)內(nèi)存形成 struct file ,然后將 struct file 放入一個(gè)文件的雙鏈表里,struct file 的首地址再放入鏈表中下標(biāo)為 3 處的地方,最后返回他的文件描述符即可。

圖片

向文件寫入數(shù)據(jù)時(shí),是先將數(shù)據(jù)寫入到對應(yīng)文件的緩沖區(qū)當(dāng)中,然后定期將緩沖區(qū)數(shù)據(jù)刷新,數(shù)據(jù)才能進(jìn)入磁盤。

那么為什么進(jìn)程創(chuàng)建時(shí)會(huì)默認(rèn)打開0、1、2 呢?

我們知道操作系統(tǒng)能夠識(shí)別硬件,操作系統(tǒng)能管理硬件也意味著鍵盤,顯示器這些東西都有自己對應(yīng)的 struct_file ,將這 3 個(gè) struct_file 放入雙鏈表,就會(huì)對應(yīng)填入到下標(biāo)為 0,1,2 的位置,就默認(rèn)打開了標(biāo)準(zhǔn)輸入流、輸出流、錯(cuò)誤流。

內(nèi)存文件

磁盤文件和內(nèi)存文件之間的關(guān)系就像程序和進(jìn)程的關(guān)系一樣,當(dāng)程序運(yùn)行起來后便成了進(jìn)程,而當(dāng)磁盤文件加載到內(nèi)存后便成了內(nèi)存文件。

磁盤文件分為了文件內(nèi)容和文件屬性兩部分,也將文件屬性叫做元信息,文件加載到內(nèi)存時(shí),一般先加載文件的屬性信息,當(dāng)需要對文件內(nèi)容進(jìn)行讀取、輸入或輸出等操作時(shí),再加載文件數(shù)據(jù)

分配規(guī)則

我們還是用最開始的代碼做解釋:

#include
#include
#include
#include
int main()
{
umask(0);
int fd1 = open("log1.txt", O_RDONLY | O_CREAT, 0666);
int fd2 = open("log2.txt", O_RDONLY | O_CREAT, 0666);
int fd3 = open("log3.txt", O_RDONLY | O_CREAT, 0666);
int fd4 = open("log4.txt", O_RDONLY | O_CREAT, 0666);
int fd5 = open("log5.txt", O_RDONLY | O_CREAT, 0666);
printf("fd1:%dn", fd1);
printf("fd2:%dn", fd2);
printf("fd3:%dn", fd3);
printf("fd4:%dn", fd4);
printf("fd5:%dn", fd5);
return 0;
}

然而文件描述符是從最小的 0 開始且未被分配的開始分配的,比如我關(guān)閉了 0,2 的流,那么新打開 3 個(gè)文件就是不是 3,4 5 而是 0,2,3 了

close(0);
close(2);//關(guān)閉描述符為 0,2 的文件

重定向

原理

到這里其實(shí)不難理解重定向的原理是修改文件描述符下標(biāo)對應(yīng)的 struct file* 內(nèi)容,比如我們說過的輸出重定向就是將一個(gè)本應(yīng)該輸出到一個(gè)文件的數(shù)據(jù)輸出到另一個(gè)文件

比如想讓本應(yīng)該輸出到顯示器的數(shù)據(jù)輸出到 log.txt 文件當(dāng)中,那么可以在打開 log.txt 文件之前將文件描述符為 1 的文件關(guān)閉,也就是將“顯示器文件”關(guān)閉,這樣一來,當(dāng)我們后續(xù)打開 log.txt 文件時(shí)所分配到的文件描述符就是 1

#include
#include
#include
#include
#include
int main()
{
close(1);
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);//輸出重定向
if (fd < 0){
perror("open");
return 1;
}
printf("hello worldn");
printf("hello worldn");
printf("hello worldn");
printf("hello worldn");
printf("hello worldn");
fflush(stdout);

close(fd);
return 0;
}

圖片

這里 printf 是默認(rèn)向 stdout 輸出數(shù)據(jù)的,而 stdout 指向的 FILE 結(jié)構(gòu)體中存儲(chǔ)的文件描述符就是1,因此 printf 實(shí)際上就是向文件描述符為1的文件輸出數(shù)據(jù)。C 的數(shù)據(jù)并不是立馬寫到了內(nèi)存操作系統(tǒng)里面,而是寫到了緩沖區(qū)當(dāng)中,所以使用 printf 打印完后需要使用 fflush 將緩沖區(qū)當(dāng)中的數(shù)據(jù)刷新到文件中

可以看出,我執(zhí)行 file 程序時(shí)并沒有任何結(jié)果,但是打印 log.txt 時(shí)卻得到了我想要的結(jié)果,因此就證明了上面的觀點(diǎn):

但是又有一個(gè)問題:標(biāo)準(zhǔn)輸出流和標(biāo)準(zhǔn)錯(cuò)誤流對應(yīng)的都是顯示器,它們有什么區(qū)別嗎?

答案是有的, 我們以代碼為例:

#include
int main()
{
printf("hello printfn"); //stdout
perror("perror"); //stderr

fprintf(stdout, "stdout:hello fprintfn"); //stdout
fprintf(stderr, "stderr:hello fprintfn"); //stderr
return 0;
}

結(jié)果一定會(huì)成功的輸出四行內(nèi)容,然后再對他進(jìn)行重定向到 log.txt 中:

圖片

很明顯這里 log.txt 文件當(dāng)中只有向標(biāo)準(zhǔn)輸出流輸出的兩行字符串,而向標(biāo)準(zhǔn)錯(cuò)誤流輸出的兩行數(shù)據(jù)并沒有重定向到文件當(dāng)中,而是仍然輸出到了顯示器上。實(shí)際上我們使用重定向時(shí),是對輸出流進(jìn)行了重定向,而對錯(cuò)誤流無影響 color{red} {實(shí)際上我們使用重定向時(shí),是對輸出流進(jìn)行了重定向,而對錯(cuò)誤流無影響}實(shí)際上我們使用重定向時(shí),是對輸出流進(jìn)行了重定向,而對錯(cuò)誤流無影響。

dup2

要完成重定向我們只需對 fd_array 數(shù)組當(dāng)中元素的拷貝即可,Linux 中對于重定向給出了一個(gè)接口:==dup2 ==,我們可以使用這個(gè)接口完成重定向:

int dup2(int oldfd, int newfd);

dup2 會(huì)將 fd_array[oldfd] 的內(nèi)容拷貝到 fd_array[newfd] 當(dāng)中,如果有必要的話我們需要先使用關(guān)閉文件描述符為 newfd 的文件,dup2 函數(shù)返回值如果調(diào)用成功返回 newfd,否則返回 -1。

需要的是:

  1. 如果 oldfd 不是有效的文件描述符,則 dup2 調(diào)用失敗,并且此時(shí)文件描述符為 newfd 的文件沒有被關(guān)閉
  2. oldfd 是一個(gè)有效的文件描述符,但是 newfd 和 oldfd 具有相同的值,則 dup2 不做任何操作,并返回 newfd

比如:

#include
#include
#include
#include
#include
int main()
{
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
if (fd < 0){
perror("open");
return 1;
}
close(1);
dup2(fd, 1);
printf("hello printfn");
fprintf(stdout, "hello fprintfn");
return 0;
}

圖片

就像這樣,數(shù)據(jù)會(huì)被傳到 log.txt 里面。

重定向模擬實(shí)現(xiàn)

在我們自己實(shí)現(xiàn) shell 的基礎(chǔ)上,是可以自己實(shí)現(xiàn)重定向功能的。對于獲取到的命令進(jìn)行判斷,若命令當(dāng)中包含重定向符號 >、>> 或是 <,則該命令需要進(jìn)行處理

設(shè)置 type 變量,type 為 0 表示命令當(dāng)中為輸出重定向,type 為 1 表示追加重定向,type為 2 表示輸入重定向。若 type 值為 0 或者 1,則使用 dup2 接口實(shí)現(xiàn)目標(biāo)文件與標(biāo)準(zhǔn)輸出流的重定向;若 type 值為 2,則使用 dup2 接口實(shí)現(xiàn)目標(biāo)文件與標(biāo)準(zhǔn)輸入流的重定向

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LEN 1024 //命令最大長度
#define NUM 32 //命令拆分后的最大個(gè)數(shù)
int main()
{
int type = 0; //0 >, 1 >>, 2 <
char cmd[LEN]; //存儲(chǔ)命令
char* myargv[NUM]; //存儲(chǔ)命令拆分后的結(jié)果
char hostname[32]; //主機(jī)名
char pwd[128]; //當(dāng)前目錄
while (1){
//獲取命令提示信息
struct passwd* pass = getpwuid(getuid());
gethostname(hostname, sizeof(hostname)-1);
getcwd(pwd, sizeof(pwd)-1);
int len = strlen(pwd);
char* p = pwd + len - 1;
while (*p != '/'){
p--;
}
p++;
//打印命令提示信息
printf("[%s@%s %s]$ ", pass->pw_name, hostname, p);
//讀取命令
fgets(cmd, LEN, stdin);
cmd[strlen(cmd) - 1] = '?';

//實(shí)現(xiàn)重定向功能
char* start = cmd;
while (*start != '?'){
if (*start == '>'){
type = 0; //遇到一個(gè)'>',輸出重定向
*start = '?';
start++;
if (*start == '>'){
type = 1; //遇到第二個(gè)'>',追加重定向
start++;
}
break;
}
if (*start == '<'){
type = 2; //遇到'<',輸入重定向
*start = '?';
start++;
break;
}
start++;
}
if (*start != '?'){ //start位置不為'?',說明命令包含重定向內(nèi)容
while (isspace(*start)) //跳過重定向符號后面的空格
start++;
}
else{
start = NULL; //start設(shè)置為NULL,標(biāo)識(shí)命令當(dāng)中不含重定向內(nèi)容
}

//拆分命令
myargv[0] = strtok(cmd, " ");
int i = 1;
while (myargv[i] = strtok(NULL, " ")){
i++;
}
pid_t id = fork(); //創(chuàng)建子進(jìn)程執(zhí)行命令
if (id == 0){
//child
if (start != NULL){
if (type == 0){ //輸出重定向
int fd = open(start, O_WRONLY | O_CREAT | O_TRUNC, 0664); //以寫的方式打開文件(清空原文件內(nèi)容)
if (fd < 0){
error("open");
exit(2);
}
close(1);
dup2(fd, 1); //重定向
}
else if (type == 1){ //追加重定向
int fd = open(start, O_WRONLY | O_APPEND | O_CREAT, 0664); //以追加的方式打開文件
if (fd < 0){
perror("open");
exit(2);
}
close(1);
dup2(fd, 1); //重定向
}
else{ //輸入重定向
int fd = open(start, O_RDONLY); //以讀的方式打開文件
if (fd < 0){
perror("open");
exit(2);
}
close(0);
dup2(fd, 0); //重定向
}
}

execvp(myargv[0], myargv); //child進(jìn)行程序替換
exit(1); //替換失敗的退出碼設(shè)置為1
}
//shell
int status = 0;
pid_t ret = waitpid(id, &status, 0); //shell等待child退出
if (ret > 0){
printf("exit code:%dn", WEXITSTATUS(status)); //打印child的退出碼
}
}
return 0;
}

效果如圖:

圖片

FILE 的文件描述符

訪問文件的本質(zhì)都是通過文件描述符進(jìn)行訪問的,而且?guī)旌瘮?shù)又是對系統(tǒng)接口的封裝,所以在庫函數(shù)中的 FILE 結(jié)構(gòu)體也必定存在文件描述符 fd

我們在 / u s r / i n c l u d e / s t d i o . h color{red} {/usr/include/stdio.h}/usr/include/stdio.h 頭文件中可以看到下面這句代碼,也就是說 FILE 實(shí)際上就是struct _IO_FILE 結(jié)構(gòu)體的一個(gè)別名。

typedef struct _IO_FILE FILE;

接下來轉(zhuǎn)到 struct _IO_FILE 結(jié)構(gòu)體的定義,其中我們可以看到一個(gè)名為_fileno的成員,這個(gè)成員實(shí)際上就是封裝的文件描述符

struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

//緩沖區(qū)相關(guān) /* The following pointers correspond to the C++ streambuf
protocol. / / Note: Tk uses the _IO_read_ptr and _IO_read_end
fields directly. / char _IO_read_ptr; /* Current read pointer /
char _IO_read_end; /* End of get area. / char _IO_read_base;
/* Start of putback+get area. / char _IO_write_base; /* Start of
put area. / char _IO_write_ptr; /* Current put pointer. / char
_IO_write_end; /* End of put area. / char _IO_buf_base; /* Start of reserve area. / char _IO_buf_end; /* End of reserve area. /
/ The following fields are used to support backing up and undo. */
char _IO_save_base; / Pointer to start of non-current get area. */
char _IO_backup_base; / Pointer to first valid character of backup
area */ char _IO_save_end; / Pointer to end of non-current get
area. */

struct _IO_marker *_markers;

struct _IO_FILE *_chain;

int _fileno; //封裝的文件描述符
#if 0 int _blksize;
#else int _flags2;
#endif _IO_off_t _old_offset; /* This used to be _offset but it’s too small. */

#define __HAVE_COLUMN /* temporary / / 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char
_vtable_offset; char _shortbuf[1];

/* char* _save_gptr; char* _save_egptr; */

_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE };

那我們再來聊聊文件函數(shù)的底層:

fopen 函數(shù)會(huì)為用戶在上層調(diào)申請 FILE 結(jié)構(gòu)體,返回 FILE* 結(jié)構(gòu)體指針,底層上調(diào)用 open 函數(shù)獲取文件的 fd,并將 fd 交給 _fileno 填充,這樣就完成了文件的打開操作。其他的比如 fread、fwrite、fputs、fgets ,都會(huì)先根據(jù)我們傳入的文件指針找到對應(yīng)的FILE結(jié)構(gòu)體,然后在FILE結(jié)構(gòu)體當(dāng)中找到文件描述符,最后通過文件描述符對文件進(jìn)行的一系列操作

我們以三種輸出函數(shù)為例:

#include
#include
int main()
{
//c
printf("hello printfn");
fputs("hello fputsn", stdout);
//system
write(1, "hello writen", 12);
fork();
return 0;
}

圖片

看到這結(jié)果是不是覺得淦!好怪。按照代碼邏輯的話,這里應(yīng)該只會(huì)打印出三個(gè)句子對應(yīng)三個(gè)函數(shù),但是為什么這里有兩個(gè)函數(shù)出現(xiàn)了兩次呢?

不難發(fā)現(xiàn),這里重復(fù)的兩個(gè)函數(shù)都是 C 庫函數(shù),我們就要牽扯到三種緩沖方式了:

無緩沖
行緩沖(對顯示器進(jìn)行刷新數(shù)據(jù))
全緩沖(對磁盤文件寫入數(shù)據(jù))

直接執(zhí)行可執(zhí)行程序,將數(shù)據(jù)打印到顯示器時(shí)所采用的就是行緩沖,因?yàn)榇a當(dāng)中每句話后面都有 n,所以當(dāng)我們執(zhí)行完對應(yīng)代碼后就立即將數(shù)據(jù)刷新到了顯示器上

如果將運(yùn)行結(jié)果重定向到 log.txt 文件時(shí),數(shù)據(jù)的刷新策略就變?yōu)榱巳彌_,此時(shí)使用 printf 和 fputs 打印的數(shù)據(jù)都打印到了C語言自帶的緩沖區(qū)當(dāng)中,之后 fork 創(chuàng)建子進(jìn)程時(shí),由于進(jìn)程間具有獨(dú)立性,而之后當(dāng)父進(jìn)程或是子進(jìn)程對要刷新緩沖區(qū)內(nèi)容時(shí),本質(zhì)就是對父子進(jìn)程共享的數(shù)據(jù)進(jìn)行了修改,此時(shí)就需要對數(shù)據(jù)進(jìn)行寫時(shí)拷貝,至此緩沖區(qū)當(dāng)中的數(shù)據(jù)就變成了兩份,一份父進(jìn)程的,一份子進(jìn)程的,所以重定向到 log.txt 文件當(dāng)中 printf 和 puts 函數(shù)打印的數(shù)據(jù)就有兩份。但由于 write 是系統(tǒng)接口,我們可以將 write 看作是沒有緩沖區(qū)的,因此 write 打印的數(shù)據(jù)就只打印了一份

這個(gè)緩沖區(qū)是誰提供的?

他是 C 自帶的,如果說這個(gè)緩沖區(qū)是操作系統(tǒng)提供的,那么 printf、fputs 和 write 打印的數(shù)據(jù)重定向到文件后都應(yīng)該打印兩次

這個(gè)緩沖區(qū)在哪?

printf 是將數(shù)據(jù)打印到 stdout 里面,而 stdout 就是一個(gè) FILE* 指針,在 FILE 結(jié)構(gòu)體當(dāng)中還有一大部分成員是用于記錄緩沖區(qū)相關(guān)的信息的,我們來看看底層代碼:

//緩沖區(qū)相關(guān)
/* The following pointers correspond to the C++ streambuf protocol. /
/ Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. /
char _IO_read_ptr; /* Current read pointer /
char _IO_read_end; /* End of get area. /
char _IO_read_base; /* Start of putback+get area. /
char _IO_write_base; /* Start of put area. /
char _IO_write_ptr; /* Current put pointer. /
char _IO_write_end; /* End of put area. /
char _IO_buf_base; /* Start of reserve area. /
char _IO_buf_end; /* End of reserve area. /
/ The following fields are used to support backing up and undo. */
char _IO_save_base; / Pointer to start of non-current get area. */
char _IO_backup_base; / Pointer to first valid character of backup area */
char _IO_save_end; / Pointer to end of non-current get area. */

抗疫知道這里緩沖區(qū)是由 C 提供,在 FILE 結(jié)構(gòu)體當(dāng)中進(jìn)行維護(hù),F(xiàn)ILE 結(jié)構(gòu)體當(dāng)中不僅保存了對應(yīng)文件的文件描述符還保存了用戶緩沖區(qū)的相關(guān)信息

操作系統(tǒng)有緩沖區(qū)嗎?

答案是一定的,其實(shí)我們數(shù)據(jù)并不是直接刷新到顯示器和磁盤上的,而是先刷新到操作系統(tǒng)的緩沖區(qū)里面,再經(jīng)過緩沖區(qū)加載到顯示器和磁盤上,當(dāng)然這里我們先不關(guān)心操作系統(tǒng)的刷新策略。

圖片

因?yàn)椴僮飨到y(tǒng)是進(jìn)行軟硬件資源管理的軟件,所以要將數(shù)據(jù)刷新到具體外設(shè)硬件上,就必須要經(jīng)過操作系統(tǒng),看一下層狀結(jié)構(gòu)圖也許會(huì)更清楚:

圖片

inode

磁盤文件由兩部分構(gòu)成,分別是文件內(nèi)容和文件屬性。比如文件名、文件大小以及文件創(chuàng)建時(shí)間等信息都是文件屬性,文件屬性又被稱為元信息

在命令行當(dāng)中輸入ls -l,即可顯示當(dāng)前目錄下各文件的屬性信息,各種文件屬性排列如下:

圖片

在 Linux 操作系統(tǒng)中,文件的元信息和內(nèi)容是分離存儲(chǔ)的,其中保存元信息的結(jié)構(gòu)稱之為 i n o d e color{red} {其中保存元信息的結(jié)構(gòu)稱之為 inode}其中保存元信息的結(jié)構(gòu)稱之為inode,因?yàn)橄到y(tǒng)當(dāng)中可能存在大量的文件,所以我們需要給每個(gè)文件的屬性搞一個(gè)唯一的編號,即 inode 號

也就是說,inode 是一個(gè)文件的屬性集合,Linux 中幾乎每個(gè)文件都有一個(gè) inode,為了區(qū)分系統(tǒng)當(dāng)中大量的 inode,我們?yōu)槊總€(gè) inode 設(shè)置了 inode 編號,ls -i 命令即可查看當(dāng)前目錄下的文件和他的 inode 編號:

圖片

無論是文件內(nèi)容還是文件屬性,他們都是存儲(chǔ)在磁盤里面的

磁盤

盤是一種永久性存儲(chǔ)介質(zhì),在計(jì)算機(jī)中,磁盤幾乎是唯一的機(jī)械設(shè)備。與磁盤相對應(yīng)的就是內(nèi)存,內(nèi)存是掉電易失存儲(chǔ)介質(zhì),目前所有的普通文件都是在磁盤中存儲(chǔ)的,磁盤在馮諾依曼體系結(jié)構(gòu)當(dāng)中既可以充當(dāng)輸入設(shè)備,又可以充當(dāng)輸出設(shè)備:

圖片

尋址方案

對磁盤讀寫時(shí),一般有以下 3 個(gè)步驟:

確定讀寫信息的盤面
確定讀寫信息的柱面
確定讀寫信息的扇區(qū)

分區(qū)與存儲(chǔ)介質(zhì)

要理解文件系統(tǒng),我們必須要先將磁盤結(jié)構(gòu)理解為線性的存儲(chǔ)介質(zhì),比如說小學(xué)英語的磁帶,你扯出磁帶條條時(shí)有沒有想過復(fù)讀機(jī)讀取磁帶的信息,放完重來必要做倒帶的操作才能從頭開始,這就非常貼切線性結(jié)構(gòu)了。

圖片

磁盤分區(qū)

磁盤也被稱為塊設(shè)備,以扇區(qū)為單位,一個(gè)扇區(qū)的大小通常為512字節(jié)。如果以大小為 512G 的磁盤為例,該磁盤就可被分為十億多個(gè)扇區(qū):

圖片

計(jì)為了更好的管理磁盤,磁盤進(jìn)行了分區(qū),原理類似于將整個(gè)國家劃分為省市區(qū)縣進(jìn)行管理,使用分區(qū)編輯器在磁盤上劃分幾個(gè)邏輯部分,盤片一旦劃分成數(shù)個(gè)分區(qū),不同的目錄與文件就可以存儲(chǔ)進(jìn)不同的分區(qū),分區(qū)越多,文件的性質(zhì)區(qū)分越細(xì),Windows 磁盤就被分為 C 盤和 D 盤

Linux 也是可以查看文件的分區(qū)信息:

ls /dev/vda* -l

格式化

磁盤分區(qū)完成后就會(huì)進(jìn)行格式化,格式化后每個(gè)分區(qū) inode 數(shù)就會(huì)被確定下來,所以說格式化是對分區(qū)進(jìn)行初始化的一種操作,會(huì)導(dǎo)致所有資源被清除,本質(zhì)上是對分區(qū)后各個(gè)區(qū)域?qū)懭牍芾硇畔?/p>

其中的管理信息內(nèi)容是由文件系統(tǒng)決定的,不同的文件系統(tǒng)格式化時(shí)管理信息是不同的,常見的文件系統(tǒng)有 EXT2、EXT3、XFS、NTFS 等

EXT2 存儲(chǔ)方案

而對于每一個(gè)分區(qū)來說,分區(qū)的頭部有一個(gè)啟動(dòng)塊(Boot Block),對于該分區(qū)的其余區(qū)域,EXT2 文件系統(tǒng)會(huì)根據(jù)分區(qū)大小劃分為一個(gè)個(gè)的塊組(Block Group)

圖片

啟動(dòng)塊的大小是確定的,而塊組的大小是由格式化的時(shí)候確定的,并且不可以更改。

其次,每個(gè)組塊都有著相同的組成結(jié)構(gòu),每個(gè)組塊都由超級塊(Super Block)、塊組描述符表(Group Descriptor Table)、塊位圖(Block Bitmap)、inode位圖(inode Bitmap)、inode表(inode Table)以及數(shù)據(jù)表(Data Block)組成:

圖片

Super Block: 存放文件系統(tǒng)本身的結(jié)構(gòu)信息。主要有:Data Block和inode的總量、未使用的Data Block和inode的數(shù)量、一個(gè)Data Block和inode的大小、最近一次掛載時(shí)間。Super Block的信息被破壞,可以說整個(gè)文件系統(tǒng)結(jié)構(gòu)就被破壞了
Group Descriptor Table: 塊組描述符表,描述該分區(qū)當(dāng)中塊組的屬性信息
Block Bitmap: 塊位圖中記錄著 Data Block 中哪個(gè)數(shù)據(jù)塊已經(jīng)被占用或沒有被占用
inode Bitmap: inode 位圖中記錄著每個(gè)inode 是否空閑可用
inode Table: 文件屬性,即每個(gè)文件的inode。
Data Blocks: 文件內(nèi)容

因?yàn)?super block 極為重要,所以一般在其他塊組中會(huì)存在冗余,方便損壞后拷貝恢復(fù)

此時(shí)我們就可以理解文件創(chuàng)建了:

  1. 先通過遍歷 inode 位圖找到一個(gè)空閑的 inode
  2. 再在 inode 表當(dāng)中找到對應(yīng)的 inode,并將文件的屬性信息填充進(jìn) inode 結(jié)構(gòu)中。
  3. 將該文件的文件名和inode指針添加到目錄文件的數(shù)據(jù)塊中

文件寫入也是同理:

  1. 通過 inode 編號找到對應(yīng)的 inode 結(jié)構(gòu)
  2. 通過 inode 結(jié)構(gòu)找到存儲(chǔ)該文件內(nèi)容的數(shù)據(jù)塊,并將數(shù)據(jù)寫入數(shù)據(jù)塊
  3. 若不存在數(shù)據(jù)塊或申請的數(shù)據(jù)塊已被寫滿,則通過遍歷塊位圖的方式找到一個(gè)空閑的塊號,并在數(shù)據(jù)區(qū)當(dāng)中找到對應(yīng)的空閑塊,再將數(shù)據(jù)寫入數(shù)據(jù)塊,最后還需要建立數(shù)據(jù)塊和 inode 結(jié)構(gòu)的對應(yīng)關(guān)系(對應(yīng)關(guān)系是通過數(shù)組進(jìn)行維護(hù)的,該數(shù)組一般可以存儲(chǔ) 15 個(gè)元素,其中前 12 個(gè)元素分別對應(yīng)文件使用的 12 個(gè)數(shù)據(jù)塊,剩余的三個(gè)元素分別是一級索引、二級索引和三級索引,當(dāng)該文件使用數(shù)據(jù)塊的個(gè)數(shù)超過12個(gè)時(shí),可以用這三個(gè)索引進(jìn)行數(shù)據(jù)塊擴(kuò)充)

文件刪除也是同理:

其實(shí)刪除并不會(huì)真正將文件信息刪除,而只是將其 inode 和數(shù)據(jù)塊號置為無效,所以刪除文件后短時(shí)間內(nèi)是可以恢復(fù)的。

短時(shí)間內(nèi)是個(gè)什么意思呢,因?yàn)槲募?yīng)的 inode 號和數(shù)據(jù)塊號被置為了無效,后續(xù)創(chuàng)建其他文件或是對其他文件進(jìn)行寫入操作申請 inode 號和數(shù)據(jù)塊號時(shí),可能會(huì)將該無效了的 inode號和數(shù)據(jù)塊號分配出去,此時(shí)刪除文件的數(shù)據(jù)就會(huì)被覆蓋,也就無法恢復(fù)文件了

這也就就是了為什么拷貝文件的時(shí)候很慢,而刪除文件的時(shí)候很快

文件目錄也是同理:

Linux下一切皆文件,目錄當(dāng)然也會(huì)被看作為文件。目錄有自己的屬性信息,他 inode 結(jié)構(gòu)中存儲(chǔ)的是目錄的屬性信息,比如目錄的大小、目錄的擁有者等;目錄的數(shù)據(jù)塊當(dāng)中存儲(chǔ)的就是該目錄下的文件名以及對應(yīng)文件的 inode 指針。

注意: 文件名并沒有存儲(chǔ)在自己的 inode 結(jié)構(gòu)當(dāng)中,而是存儲(chǔ)在該文件所處目錄文件的文件內(nèi)容當(dāng)中。因?yàn)橄到y(tǒng)并不關(guān)心文件名,他只關(guān)心文件的 inode ,而文件名和 inode 指針存儲(chǔ)在其目錄文件的文件內(nèi)容當(dāng)中后,目錄通過文件名和文件的 inode 指針即可將文件名和文件內(nèi)容及其屬性連接起來

軟鏈接

文件軟鏈接的創(chuàng)建可以通過這個(gè)命令:

ln -s myproc myproc-s

效果如下:

圖片

我們可以通過ls -i可以看到軟鏈接的 inode 與源文件的 inode 是不同的,并且軟鏈接的大小比源文件的大小要小得多!

圖片

刪除源文件后軟鏈接文件不能獨(dú)立存在,雖然仍保留文件名,但卻不能執(zhí)行或是查看軟鏈接的內(nèi)容

硬鏈接

文件硬鏈接的創(chuàng)建可以通過這個(gè)命令:

ln myproc myproc-h

效果如下:

圖片

圖片

我們可以通過ls -i可以看到硬鏈接的 inode 與源文件的 inode 是相同的,并且硬鏈接文件的大小與源文件的大小也是相同的,特別注意的是,當(dāng)創(chuàng)建了一個(gè)硬鏈接文件后,該硬鏈接文件和源文件的硬鏈接數(shù)都變成了 2

圖片

所以硬鏈接文件就是源文件的一個(gè)別名 color{red} {源文件的一個(gè)別名}源文件的一個(gè)別名,一個(gè)文件有幾個(gè)文件名,該文件的硬鏈接數(shù)就是幾,這里 inode 為 659031 的文件有 myproc 和 myproc-h 兩個(gè)文件名,因此該文件的硬鏈接數(shù)為 2

與軟連接不同的是,當(dāng)硬鏈接的源文件被刪除后,硬鏈接文件仍能正常執(zhí)行,只是文件的鏈接數(shù)減少了一個(gè),但是硬鏈接可以同步修改多個(gè)不在或者同在一個(gè)目錄下的文件名,其中一個(gè)修改后,所有與其有硬鏈接的文件都會(huì)一起被修改

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8961

    瀏覽量

    153260
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11469

    瀏覽量

    212889
  • 程序
    +關(guān)注

    關(guān)注

    117

    文章

    3824

    瀏覽量

    82491
  • 文件系統(tǒng)
    +關(guān)注

    關(guān)注

    0

    文章

    294

    瀏覽量

    20302
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4372

    瀏覽量

    64285
收藏 人收藏

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    i.MX6ULL】驅(qū)動(dòng)開發(fā)9——Linux I/O模型分析并進(jìn)來類比

    本篇介紹了Linux中的五種I/O模型:阻塞式I/O模型、非阻塞式I/
    的頭像 發(fā)表于 05-26 09:05 ?2524次閱讀
    【<b class='flag-5'>i</b>.MX6ULL】驅(qū)動(dòng)開發(fā)9——<b class='flag-5'>Linux</b> <b class='flag-5'>I</b>/<b class='flag-5'>O</b>模型分析并進(jìn)來類比

    Linux系統(tǒng)中網(wǎng)絡(luò)I/O性能改進(jìn)方法的研究

    選擇并設(shè)計(jì)高效的網(wǎng)絡(luò)I/O模型是改善服務(wù)器性能的關(guān)鍵。該文通過對Linux系統(tǒng)中幾種網(wǎng)絡(luò)I/O模型的分析和研究,提出3種改善網(wǎng)絡(luò)
    發(fā)表于 04-09 09:41 ?28次下載

    Java I/O 的相關(guān)方法分析

    I/O,即 Input/Output(輸入/輸出) 的簡稱。就 I/O 而言,概念上有 5 種模型:blocking I/
    發(fā)表于 09-27 13:18 ?0次下載
    Java <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 的相關(guān)方法分析

    Linux 系統(tǒng)應(yīng)用編程之標(biāo)準(zhǔn)I/O詳解

    本章前面幾節(jié)所述的文件及I/O讀寫都是基于文件描述符的。這些都是基本的I/O控制,是不帶緩存的。而本節(jié)所要討論的I/
    發(fā)表于 10-18 15:45 ?0次下載

    學(xué)會(huì)處理Linux內(nèi)核訪問外設(shè)I/O資源的方式

    Linux內(nèi)核訪問外設(shè)I/O內(nèi)存資源的方式有兩種:動(dòng)態(tài)映射(ioremap)和靜態(tài)映射(map_desc)。
    發(fā)表于 05-05 13:54 ?612次閱讀

    如何更改 LinuxI/O 調(diào)度器

    LinuxI/O 調(diào)度器是一個(gè)以塊式 I/O 訪問存儲(chǔ)卷的進(jìn)程,有時(shí)也叫磁盤調(diào)度器。Linux
    發(fā)表于 05-15 15:54 ?958次閱讀
    如何更改 <b class='flag-5'>Linux</b> 的 <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 調(diào)度器

    Linux I/O多路復(fù)用

    Linux中一切皆文件,不論是我們存儲(chǔ)在磁盤上的字符文件,可執(zhí)行文件還是我們的接入電腦的I/O設(shè)備等都被VFS抽象成了文件,比如標(biāo)準(zhǔn)輸入設(shè)備默認(rèn)是鍵盤,我們在操作標(biāo)準(zhǔn)輸入設(shè)備的時(shí)候
    發(fā)表于 04-02 14:31 ?368次閱讀

    Linux中如何使用信號驅(qū)動(dòng)式I/O

    一、Linux 的 5 種 IO 模型 二、如何使用信號驅(qū)動(dòng)式 I/O? 三、內(nèi)核何時(shí)會(huì)發(fā)送 “IO 就緒” 信號? 四、最簡單的示例 五、擴(kuò)展知識(shí) 一、Linux 的 5 種 IO
    的頭像 發(fā)表于 03-12 14:47 ?2637次閱讀
    <b class='flag-5'>Linux</b>中如何使用信號驅(qū)動(dòng)式<b class='flag-5'>I</b>/<b class='flag-5'>O</b>?

    深入理解Linux傳統(tǒng)的System Call I/O

    傳統(tǒng)的 System Call I/OLinux 系統(tǒng)中,傳統(tǒng)的訪問方式是通過 write() 和 read() 兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn)的,通過 read() 函數(shù)讀取文件到到緩存區(qū)中,然后通過
    的頭像 發(fā)表于 11-19 09:52 ?2160次閱讀
    深入<b class='flag-5'>理解</b><b class='flag-5'>Linux</b>傳統(tǒng)的System Call <b class='flag-5'>I</b>/<b class='flag-5'>O</b>

    Linux磁盤I/O的性能指標(biāo)和查看性能工具

    在我之前的文章:《探討 Linux 的磁盤 I/O》中,我談到了 Linux 磁盤 I/O 的工
    的頭像 發(fā)表于 05-14 15:21 ?2906次閱讀

    Linux I/O重定向詳解

    Linux I/O重定向可以定義為,更改從命令讀取輸入到命令發(fā)送輸出的方式。你可以重定向命令的輸入和輸出。對于重定向符號,可以是 或者 | 。
    的頭像 發(fā)表于 05-04 14:34 ?1034次閱讀

    深入理解 LinuxI/O 系統(tǒng)

    傳統(tǒng)的 System Call I/OLinux 系統(tǒng)中,傳統(tǒng)的訪問方式是通過 write() 和 read() 兩個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn)的,通過 read() 函數(shù)讀取文件到到緩存區(qū)中,然后通過 write() 方法把緩存中的數(shù)
    發(fā)表于 05-26 09:31 ?481次閱讀
    深入<b class='flag-5'>理解</b> <b class='flag-5'>Linux</b> 的 <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 系統(tǒng)

    Linux系統(tǒng)下I/O操作講解

    Linux系統(tǒng)下I/O 一、I/O簡介 I/O(輸入
    的頭像 發(fā)表于 11-08 15:13 ?1754次閱讀
    <b class='flag-5'>Linux</b>系統(tǒng)下<b class='flag-5'>I</b>/<b class='flag-5'>O</b>操作講解

    Linux I/O 接口的類型及處理流程

    Linux I/O 接口 Linux I/O 接口可以分為以下幾種類型: 文件
    的頭像 發(fā)表于 11-08 16:43 ?1247次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 接口的類型及處理流程

    I/O接口與I/O端口的區(qū)別

    在計(jì)算機(jī)系統(tǒng)中,I/O接口與I/O端口是實(shí)現(xiàn)CPU與外部設(shè)備數(shù)據(jù)交換的關(guān)鍵組件,它們在功能、結(jié)構(gòu)、作用及運(yùn)作機(jī)制上均存在顯著差異,卻又相互協(xié)同工作,共同構(gòu)建起CPU與外部設(shè)備之間的橋梁
    的頭像 發(fā)表于 02-02 16:00 ?1172次閱讀
    主站蜘蛛池模板: 国产精品亚洲一区二区三区在线播放 | 免看乌克兰a一级 | 成年网站在线播放 | 韩国理论片在线看2828dy | 亚洲精品成人网 | 亚洲丰满熟妇毛片在线播放 | 天天躁日日躁成人字幕aⅴ 天天躁夜夜躁 | 亚洲人成电影综合网站色 | 天天干天天操天天爽 | 丁香六月激情综合 | 美女国产在线观看免费观看 | 中文天堂资源在线www | 美女被羞羞产奶视频网站 | 让她爽的喷水叫爽乱 | eeuss久久久精品影院 | 成人伊人亚洲人综合网站222 | 日日日天天射天天干视频 | 在线视频免费播放 | 中文字幕欧美日韩 | 亚洲 另类色区 欧美日韩 | 国产亚洲精品久久午夜 | 在线观看视频h | 91国内在线 | 午夜欧美| 在线亚洲精品 | 91夜夜人人揉人人捏人人添 | 婷婷九月丁香 | 国产网站在线 | 免费毛片网站 | 高清成年美女黄网站色大 | 国产激情视频一区二区三区 | 日本加勒比在线视频 | 嫩草影院永久入口在线观看 | 91啦视频在线 | 美女视频一区二区三区 | 片黄免费 | 中文字幕天天干 | 色网站综合 | 五月天婷婷影院 | 国模伊人| 黄色hd |