重定向與文件緩沖機制

目錄

一、重定向的原理與實踐

1. 輸出重定向:讓數據流向新目的地

2. 追加重定向:在文件末尾追加數據

3. 輸入重定向:從指定文件讀取數據

4. 標準輸出流與標準錯誤流的區別

5. 使用?dup2?實現重定向

二、FILE 結構體的奧秘

1. FILE 中的文件描述符

2. FILE 中的緩沖區

3. 緩沖區的提供者與位置

4. 操作系統的緩沖區

5.?緩沖體系的層級架構

?編輯


一、重定向的原理與實踐

1. 輸出重定向:讓數據流向新目的地

原理

????????輸出重定向的本質是修改文件描述符下標對應的 struct file* 內容,將原本應該輸出到一個文件(通常是顯示器)的數據,改為輸出到另一個文件。

代碼示例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {// 關閉標準輸出流(文件描述符 1)close(1);// 打開 log.txt 文件,獲取文件描述符int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0) {perror("open");return 1;}// 使用 printf 輸出數據printf("hello world\n");printf("hello world\n");printf("hello world\n");printf("hello world\n");printf("hello world\n");// 刷新緩沖區,確保數據寫入文件fflush(stdout);// 關閉文件描述符close(fd);return 0;
}

運行結果 :運行程序后,顯示器上沒有輸出數據,而 log.txt 文件中寫入了多行 "hello world"。

說明printf 函數默認向標準輸出流(stdout)輸出數據,而 stdout 指向的 FILE 結構體中存儲的文件描述符是 1。通過關閉文件描述符 1 并重新打開文件,我們實現了輸出重定向。

2. 追加重定向:在文件末尾追加數據

原理

????????追加重定向與輸出重定向類似,區別在于數據是追加到目標文件末尾,而不是覆蓋原有內容。通過O_APPEND標志,每次寫入都會自動定位到文件末尾,保留原有內容。

代碼示例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {// 關閉標準輸出流(文件描述符 1)close(1);// 以追加方式打開 log.txt 文件int fd = open("log.txt", O_WRONLY | O_APPEND | O_CREAT, 0666);if (fd < 0) {perror("open");return 1;}// 使用 printf 輸出數據printf("hello Linux\n");printf("hello Linux\n");printf("hello Linux\n");printf("hello Linux\n");printf("hello Linux\n");// 刷新緩沖區,確保數據寫入文件fflush(stdout);// 關閉文件描述符close(fd);return 0;
}

運行結果 :運行程序后,log.txt 文件中新增了多行 "hello Linux",且這些內容追加在原有內容之后。

3. 輸入重定向:從指定文件讀取數據

原理

????????輸入重定向是將原本從標準輸入流(通常是鍵盤)讀取數據的操作,改為從指定文件讀取數據。

代碼示例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {// 關閉標準輸入流(文件描述符 0)close(0);// 打開 log.txt 文件,獲取文件描述符int fd = open("log.txt", O_RDONLY | O_CREAT, 0666);if (fd < 0) {perror("open");return 1;}// 定義字符數組,用于存儲讀取的數據char str[40];// 使用 scanf 從文件讀取數據while (scanf("%s", str) != EOF) {printf("%s\n", str);}// 關閉文件描述符close(fd);return 0;
}

運行結果 :運行程序后,scanf 函數從 log.txt 文件中讀取數據,并通過 printf 輸出到顯示器。

說明scanf 函數默認從標準輸入流(stdin)讀取數據,而 stdin 指向的 FILE 結構體中存儲的文件描述符是 0。通過關閉文件描述符 0 并重新打開文件,我們實現了輸入重定向。

4. 標準輸出流與標準錯誤流的區別

原理

????????標準輸出流(stdout,文件描述符 1)和標準錯誤流(stderr,文件描述符 2)都默認輸出到顯示器,但它們在重定向時的行為不同。重定向操作只影響標準輸出流,而標準錯誤流不受影響。

操作stdout表現stderr表現
直接運行屏幕輸出屏幕輸出
重定向到文件寫入文件仍顯示屏幕

代碼示例

#include <stdio.h>int main() {// 向標準輸出流輸出數據printf("hello printf\n");// 向標準錯誤流輸出數據perror("perror");// 使用 fprintf 向標準輸出流和標準錯誤流輸出數據fprintf(stdout, "stdout:hello fprintf\n");fprintf(stderr, "stderr:hello fprintf\n");return 0;
}

運行結果 :直接運行程序時,顯示器上輸出四行字符串。若將程序運行結果重定向到文件 log.txt,則 log.txt 中只有標準輸出流的兩行字符串,而標準錯誤流的兩行數據仍輸出到顯示器。

