緩沖區(C語言緩沖區+內核緩沖區)一個例子解釋他們的關系和作用!!!

首先提出問題: 為什么以下代碼是先sleep三秒后,屏幕才顯示"XXXXXXX"。
#include<stdio.h>
#include<unistd.h>int main()
{printf("XXXXXXX");sleep(3);return 0;
}

為什么以下代碼是先顯示"XXXXXXX",屏幕才顯示"XXXXXXX"sleep三秒。

#include<stdio.h>
#include<unistd.h>int main()
{printf("XXXXXXX\n");sleep(3);return 0;
}

在我們學習C語言的時候經常聽說“某某字符串,某某變量在printf時是先進入緩沖區啦。。。”類似于這種,那時候沒人會去細說緩沖區,那么今天我們就來詳細說說緩沖區。

1.什么是緩沖區

舉個例子:

假設有兩個人,張三和李四。他們兩個是網友,有一天呢李四過生日,張三就準備把自己給他準備的禮物給愛他,但是張三和李四距離太遠,以前呢張三騎著自己的小單車就出發了,經過兩個月的長途跋涉終于送給了李四。張三兩個月沒上班,一想,虧死了。

后來,有了菜鳥驛站,張三需要給李四禮物就不用騎單車了,張三只需要把快遞交給樓下的菜鳥驛站,至于菜鳥驛站什么時候發,快遞什么時候到都不需要張三管,張三就可以繼續上班了,快遞到了先是放在李四樓下的菜鳥驛站,至于李四什么時候在家,菜鳥驛站再送。

至此,我們其實就可以大概猜得出,菜鳥驛站其實就是緩沖區的意思,而張三就是程序員,李四就是硬件。張三家樓下的菜鳥驛站就是C語言FILE結構體里面的一個長數組,他就是C語言下的緩沖區,我們的printf實際上就是先把數據寫到它里面去。(下面會詳細說FILE)

struct _IO_FILE {// ...其他字段...char* _IO_buf_base;    // 緩沖區起始位置char* _IO_buf_end;     // 緩沖區結束位置// ...其他字段...
};

李四樓下的緩沖區就是內核里面的緩沖區,在struct file里,struct?file里不止有文件的屬性和內容信息,還有緩沖區。?

struct file {// ...const struct file_operations *f_op;  // 文件操作函數指針struct address_space *f_mapping;     // 指向address_space結構// ...
};

其中,f_mapping字段指向一個struct address_space,這是文件與頁緩存之間的橋梁。?

緩沖區是內存空間的?部分。也就是說,在內存空間中預留了?定的存儲空間,這些存儲空間?來緩沖輸?或輸出的數據,這部分預留的空間就叫做緩沖區。緩沖區根據其對應的是輸?設備還是輸出設備,分為輸?緩沖區和輸出緩沖區。

?

2 .為什么要引?緩沖區機制

讀寫?件時,如果不會開辟對?件操作的緩沖區,直接通過系統調?對磁盤進?操作(讀、寫等),那么每次對?件進??次讀寫操作時,都需要使?讀寫系統調?來處理此操作,即需要執??次系統調?,執??次系統調?將涉及到CPU狀態的切換,即從??空間切換到內核空間,實現進程上下?的切換,這將損耗?定的CPU時間,頻繁的磁盤訪問對程序的執?效率造成很?的影響。
為了減少使?系統調?的次數,提?效率,我們就可以采?緩沖機制。?如我們從磁盤?取信息,可 以在磁盤?件進?操作時,可以?次從?件中讀出?量的數據到緩沖區中,以后對這部分的訪問就不 需要再使?系統調?了,等緩沖區的數據取完后再去磁盤中讀取,這樣就可以減少磁盤的讀寫次數,再加上計算機對緩沖區的操作? 快于對磁盤的操作,故應?緩沖區可? 提?計算機的運?速度。
??如,我們使?打印機打印?檔,由于打印機的打印速度相對較慢,我們先把?檔輸出到打印機相應的緩沖區,打印機再??逐步打印,這時我們的CPU可以處理別的事情。可以看出,緩沖區就是?塊內存區,它?在輸?輸出設備和CPU之間,?來緩存數據。它使得低速的輸?輸出設備和?速的CPU能夠協調?作,避免低速的輸?輸出設備占?CPU,解放出CPU,使其能夠?效率?作

