??不管是作業系統,還是學語言,大家應該都聽過緩沖區,那么什么是緩沖區?緩沖區又位于什么地方?在這篇文章,小編將帶你了解緩沖區,??????先讓我們欣賞下面的一張圖,讓我們恭僖EDG奪得冠軍,??????

緩沖區
- 1、緩沖區的引出
- 1.1 “\n”
- 1.2 fflush
- 2、緩沖方案
- 3、緩沖區的提供者
- 3.1 通過代碼觀察現象
- 3.2 結果分析和原因剖析
- 3.3 結論
- 4、內核緩沖區
1、緩沖區的引出
1.1 “\n”
??對于下面的代碼,他們的執行結果會是怎樣的呢?
int main()
{
printf("你好呀,緩沖區!\n");
sleep(3);
return 0;
}
int main()
{
printf("你好呀,緩沖區!");
sleep(3);
return 0;
}
??兩個代碼都是列印“你好呀,緩沖區!”,然后sleep 3s,但是唯一不同的是,代碼1在列印內容后有一個“\n”的換行,而代碼2是沒有的,這小小的一個符號,會讓我們的結果產生怎樣的不同呢?
代碼1運行結果如圖:先列印字串,然后休眠3s
|
|
|
|
1.2 fflush
??相信讀者都了解檔案描述符和重定向的相關知識,如果對這方面不了解的讀者可以查看小編的另一篇文章《檔案描述符》,我們知道,對于下面的代碼,當我們直接運行的時候,無論是顯示幕還是log.txt中都沒有對應的資料,其結果如下:
int main()
{
close(1);
umask(0);
int fd = open("log.txt",O_WRONLY|O_CREAT, 0666);
if(fd<0){
perror("open file!\n");
return 1;
}
printf("你好呀,緩沖區!\n");
close(fd);
return 0;
}
|
|
int main()
{
close(1);
umask(0);
int fd = open("log.txt",O_WRONLY|O_CREAT, 0666);
if(fd<0){
perror("open file!\n");
return 1;
}
printf("你好呀,緩沖區!\n");
fflush(stdout);
close(fd);
return 0;
}