5. 使用?dup2?實現重定向

原理

dup2 函數用于將一個文件描述符的內容拷貝到另一個文件描述符,從而實現重定向。

函數原型

int dup2(int oldfd, int newfd);

代碼示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>int main() {// 打開 log.txt 文件,獲取文件描述符int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0) {perror("open");return 1;}// 關閉標準輸出流(文件描述符 1)close(1);// 使用 dup2 將 fd 拷貝到文件描述符 1// 將log.txt克隆到標準輸出dup2(fd, 1);// 使用 printf 輸出數據// 自動寫入log.txtprintf("hello printf\n");fprintf(stdout, "hello fprintf\n");// 關閉文件描述符close(fd);return 0;
}

運行結果 :運行程序后,log.txt 文件中寫入了 "hello printf" 和 "hello fprintf"。

二、FILE 結構體的奧秘

1. FILE 中的文件描述符

原理

????????C 語言的庫函數是對系統調用接口的封裝,文件操作本質上是通過文件描述符進行的。FILE 結構體內部封裝了文件描述符。

/usr/include/stdio.h 頭文件中,FILEstruct _IO_FILE 的別名。

typedef struct _IO_FILE FILE;

/usr/include/libio.h 頭文件中,struct _IO_FILE 定義了 _fileno 成員,用于存儲文件描述符。

struct _IO_FILE {int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//緩沖區相關/* 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 0int _blksize;
#elseint _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
};

示例代碼

#include <stdio.h>int main() {FILE *fp = fopen("test.txt", "w");if (fp == NULL) {perror("fopen");return 1;}// 獲取 FILE 結構體中的文件描述符int fd = fileno(fp);// 使用文件描述符進行操作// ...// 關閉文件fclose(fp);return 0;
}

說明fopen 函數在上層為用戶申請 FILE 結構體變量,并返回該結構體的地址。在底層通過系統接口 open 打開文件,得到文件描述符,并將其存儲在 FILE 結構體的 _fileno 成員中。

2. FILE 中的緩沖區

原理

????????C 語言中的文件操作函數(如 printffputs 等)使用緩沖區來提高效率。緩沖區的類型有無緩沖、行緩沖和全緩沖。

緩沖類型對比表

緩沖類型刷新條件典型應用場景
無緩沖立即寫入標準錯誤
行緩沖遇換行符或緩沖區滿終端輸出
全緩沖緩沖區滿或強制刷新文件操作

代碼示例

#include <stdio.h>
#include <unistd.h>int main() {// 使用 printf 輸出數據printf("hello printf\n");// 使用 fputs 輸出數據fputs("hello fputs\n", stdout);// 使用 write 系統接口輸出數據write(1, "hello write\n", 12);// 創建子進程fork();return 0;
}

運行結果 :直接運行程序時,數據輸出到顯示器。若將程序運行結果重定向到文件 log.txt,則 log.txtprintffputs 的數據出現兩次,而 write 的數據只出現一次。

說明printffputs 使用緩沖區,當程序運行結果重定向到文件時,緩沖區的數據會被復制到父進程和子進程中,導致數據重復。而 write 是系統接口,沒有緩沖區,數據直接輸出到目標文件。

3. 緩沖區的提供者與位置

原理

????????緩沖區由 C 語言提供,在 FILE 結構體中維護。FILE 結構體中包含多個成員,用于記錄緩沖區的相關信息。

/usr/include/libio.h 頭文件中,struct _IO_FILE 定義了多個與緩沖區相關的成員,如 _IO_read_ptr_IO_read_end 等。

//緩沖區相關
/* 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. */

示例代碼

#include <stdio.h>int main() {FILE *fp = fopen("test.txt", "w");if (fp == NULL) {perror("fopen");return 1;}// 使用 fputs 寫入數據fputs("hello fputs", fp);// 刷新緩沖區fflush(fp);// 關閉文件fclose(fp);return 0;
}

說明fputs 函數將數據寫入 FILE 結構體中的緩沖區,fflush 函數用于刷新緩沖區,將數據寫入文件。

4. 操作系統的緩沖區

原理

????????操作系統也有自己的緩沖區。當用戶緩沖區的數據被刷新到操作系統緩沖區后,操作系統會根據自己的刷新機制,將數據寫入磁盤或顯示器。

示例代碼

#include <stdio.h>
#include <unistd.h>int main() {// 使用 printf 輸出數據printf("hello printf\n");// 刷新用戶緩沖區fflush(stdout);// 暫停 2 秒sleep(2);return 0;
}