3.緩沖類型

標準I/O提供了3種類型的緩沖區:
緩沖區:這種緩沖?式要求填滿整個緩沖區后才進?I/O系統調?操作。對于磁盤?件的操作通常使?全緩沖的?式訪問。
?緩沖區:在?緩沖情況下,當在輸?和輸出中遇到換?符時,標準I/O庫函數將會執?系統調?操作。當所操作的流涉及?個終端時(例如標準輸?和標準輸出),使??緩沖?式。因為標準I/O庫每?的緩沖區?度是固定的,所以只要填滿了緩沖區,即使還沒有遇到換?符,也會執?I/O系統調?操作,默認?緩沖區的??為1024。
?緩沖區:?緩沖區是指標準I/O庫不對字符進?緩存,直接調?系統調?。標準出錯流stderr通常是不帶緩沖區的,這使得出錯信息能夠盡快地顯?出來。
除了上述列舉的默認刷新?式,下列特殊情況也會引發緩沖區的刷新:
1.? 緩沖區滿時;
2.? 執?flush語句;
?例如下:
include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {close(1);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 0;}printf("hello world: %d\n", fd);close(fd);return 0;
}

這里相當于把stdout用log.txt替換

我們本來想使?重定向思維,讓本應該打印在顯?器上的內容寫到“log.txt”?件中,但我們發現,
程序運?結束后,?件中并沒有被寫?內容:
[ljh@VM-8-12-centos buffer]$ ./myfile
[ljh@VM-8-12-centos buffer]$ ls
log.txt makefile myfile myfile.c
[ljh@VM-8-12-centos buffer]$ cat log.txt
[ljh@VM-8-12-centos buffer]$
這是由于我們將1號描述符重定向到磁盤?件后, 緩沖區的刷新?式成為了全緩沖 。?我們寫?的內容并 沒有填滿整個緩沖區 ,導致并不會將緩沖區的內容刷新到磁盤?件中。怎么辦呢?
可以使?fflush強制刷新下緩沖區。

當調用printf()時,數據被寫入stdout對應的FILE結構體管理的緩沖區,這個緩沖區完全在用戶空間,由 C 標準庫 (libc) 維護默認情況下,重定向到文件的流是全緩沖的,緩沖區大小通常為 4KB 或 8KB。

只有當 C 庫緩沖區刷新時 (通過fflush()或緩沖區滿),才會調用write()系統調用,write()將數據從用戶空間復制到內核空間的頁緩存 (Page Cache),頁緩存由內核管理,位于物理內存中

include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {close(1);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 0;}printf("hello world: %d\n", fd);fflush(stdout);close(fd);return 0;
}
有?種解決?法,剛好可以驗證?下stderr是不帶緩沖區的,代碼如下:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {close(2);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 0;}perror("hello world");close(fd);return 0;
}
這種?式便可以將2號?件描述符重定向??件,由于stderr沒有緩沖區,“hello world”不?fflash
就可以寫??件。

4.FILE

上面我們提到了FILE,這里我們詳細來聊聊。

4.1 FILE結構體的本質與作用

FILE是 C 標準庫中定義的結構體類型,用于封裝文件操作的相關信息,是用戶空間與系統調用之間的橋梁。它本質上是對文件描述符(fd)的一層封裝,同時管理 I/O 緩沖區,以提升 I/O 操作效率。

4.2 FILE結構體的核心字段

?在 GNU C 庫(glibc)中,FILE結構體的關鍵字段如下:

struct _IO_FILE {int _fileno;              // 封裝的文件描述符(fd)char* _IO_buf_base;       // 緩沖區起始地址char* _IO_buf_end;        // 緩沖區結束地址char* _IO_read_ptr;       // 讀緩沖區當前位置char* _IO_write_ptr;      // 寫緩沖區當前位置int _flags;               // 文件狀態標志(如讀寫模式、是否關閉等)const struct _IO_jump_t* _vtable;  // 函數指針表,指向I/O操作函數// 其他字段(省略)
};
  1. _fileno
    直接關聯系統調用的文件描述符,是FILE與內核交互的橋梁。例如,printf最終會通過_fileno對應的 fd 調用write系統調用。

  2. 緩沖區相關指針

    • _IO_buf_base_IO_buf_end:標記緩沖區的物理范圍。
    • _IO_read_ptr_IO_write_ptr:記錄當前讀寫位置,用于控制數據在緩沖區中的流動。
  3. _vtable
    指向函數表,包含一系列 I/O 操作的實現(如讀、寫、刷新緩沖區等),體現了面向對象的設計思想。

4.3?FILE與系統調用的關系

用戶空間視角:通過FILE*指針調用printffscanf等庫函數,數據先存入FILE的緩沖區。

內核空間視角:當緩沖區刷新時(如調用fflush、緩沖區滿或程序結束),庫函數通過_fileno調用writeread等系統調用,將數據傳入內核緩沖區(頁緩存)

4.4 為什么需要FILE結構體?

  1. 跨平臺兼容性:封裝不同系統的文件操作細節(如 Windows 和 Linux 的文件描述符機制差異)。
  2. 緩沖區優化:通過用戶空間緩沖區減少系統調用次數,提升 I/O 效率。
  3. 高層抽象:提供更易用的接口(如printf的格式化輸出),隱藏底層系統調用的復雜性

FILE結構體是 C 語言 I/O 體系的核心,它通過封裝文件描述符和管理用戶空間緩沖區,在高效性和易用性之間取得平衡。理解FILE的內部結構,有助于深入掌握 C 語言 I/O 操作的本質,以及緩沖區刷新、重定向等關鍵機制。

5.一個關鍵問題

為了讓我們更好的理解上面的內容,我們來看以下代碼:

#include <stdio.h>
#include <string.h>
#include <unistd.h> 
int main()
{const char *msg0="hello printf\n";const char *msg1="hello fwrite\n";const char *msg2="hello write\n";printf("%s", msg0);fwrite(msg1, strlen(msg1), 1, stdout);write(1, msg2, strlen(msg2));fork();return 0;
}

這個代碼看起來簡單,卻是檢測是否理解緩沖區的關鍵!!!

問題:

為什么運行出結果:

而重定向時:

為什么結果不一樣呢???

其實關鍵的原因就在于,顯示器文件和該文本文件的緩沖區刷新方式不同。

顯示器是行刷新,而文本是滿刷新。

所以在顯示器上,一行一行刷新,到子進程時緩沖區也沒東西了。

在重定向時,開始數據進入緩沖區,但是一直沒刷新,write是系統調用,直接輸入到內核緩沖區了所以先打印,到了fork時,子進程繼承了父進程緩沖區的東西,文件退出時,自動ffulsh,所以就得到了我們看見的現象。

printf fwrite (庫函數)都輸出了2次,? write 只輸出了?次(系統調?)。為
什么呢?肯定和fork有關!
  • 一般 C 庫函數寫入文件時是全緩沖的,而寫入顯示器是行緩沖。
  • printf fwrite 庫函數 + 會自帶緩沖區(進度條例子就可以說明),當發生重定向到普通文件時,數據的緩沖方式由行緩沖變成了全緩沖。
  • 而我們放在緩沖區中的數據,就不會被立即刷新,甚至 fork 之后
  • 但是進程退出之后,會統一刷新,寫入文件當中。
  • 但是 fork 的時候,父子數據會發生寫時拷貝,所以當你父進程準備刷新的時候,子進程也就有了同樣的一份數據,隨即產生兩份數據。
  • write 沒有變化,說明沒有所謂的緩沖區。
