> 作者:?舊言~
> 座右銘:松樹千年終是朽,槿花一日自為榮。> 目標(biāo):理解緩沖區(qū)
> 毒雞湯:有些事情,總是不明白,所以我不會(huì)堅(jiān)持。早安!
> 專欄選自:Linux初階
> 望小伙伴們點(diǎn)贊??收藏?加關(guān)注喲????
??前言
緩沖區(qū)大家其實(shí)不陌生,像我們使用的 VS2019 編譯器這里就有緩沖區(qū),那它到底在哪呢,比如我們打印時(shí)的窗口需要我們輸入,這里就有緩沖區(qū)。其實(shí)在輸入我們也好奇為什么編譯器會(huì)等待我們輸入,這里就不得不談我們緩沖區(qū)的相關(guān)知識(shí),那具體是什么呢?今天我們來解開這層面紗。
?主體
學(xué)習(xí)【Linux】基礎(chǔ)IO----理解緩沖區(qū)咱們按照下面的圖解:
?? 認(rèn)識(shí)緩沖區(qū)
???為什么有緩沖區(qū)
概念:
緩沖區(qū) (buffer),它是內(nèi)存空間的一部分。 也就是說,在內(nèi)存空間中預(yù)留了一定的存儲(chǔ)空間,這些存儲(chǔ)空間用來緩沖輸入或輸出的數(shù)據(jù),這部分預(yù)留的空間就叫做緩沖區(qū),顯然緩沖區(qū)是具有一定大小的。
理解:
數(shù)據(jù)如果直接從內(nèi)存到磁盤,在內(nèi)存中速度快,但是訪問外設(shè)效率比較低,那太消耗時(shí)間了,屬于外設(shè)IO,所以緩沖區(qū)的意義就是節(jié)省進(jìn)程進(jìn)行數(shù)據(jù)IO的時(shí)間!進(jìn)程需要把數(shù)據(jù)拷貝到緩沖區(qū)里:我們并不需要拷貝,而是調(diào)用fwrite,與其理解fwrite是寫入到文件的函數(shù),倒不如理解fwrite是拷貝函數(shù),將數(shù)據(jù)從進(jìn)程拷貝到緩沖區(qū)或者外設(shè)當(dāng)中。
圖解:
數(shù)據(jù)可以直接拷貝到緩沖區(qū),高速設(shè)備不用在等待低速設(shè)備,提高計(jì)算機(jī)的效率。
???緩沖區(qū)如何刷新
概念:
緩沖區(qū)的刷新策略:如果有一塊數(shù)據(jù),一次寫入到外設(shè)(效率最高)vs如果有一塊數(shù)據(jù),多次少量寫入到外設(shè),需要多次IO。緩沖區(qū)一定結(jié)合具體的設(shè)備定制自己的刷新策略
方法:
- 立即刷新——無緩沖 ,場(chǎng)景較少,比如調(diào)用printf直接fflush
- 行刷新——行緩沖——顯示器 ,數(shù)據(jù)的printf帶上\n就會(huì)立馬顯示到顯示器上。顯示器為什么是行緩沖:顯示器是外設(shè),進(jìn)程運(yùn)行時(shí)在內(nèi)存里的,把數(shù)據(jù)定期要刷新到外設(shè),顯示器設(shè)備比較特殊,是給用戶來看的,從左到右,所以顯示器為了保證刷新效率,并且用戶體驗(yàn)良好,所以顯示器采用行緩沖,滿足用戶的閱讀體驗(yàn)并且在一定程度上效率不至于太低
- 緩沖區(qū)滿——全緩沖——磁盤文件,效率最高,只需要一次IO,比如文件讀寫的時(shí)候,直接寫到磁盤文件
總結(jié):
但是存在特殊情況:a.用戶強(qiáng)制刷新 b,進(jìn)程退出——一般到要進(jìn)行緩沖區(qū)刷新,所以對(duì)于全緩沖,緩沖區(qū)滿了采取刷新,減少IO次數(shù),提高效率。
???緩沖區(qū)在哪里呢
緩沖區(qū)的位置究竟在哪里???
從上面的例子我們直接往顯示器上打印結(jié)果為4條,往文件打印為7條,這跟緩沖區(qū)有關(guān),同時(shí)這也說明了緩沖區(qū)一定不在內(nèi)核中,為什么?如果在內(nèi)核中write也應(yīng)該打印兩次,write是系統(tǒng)接口。我們之前談?wù)摰乃芯彌_區(qū)都指的是用戶級(jí)語言層面提供的緩沖區(qū)。這個(gè)緩沖區(qū),在stdout,stdin,stderr對(duì)應(yīng)的類型---->FILE*,FILE是一個(gè)結(jié)構(gòu)體,里面封裝了fd,同時(shí)還包括了一個(gè)緩沖區(qū)!
理解FILE結(jié)構(gòu)體緩沖區(qū):
FILE結(jié)構(gòu)體緩沖區(qū),所以我們直接要強(qiáng)制刷新的時(shí)候fflush(文件指針),關(guān)閉文件fclose(文件指針),這是因?yàn)閭鬟M(jìn)去的文件指針對(duì)應(yīng)的緩沖區(qū)。
查看源碼來解釋FILE結(jié)構(gòu)體:
?
?
分析:
總結(jié):
- 所以我們一般所說的緩沖區(qū)是語言級(jí)別的緩沖區(qū),C語言提供的在FILE結(jié)構(gòu)體里對(duì)應(yīng)的緩沖區(qū)。
- 重定向?qū)е滤⑿虏呗园l(fā)生了改變(由行緩沖變成了全緩沖)。同時(shí)發(fā)生了寫時(shí)拷貝,父子進(jìn)程各自刷新
?? 引入緩沖器
概念分析:
高速設(shè)備與低速設(shè)備的不匹配(cpu運(yùn)算是納秒,內(nèi)存是微秒,磁盤是毫秒甚至是秒相差1000倍),勢(shì)必會(huì)讓高速設(shè)備花時(shí)間等待低速設(shè)備,我們可以在這兩者之間設(shè)立一個(gè)緩沖區(qū)。
緩沖區(qū)優(yōu)點(diǎn):
- 可以解除兩者的制約關(guān)系,數(shù)據(jù)可以直接送往緩沖區(qū),高速設(shè)備不用再等待低速設(shè)備,提高了計(jì)算機(jī)的效率
- 可以減少數(shù)據(jù)的讀寫次數(shù),如果每次數(shù)據(jù)只傳輸一點(diǎn)數(shù)據(jù),就需要傳送很多次,這樣會(huì)浪費(fèi)很多時(shí)間,因?yàn)殚_始讀寫與終止讀寫所需要的時(shí)間很長(zhǎng),如果將數(shù)據(jù)送往緩沖區(qū),待緩沖區(qū)滿后再進(jìn)行傳送會(huì)大大減少讀寫次數(shù),這樣就可以節(jié)省很多時(shí)間。例如:我們想將數(shù)據(jù)寫入到磁盤中,不是立馬將數(shù)據(jù)寫到磁盤中,而是先輸入緩沖區(qū)中,當(dāng)緩沖區(qū)滿了以后,再將數(shù)據(jù)寫入到磁盤中,這樣就可以減少磁盤的讀寫次數(shù),不然磁盤很容易壞掉
?? 緩沖區(qū)答疑
???問題一:代碼分析
問題拋出:
分析結(jié)果:
同樣的一個(gè)程序,向顯示器打印輸出4行文本,向普通文件(磁盤上)打印的時(shí)候,變成了7行,說明上面測(cè)試,并不影響系統(tǒng)接口
- C的IO接口是打印了2次的
- 系統(tǒng)接口,只打印了一次
我們最后調(diào)用fork,上面的函數(shù)已經(jīng)被執(zhí)行完了,但不代表數(shù)據(jù)已經(jīng)被刷新了。
???問題二:緩沖區(qū)是誰提供
曾經(jīng)“我們所談的緩沖區(qū)”,絕對(duì)不是由OS提供的,如果是OS同一提供,那么我們上面的代碼,表現(xiàn)應(yīng)該是一樣的,而不是C的IO接口打印兩次,所以是C標(biāo)準(zhǔn)庫提供并且維護(hù)的用戶級(jí)緩沖區(qū)
fputs把不是直接把數(shù)據(jù)直接放進(jìn)操作系統(tǒng),而是加載進(jìn)C標(biāo)準(zhǔn)庫的緩沖區(qū)中,加載完后自己可以直接返回;如果直接調(diào)用的是write接口,則是直接寫給OS,不經(jīng)過緩沖區(qū)
- C語言提供的接口都是向顯示器打印的,刷新策略都是行刷新,那么最后執(zhí)行fork的時(shí)候 —— 一定是函數(shù)執(zhí)行完了 && 數(shù)據(jù)已經(jīng)被刷新了(因?yàn)槎紟n),所以fork執(zhí)行無意義
- 如你對(duì)應(yīng)的程序進(jìn)行了重定向 ——> 要向磁盤文件打印 ——> 隱形的刷新策略變成了全緩沖!—— > \n便沒有意義了 ——> 函數(shù)一定執(zhí)行完了,數(shù)據(jù)還沒有刷新?。?在當(dāng)前進(jìn)程對(duì)應(yīng)的C標(biāo)準(zhǔn)庫中的緩沖區(qū)中?。?/li>
?? 設(shè)計(jì)用戶緩沖區(qū)
代碼如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#define NUM 1024
struct MyFILE_{
int fd; //文件描述符
char buffer[1024]; // 緩沖區(qū)
int end; //當(dāng)前緩沖區(qū)的結(jié)尾
};
typedef struct MyFILE_ MyFILE;//類型重命名
MyFILE *fopen_(const char *pathname, const char *mode)
{
assert(pathname);
assert(mode);
MyFILE *fp = NULL;//什么也沒做,最后返回NULL
if(strcmp(mode, "r") == 0)
{
}
else if(strcmp(mode, "r+") == 0)
{
}
else if(strcmp(mode, "w") == 0)
{
int fd = open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0666);
if(fd >= 0)
{
fp = (MyFILE*)malloc(sizeof(MyFILE));
memset(fp, 0, sizeof(MyFILE));
fp->fd = fd;
}
}
else if(strcmp(mode, "w+") == 0)
{
}
else if(strcmp(mode, "a") == 0)
{
}
else if(strcmp(mode, "a+") == 0)
{
}
else{
//什么都不做
}
return fp;
}
//是不是應(yīng)該是C標(biāo)準(zhǔn)庫中的實(shí)現(xiàn)!
void fputs_(const char *message, MyFILE *fp)
{
assert(message);
assert(fp);
strcpy(fp->buffer+fp->end, message); //abcde\0
fp->end += strlen(message);
//for debug
printf("%s\n", fp->buffer);
//暫時(shí)沒有刷新, 刷新策略是誰來執(zhí)行的呢?用戶通過執(zhí)行C標(biāo)準(zhǔn)庫中的代碼邏輯,來完成刷新動(dòng)作
//這里效率提高,體現(xiàn)在哪里呢??因?yàn)镃提供了緩沖區(qū),那么我們就通過策略,減少了IO的執(zhí)行次數(shù)(不是數(shù)據(jù)量)
if(fp->fd == 0)
{
//標(biāo)準(zhǔn)輸入
}
else if(fp->fd == 1)
{
//標(biāo)準(zhǔn)輸出
if(fp->buffer[fp->end-1] =='\n' )
{
//fprintf(stderr, "fflush: %s", fp->buffer); //2
write(fp->fd, fp->buffer, fp->end);
fp->end = 0;
}
}
else if(fp->fd == 2)
{
//標(biāo)準(zhǔn)錯(cuò)誤
}
else
{
//其他文件
}
}
void fflush_(MyFILE *fp)
{
assert(fp);
if(fp->end != 0)
{
//暫且認(rèn)為刷新了--其實(shí)是把數(shù)據(jù)寫到了內(nèi)核
write(fp->fd, fp->buffer, fp->end);
syncfs(fp->fd); //將數(shù)據(jù)寫入到磁盤
fp->end = 0;
}
}
void fclose_(MyFILE *fp)
{
assert(fp);
fflush_(fp);
close(fp->fd);
free(fp);
}
int main()
{
close(1);
MyFILE *fp = fopen_("./log.txt", "w");
if(fp == NULL)
{
printf("open file error");
return 1;
}
fputs_("one:hello world error", fp);
fputs_("two:hello world error", fp);
fputs_("three:hello world error", fp);
fputs_("four:hello world error", fp);
fclose(fp);
}
??結(jié)束語?
? ? ? ?今天內(nèi)容就到這里啦,時(shí)間過得很快,大家沉下心來好好學(xué)習(xí),會(huì)有一定的收獲的,大家多多堅(jiān)持,嘻嘻,成功路上注定孤獨(dú),因?yàn)閳?jiān)持的人不多。那請(qǐng)大家舉起自己的小手給博主一鍵三連,有你們的支持是我最大的動(dòng)力??????,回見。文章來源:http://www.zghlxwxcb.cn/news/detail-850362.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-850362.html
到了這里,關(guān)于【Linux】基礎(chǔ)IO----理解緩沖區(qū)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!