說明printf 輸出的數據先存儲在用戶緩沖區,fflush 刷新用戶緩沖區后,數據進入操作系統緩沖區。操作系統會根據自己的刷新機制,將數據寫入顯示器。

5.?緩沖體系的層級架構

數據流動路徑:

用戶程序 → C庫緩沖區(用戶空間)→ 內核緩沖區(內核空間)→ 物理存儲設備

關鍵特點

  • 用戶緩沖區由C庫維護

  • 內核緩沖區由OS管理

  • fflush只刷新用戶→內核階段

  • 最終落盤由內核決定

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

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

相關文章

DeepSeek 沖擊(含本地化部署實踐)

DeepSeek無疑是春節檔最火爆的話題&#xff0c;上線不足一月&#xff0c;其全球累計下載量已達4000萬&#xff0c;反超ChatGPT成為全球增長最快的AI應用&#xff0c;并且完全開源。那么究竟DeepSeek有什么魔力&#xff0c;能夠讓大家趨之若鶩&#xff0c;他又將怎樣改變世界AI格…

顯微鏡下的人體結構

顯微鏡下的人體結構&#xff0c;看完以后&#xff0c;你還覺得人類是進化而來的嗎&#xff1f;...... 第一張&#xff1a;電子顯微鏡所觀察到的人類血管&#xff0c;可以非常清楚的看到里面的白細胞和紅細胞 第二張&#xff1a;正在分泌耳垢&#xff08;耳屎&#xff09;的耳道…

DApp 開發入門指南

DApp 開發入門指南 &#x1f528; 1. DApp 基礎概念 1.1 什么是 DApp&#xff1f; 去中心化應用&#xff08;DApp&#xff09;是基于區塊鏈的應用程序&#xff0c;特點是&#xff1a; 后端運行在區塊鏈網絡前端可以是任何框架使用智能合約處理業務邏輯數據存儲在區塊鏈上 1…

鴻蒙狀態管理概述 v2

狀態管理v2 概述狀態管理之v2ObservedV2 和 Trace狀態管理V1版本對嵌套類對象屬性變化直接觀測的局限性ObservedV2 和 Trace 使用場景 Local狀態管理V1版本State裝飾器的局限性 Param狀態管理V1版本接受外部傳入的裝飾器的局限性 OnceEventComputedComputed 使用場景 TypePersi…

Git中revert和reset區別?

git revert 和 git reset 都用于撤銷 Git 中的提交&#xff0c;但它們的作用和使用場景不同&#xff1a; git revert: 作用&#xff1a;創建一個新的提交&#xff0c;撤銷指定的提交內容。使用場景&#xff1a;用于“回滾”已推送到遠程倉庫的提交。這種方法不會改變提交歷史&a…

LabVIEW開發中的電機控制與相機像素差

在電機控制系統中&#xff0c;我們需要精確控制電機運動與相機拍攝畫面之間的關系。理想情況下&#xff0c;當電機帶動相機移動同樣的距離時&#xff0c;相機拍攝畫面中兩點之間的像素差應當是一個固定值。然而&#xff0c;在實際應用中&#xff0c;我們發現這一像素差并非固定…

從零到一:構建現代 React 應用的完整指南

1. create-react-app (CRA) 簡介: create-react-app 是官方推薦的 React 項目腳手架工具,提供了一個開箱即用的開發環境,幫助開發者快速啟動 React 應用。它會自動配置 Webpack、Babel、ESLint 等工具,讓你專注于開發而不需要手動配置工具鏈。 特點: 零配置:CRA 自動配…

rman 備份恢復1

前提&#xff1a; rman用戶必須具有sysdba權限 使用常用連接方式如下&#xff1a; rman target / rman target sys/oracle rman target sys/oracleprod1 catalog dav/oracledav_db 一個rman連接會產生兩個進程&#xff0c;action字段為空的就是rman的監控進程&#xff0c;另…

Qt程序退出相關資源釋放問題

目錄 問題背景&#xff1a; aboutToQuit 代碼舉例 closeEvent事件 代碼舉例 程序退出方式 quit() exit(int returnCode 0) close() 問題背景&#xff1a; 實際項目中程序退出前往往需要及進行一些資源釋放、配置保存、線程中斷等操作&#xff0c;避免資源浪費&#xff…

【DeepSeek】Mac m1電腦部署DeepSeek

一、電腦配置 個人電腦配置 二、安裝ollama 簡介&#xff1a;Ollama 是一個強大的開源框架&#xff0c;是一個為本地運行大型語言模型而設計的工具&#xff0c;它幫助用戶快速在本地運行大模型&#xff0c;通過簡單的安裝指令&#xff0c;可以讓用戶執行一條命令就在本地運…