2、緩沖方案
??一般情況下,緩沖/重繪方案分為三種:a.無緩沖;b.行緩沖(常見的對顯示幕進行重繪資料時);c.全緩沖(對檔案寫入的時候采用的是全緩沖,這里的檔案可以理解為磁盤檔案),
??說白了就是在計算機記憶體中提供了一塊記憶體空間,不斷地往空間中寫入資料,當行緩沖滿了,或者寫入了\n,他會把一行或者包括\n在內的所有內容全部重繪出來,如果沒有+\n,但是也想要他重繪就要加fflush(stdout),就會將緩沖區的內容重繪出來;全緩沖就是這塊緩沖區必須滿了才會重繪到對應的磁盤中去,
??那么為什么顯示幕是行緩沖,而檔案寫入是全緩沖呢?因為計算機遵循的是馮諾依曼體系,像檔案這樣的其實是屬于外設,或者外設之上,比如鍵盤是外設,磁盤是外設,換句話說我們要把資料重繪到磁盤或者顯示幕上都叫做往外設中寫入,外設的寫入效率是很低的,所以我們把資料積累到記憶體緩沖區中,當積累的足夠多的時候定期重繪,提高效率,
??按照這說法,那是不是全緩沖效率最高,答案是對的!!!理論上全緩沖效率確實是最高的,那為什么顯示幕還要再進行行緩沖呢?可以這樣理解,磁盤在檔案寫入的時候人是不會讀的,顯示幕不一樣,當一個人往顯示幕上寫入資料的時候,他是想盡快拿到對應的輸出結果,如果選擇無緩沖,效率就會很低,全緩沖人看到訊息就不及時,所以設定為行緩沖,所謂行緩沖就是在效率和可用性做的平衡,
3、緩沖區的提供者
3.1 通過代碼觀察現象
??為了理清這些問題,我們寫一份代碼讓大家看一下奇怪的現象,下面這份代碼是分別呼叫C庫函式介面和系統呼叫介面列印三行代碼,結果也是沒有問題的列印到顯示幕中,我們./test>log.txt,將結果進行重定向,也是正確的,
int main()
{
//C語言庫函式介面
printf("hello printf!\n");
rprintf(stdout, "hello rprintf!\n");
//系統呼叫介面
const char* str = "hello wirte!\n";
write(1 str, strlen(str));
return 0;
}
|
|
int main()
{
//C語言庫函式介面
printf("hello printf!\n");
rprintf(stdout, "hello rprintf!\n");
//系統呼叫介面
const char* str = "hello wirte!\n";
write(1 str, strlen(str));
fork();//添加了一個fork函式,生成子行程
return 0;
}
|
|
3.2 結果分析和原因剖析
結果分析: 我們往顯示幕上打,結果是3行,顯示幕采用的是行緩沖;如果重定向到檔案里,此時緩沖方式發生了變化,成了全緩沖,而且在往檔案中列印的時候,hello printf和hello fprintf列印了兩次,hello write只列印了依次一次,根據上面的結果,我們得出結論:(1.重定向還是不重定向會更改行程的緩沖方式;(2.C介面打了兩次,系統呼叫介面列印了一次,
原因剖析: fork是在程式的最后,根據程式自上而下執行的原理,當執行到fork的時候三個列印函式確實已經執行完了,但是有沒有全部重繪呢?有沒有全部顯示呢?-答案是不一定,當你是往顯示幕上列印的時候,因為他是行重繪,并且都帶了\n,所以三個列印函式都完成了列印&&重繪的作業;當你把程式運行起來重定向到log.txt中時,重繪方式變成了全緩沖,C語言的兩個庫函式僅僅完成了列印功能,內容還是在緩沖區中,沒有重繪,即hello printf和hello fprintf只是列印在了緩沖區,并沒有顯示出來,當執行到fork的時候,這個緩沖區是是父行程中的一塊記憶體區域,里面保存了當前行程的資料,所以當你fork的時候,后續完成之后,父子行程都要進行資料重繪,因為程式退出時,該緩沖區記憶體區域就要被重繪了,當父子行程各自進行重繪時,因行程具有獨立性,不管父子行程誰先重繪緩沖區,一旦一個行程重繪了緩沖區,重繪緩沖區本質就是修改緩沖區資料,此時就要發生寫時拷貝,所以C語言的庫函式列印了兩次,因為a.緩沖區的存在;b.發生了寫時拷貝!
??那為什么系統的介面write沒有列印兩次呢? 說明write沒有緩沖區!!
3.3 結論
??如果緩沖區是作業系統提供的,絕對所有的介面都要列印兩次,所以這個緩沖區是C語言自帶的!所以我們通常所說的緩沖區是語言自帶的緩沖區,所有的緩沖區都在記憶體,只不過問題是這個記憶體誰申請的,這個緩沖區是在記憶體的內核區還是記憶體的用戶區,
4、內核緩沖區
??我們前面學的是C語言提供的緩沖區,但是緩沖區不止這一個,還有其他的,記憶體分為用戶區和內核區,作業系統是軟硬體資源的管理者,我們不可能直接將FILE里面的緩沖區的內容直接重繪到磁盤和顯示幕中,而是要經過作業系統,所以在內核區還有一個屬于作業系統級別的緩沖區,這個緩沖區有自己的重繪機制,會往磁盤和顯示幕這樣的外設重繪緩沖區內容,用戶里面的緩沖區內容是往內核中的緩沖區重繪的,我們不需要關心內核中的緩沖區重繪策略,


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/353397.html
標籤:其他
上一篇:鏈表經典面試題(含圖解)
下一篇:堆疊的創建·c語言版