綜上: printf fwrite 庫函數會?帶緩沖區,? write 系統調?沒有帶緩沖區。另外,我們這?所說的緩沖區,都是??級緩沖區。其實為了提升整機性能,OS也會提供相關內核級緩沖區,不過不再我們討論范圍之內。
那這個緩沖區誰提供呢? printf fwrite 是庫函數, write 是系統調?,庫函數在系統調?的
“上層”, 是對系統調?的“封裝”,但是 write 沒有緩沖區,? printf fwrite 有,?以說
明,該緩沖區是?次加上的,?因為是C,所以由C標準庫提供。

6.簡單設計一下libc庫

結合上面的內容我們可以試著寫出libc的mini版:
my_stdio.h
#pragma once#define SIZE 1024#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2struct IO_FILE
{int flag; // 刷新方式int fileno; // 文件描述符char outbuffer[SIZE];int cap;int size;// TODO
};typedef struct IO_FILE mFILE;mFILE *mfopen(const char *filename, const char *mode);
int mwrite(const void *ptr, int num, mFILE *stream);
void mfflush(mFILE *stream);
void mfclose(mFILE *stream);

my_stdio.c

#include "my_stdio.h"
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>mFILE *mfopen(const char *filename, const char *mode)
{int fd = -1;if(strcmp(mode, "r") == 0){fd = open(filename, O_RDONLY);}else if(strcmp(mode, "w")== 0){fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);}else if(strcmp(mode, "a") == 0){fd = open(filename, O_CREAT|O_WRONLY|O_APPEND, 0666);}if(fd < 0) return NULL;mFILE *mf = (mFILE*)malloc(sizeof(mFILE));if(!mf){close(fd);return NULL;}mf->fileno = fd;mf->flag = FLUSH_LINE;mf->size = 0;mf->cap = SIZE;return mf;
}void mfflush(mFILE *stream)
{if(stream->size > 0){// 寫到內核文件的文件緩沖區中write(stream->fileno, stream->outbuffer, stream->size);// 刷新到外設fsync(stream->fileno);stream->size = 0;}
}int mfwrite(const void *ptr, int num, mFILE *stream)
{// 1. 拷貝memcpy(stream->outbuffer + stream->size, ptr, num);stream->size += num;// 2. 檢測是否要刷新if(stream->flag == FLUSH_LINE && stream->size > 0 && stream->outbuffer[stream->size - 1] == '\n'){mfflush(stream);}return num;
}void mfclose(mFILE *stream)
{if(stream->size > 0){mfflush(stream);}close(stream->fileno);
}

main.c

#include "my_stdio.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{mFILE *fp = mfopen("./log.txt", "a");if(fp == NULL){return 1;}int cnt = 10;while(cnt){printf("write %d\n", cnt);char buffer[64];snprintf(buffer, sizeof(buffer),"hello message, number is : %d", cnt);cnt--;mfwrite(buffer, strlen(buffer), fp);mfflush(fp);sleep(1);}mfclose(fp);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83355.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83355.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83355.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【2025版】Java 工程師學習路線圖 —— 掌握程度描述版

?【2025版】Java 工程師學習路線圖 &#x1f4a1; 目標&#xff1a;成為合格的 Java 工程師&#xff08;前后端都要會&#xff09; &#x1f4dd; 結構清晰 | 階段明確 | 掌握程度分級 | 適合自學或轉行 &#x1f539; 階段一&#xff1a;編程基礎 計算機通識 模塊內容推薦掌…

從零實現一個紅隊智能體

從零實現一個紅隊智能體(持續更新) 2025-06-09 背景&#xff1a;最近學了基礎些東西和工具基礎使用&#xff0c;發現一套流程下來太多需要手工要做的&#xff0c;就像自己能不能結合自己的技術棧實現小工具 &#x1f947; 第一步&#xff1a;從實用性開始分析 目標場景 希望…

Uniapp實現多選下拉框

文章目錄 前言一、效果展示1.1 下拉效果圖1.2 下拉選擇效果圖1.3 選擇顯示效果圖 二、組件源碼2.1.CustomCheckbox.vue源碼2.2.niceui-popup-select.vue源碼 三、demo.vue代碼演示 前言 之前在使用Uniapp時&#xff0c;一直都是下拉框單選。今天某個項目需求需要使用Uniapp實現…

JavaScript-Array.from

Array.from() 是 JavaScript 中用于將類數組對象&#xff08;array-like&#xff09;或可迭代對象&#xff08;iterable&#xff09;轉換為真實數組的一個非常有用的方法。 &#x1f4cc; 一、基本語法 Array.from(arrayLike, mapFn?, thisArg?)參數說明&#xff1a; 參數類…

二刷蒼穹外賣 day02

新增員工 DTO 將前端傳遞的參數列表通過對應的實體類接收 當前端提交的數據和實體類中對應的屬性差別較大時&#xff0c;使用DTO來封裝數據 Data public class EmployeeDTO implements Serializable {private Long id;private String username;private String name;private…

通過Heron Handoff 插件我們在figma設計中可以像sketch導出離線標注

一、設計交付的歷史困境與破局契機 在數字產品開發的全流程中&#xff0c;設計標注的高效傳遞始終是連接創意與實現的關鍵紐帶。傳統設計工具如 Sketch 憑借 Bluebeam、Sketch Measure 等插件構建了成熟的離線標注體系&#xff0c;設計師可將標注文件打包交付&#xff0c;開發…

SSE 數據的傳輸無法流式獲取

問題 調試過程中發現SSE數據返回的時間都是一樣的&#xff0c;懷疑是接口問題。 參考 EventSource數據一次性出來&#xff0c;并未流式輸出的原因_sourceevent為什么結果一下全部返回了-CSDN博客 處理 EventStream 不能流式返回的問題&#xff1a;Nginx 配置優化 解決方案 …

markdown文本轉換時序圖

好久沒更新了~這篇是markdown文本轉換時序圖的常用方法 文章目錄 前言一、Mermaid語法示例二、PlantUML語法示例三、在線工具快速轉換總結 前言 使用專業工具如Mermaid或PlantUML可以直接在Markdown中繪制時序圖。這些工具支持簡潔的語法&#xff0c;生成可嵌入文檔的圖表&…

谷粒商城-分布式微服務 -集群部署篇[一]

十九、k8s 集群部署 19.1 k8s 快速入門 19.1.1 簡介 Kubernetes 簡稱 k8s。是用于自動部署&#xff0c;擴展和管理容器化應用程序的開源系統。 中文官網 中文社區 官方文檔 社區文檔 概述 | Kubernetes 傳統部署時代&#xff1a; 早期&#xff0c;各個組織是在物理服務器上…

微信小程序- 用canvas生成排行榜

設計功能不是很復雜&#xff0c;也不想用插件&#xff0c;最終出現現在版本&#xff0c;主要用到微信小程序 wx.canvasToTempFilePath方法 // 直接調用改方法 createQRCode() {const qrCodeCanvasId "qrcodeCanvas";drawQrcode({width: 200,height: 200,canvasId: …

深度剖析:UI 設計怎樣為小程序構建極致輕量體驗

內容摘要 在小程序的世界里&#xff0c;用戶都追求快速、便捷的輕量體驗。但你是否好奇&#xff0c;為啥有些小程序能讓人輕松上手&#xff0c;快速達成目標&#xff0c;而有些卻讓人感覺繁瑣、卡頓&#xff1f;這里的關鍵差異&#xff0c;往往就藏在 UI 設計中。UI 設計到底施…

【網絡安全】Qt免殺樣本分析

初步研判 SHA256&#xff1a;9090807bfc569bc8dd42941841e296745e8eb18b208942b3c826b42b97ea67ff 我們可以看到引擎0檢出&#xff0c;是個免殺樣本&#xff0c;不過通過微步云沙箱的行為分析&#xff0c;已經被判為惡意 行為分析 進程行為 可以看到demo顯示調用了winver獲…

window 顯示驅動開發-如何查詢視頻處理功能(六)

D3DDDICAPS_FILTERPROPERTYRANGE請求類型 UMD 返回指向 DXVADDI_VALUERANGE 結構的指針&#xff0c;該結構包含傳遞D3DDDICAPS_FILTERPROPERTYRANGE請求類型時特定視頻流上特定篩選器設置允許的值范圍。 Direct3D 運行時在D3DDDIARG_GETCAPS的 pInfo 成員指向的變量中為特定視…

Oracle線上故障問題解決

----重啟電腦找不到sid Listener refused the connection with the following error: ORA-12505, TNS:listener does not currently know of SID given in connect descriptor Could not open connection sqlplus "/as sysdba" SQL> shutdown immediate 數據庫…

語音信號處理三十——高效多相抽取器(Polyphase+Noble)

文章目錄 前言一、Polyphase 多項分解1.定義2.拆分公式3.推導過程1&#xff09;按模 M M M拆分求和項2&#xff09;提取因子 4.總結 二、Noble恒等式1. 定義2.Noble恒等式表達方式1&#xff09;抽取系統的 Noble 恒等式2&#xff09;插值系統的 Noble 恒等式 2.Nodble恒等式推導…

廣告推薦系統中模型訓練中模型的結構信息、Dense數據、Sparse數據

下面結合廣告推薦系統常見的深度學習模型(比如 Wide & Deep、DeepFM、Two-Tower 等),介紹一下“模型的結構信息”、Dense 數據和 Sparse 數據在訓練過程中的角色及處理方式。 模型結構信息 輸入層(Input Layer) ? Sparse 輸入:各類離散高維特征(用戶 ID、廣告 ID、…

安全生產管理是什么?安全生產管理主要管什么?

安全生產管理是什么&#xff1f;安全生產管理主要管什么&#xff1f; 不管是制造業、建筑業&#xff0c;還是倉儲、物流、化工等等&#xff0c;一聊到“安全事故”&#xff0c;大家腦子里最先冒出來的兩個詞&#xff0c;肯定就是&#xff1a; 人的不安全行為物的不安全狀態 …

SecureRandom.getInstanceStrong() 與虛擬機的愛恨情仇

問題描述 使用Ruoyi-cloud 二開&#xff0c;將服務部署到虛擬機上后&#xff0c;準備登錄&#xff0c;發現驗證碼一致加載不出來&#xff0c;接口請求超時! 解決步驟 telnet 虛擬機ipport 發現可以通.curl 接口&#xff0c;發現一致不返回&#xff0c;超時了./code 接口超時&am…

DEM 地形分析與水文建模:基于 ArcGIS 的流域特征提取

技術點目錄 一、 GIS理論及ArcGIS認識二、ArcGIS數據管理與轉換三、ArcGIS地圖制作與發布四、ArcGIS數據制備與編輯五、ArcGIS矢量空間分析及應用六、ArcGIS柵格空間分析及應用七、ArcGIS空間插值及應用八、DEM數據與GIS三維分析九、ArcGIS高級建模及應用十、綜合講解了解更多 …

芯伯樂XBLW GT712選型及應用設計指南

前言 在電子工程領域&#xff0c;精準的電流測量對于眾多電路設計與系統監控至關重要。芯伯樂推出的XBLW GT712電流傳感器以其獨特的優勢&#xff0c;成為工程師在諸多應用中的首選工具。本文將深入剖析XBLW GT712的工作原理、性能特點以及應用要點&#xff0c;為工程師提供詳…