[生活雜項][運動教程]自由泳

https://v.youku.com/v_show/id_XMzgzMjkwMzg0MA.html?spma2h0k.11417342.soresults.dtitle https://v.youku.com/v_show/id_XMzgxNjM2NjY4NA.html?spma2h0k.11417342.soresults.dtitle

Linux的指令與熱鍵

一.指令 1.pwd :顯示一個用戶當前所處的目錄 2.ls :顯示當前目錄下的文件&#xff08;顯示當前文件屬性&#xff09; ls -l :顯示當前目錄下文件的屬性及更多內容&#xff08;ll是ls -l的別名&#xff0c;用法相同&#xff09; ls -l 目錄&#xff1a;顯示指定目錄內容 ls…

【Dubbo+Zookeeper】——SpringBoot+Dubbo+Zookeeper知識整合

&#x1f3bc;個人主頁&#xff1a;【Y小夜】 &#x1f60e;作者簡介&#xff1a;一位雙非學校的大二學生&#xff0c;編程愛好者&#xff0c; 專注于基礎和實戰分享&#xff0c;歡迎私信咨詢&#xff01; &#x1f386;入門專欄&#xff1a;&#x1f387;【MySQL&#xff0…

【OS安裝與使用】part5-ubuntu22.04基于conda安裝pytorch+tensorflow

文章目錄 一、待解決問題1.1 問題描述1.2 解決方法 二、方法詳述2.1 必要說明2.2 應用步驟2.2.1 明確pytorch安裝依賴2.2.2 conda創建虛擬環境2.2.3 安裝pytorch2.2.4 驗證pytorch安裝2.2.5 安裝Tensorflow2.2.6 驗證Tensorflow安裝 三、疑問四、總結 一、待解決問題 1.1 問題…

馬拉車算法

Manacher算法 ,用于處理最長回文字符串的問題&#xff0c;可以在O&#xff08;n&#xff09;的情況下&#xff0c;求出一個字符串的最長回文字符串 回文串的基礎解法&#xff1a; 以每個點為中心對稱點&#xff0c;看左右兩邊的點是否相同。這種算法的時間復雜度為O&#xff0…

氣象學中的CDO插值(多方法+多分辨率)

文章目錄 說明CDO代碼 說明 需要新建.sh腳本文件&#xff0c;將下面的CDO代碼復制到.sh腳本中&#xff0c;然后運行插值程序。 CDO代碼 #!/bin/bash # # 用戶配置區&#xff08;按實際需求修改&#xff09; # input_directory"2m_temperature" # 自定義路徑 gr…

計算機網絡:應用層 —— 動態主機配置協議 DHCP

文章目錄 什么是 DHCP&#xff1f;DHCP 的產生背景DHCP 的工作過程工作流程地址分配機制 DHCP 中繼代理總結 什么是 DHCP&#xff1f; 動態主機配置協議&#xff08;DHCP&#xff0c;Dynamic Host Configuration Protocol&#xff09;是一種網絡管理協議&#xff0c;用于自動分…

【OS安裝與使用】part3-ubuntu安裝Nvidia顯卡驅動+CUDA 12.4

文章目錄 一、待解決問題1.1 問題描述1.2 解決方法 二、方法詳述2.1 必要說明2.2 應用步驟2.2.1 更改鏡像源2.2.2 安裝NVIDIA顯卡驅動&#xff1a;nvidia-550&#xff08;1&#xff09;查詢顯卡ID&#xff08;2&#xff09;PCI ID Repository查詢顯卡型號&#xff08;3&#xf…

數據導入AI訓練步驟——人工智能訓練

一、人工操作轉化 數據導入過程 整理excel表格&#xff0c;通過數據庫管理工具導入數據&#xff0c;補充數據格式&#xff0c;調整sql語句 復制數據到目標數據 二、整理表格 三、導入數據 通過數據庫導入數據 四、合并 五、驗證更新數據 六、 更新數據 update temp_cus_hmz…

我國首條大型無人機城際低空物流航線成功首航

首航震撼開場&#xff1a;羊肉 “飛” 越 540 公里 在夜色的籠罩下&#xff0c;榆陽馬合通用機場的跑道上&#xff0c;一架大型固定翼無人機蓄勢待發&#xff0c;機身被燈光照亮&#xff0c;宛如一只即將展翅翱翔的鋼鐵巨鳥。它的貨艙里&#xff0c;滿滿裝載著新鮮的榆林羊肉&a…