目錄
前言
一、從硬件角度理解"Linux下一切皆文件"
從理解硬件是種“文件”到其他系統資源的抽象
二、緩沖區
1.緩沖區介紹
2.緩沖區的刷新策略
3.用戶級緩沖區
這個用戶級緩沖區在哪呢?
解釋關于fork再加重定向“>”后數據會打印兩份的原因
4.內核緩沖區簡介
總結
前言
"Linux下一切皆文件",這是Linux的一個基本設置理念同時也是Linux的設計哲學所在。
這篇博客,筆者首先總結一下我自學習Linux以來,到目前自己對“Linux下一切皆文件”的感悟和理解,其次再討論Linux中的緩沖區機制。
提示:以下是本篇文章正文內容,下面案例可供參考
一、從硬件角度理解"Linux下一切皆文件"
首先需要再次明確Linux操作系統的主要目的或者作用:
對上,方便用戶使用——為用戶提供穩定的、高效的、安全的使用環境。
對下,管理好計算機繁雜的軟硬件資源;
其次需要明確的是文件無外乎由兩部分構成:內容和屬性
內容決定文件“是什么”(數據含義)。
屬性決定文件的“如何用”(權限、存儲、類型)。
比如一個普通文件:
他的內容是文本、二進制數據;
他的屬性是文件名、權限、大小、時間戳等。
我們可以通過write、read等修改文件內容,也可以用chmod函數修改文件的權限等屬性。
初識Linux:常見指令介紹,文件權限的更改,以及粘滯位的理解-CSDN博客
那么思維發散一下,我們能否將這些硬件的自身狀態、操作方法抽象為“內容+屬性”,并用統一的接口修改這些硬件呢?
已知的是Linux似乎正是將系統資源(如硬件設備等)抽象為文件,提供統一的文件操作接口(open
,?read
,?write
,?close
等)。使得無論操作對象是普通文件、目錄、設備,用戶都可以通過相同的文件系統與之交互。
從理解硬件是種“文件”到其他系統資源的抽象
對于計算機上諸多的硬件資源,我目前認為操作系統通過:先整理,再管理的方法管理這些硬件。
所謂先整理,在管理。這是筆者從進程PCB的創建受到的啟發。OS為方便管理不同的進程會為其創建PCB,其中包含著進程的所有屬性信息,那管理硬件是不是也可以通過創建某種數據結構來實現管理呢?
①假設OS為方便管理各種硬件資源會為其創建某種數據結構——這里想象成某種結構體struct file,該結構體中記錄著該硬件的各種屬性信息和行為函數——即IO操作。
②通過對馮諾依曼體系結構的抽象,將計算機抽象為存儲器和其他。這個其他中包括cpu和各種硬件設備,這么劃分的原因是這些硬件都要與內存進行IO操作。我們不妨暫將所有硬件的IO操作抽象為兩個函數read( )和write( )。
通過上述兩點,創建一個struct file結構體,其中有著各硬件的狀態信息和函數行為:
struct file
{//內容int type;int status;……//屬性int (*write)();//函數指針int (*read)();……
}
那么將每個硬件file實例化(與多態有些相似),再通過一個數據結構如鏈表將這些硬件的struct對象管理起來,如鏈表。
綜上,當站在上層視角來看,這些硬件都是一種統一的數據,其中有著“內容+屬性”,這不就是一種抽象的“文件”嗎。這些個文件提供統一的文件操作接口(write、read、open、close等),無論操作對象是鍵盤、鼠標還是其他什么硬件,用戶都可以通過相同的接口與之交互。
將上述思想和方法發散到其他系統資源,同樣通過“先整理,再管理”的思想,這或許是理解“Linux下一切皆文件”的思路之一吧。
問:磁盤等硬件有輸入輸出好理解,那如顯示器等硬件不是只有輸入或者只有輸出嗎?
答:雖然如顯示器等設備沒有輸入操作,我們只需將其struct內部的函數指針置為NULL即可。
以上是筆者目前對“Linux下一切皆文件”的理解,若筆者有錯誤的認識或者讀者有更深的理解,還請讀者不吝賜教,在評論區中一起討論。
二、緩沖區
1.緩沖區介紹
1)什么是緩沖區
緩沖區本質上就是一段內存。
2)為什么要有緩沖區
磁盤等存儲設備物理I/O效率極低,通過引入緩沖區將多次小數據操作合并為大數據操作,從而節省數據IO時間,提升性能。
2.緩沖區的刷新策略
通過以下代碼觀察緩沖區:
在程序sleep的十秒之間printf不會打印,等sleep結束后才會打印。
注意printf沒有帶\n。
1 #include<stdio.h>2 #include<unistd.h>3 int main()4 {5 printf("hello Linux");//注意沒帶\n6 sleep(10); 7 return 0;8 }
但在printf和sleep之間添加了fflsh(stdout)后,printf會立即打印。
下面是緩沖區的三種刷新策略。
1)立即刷新——無緩沖
2)行刷新——行緩沖
3)緩沖區滿——全緩沖(效率最高)
有兩種特殊情況緩沖區會立即刷新:
①用戶強制刷新(如上述的fflush函數);
②程序退出——這也是為什么在某些集成開發環境下(如vs2022)程序時得等一會才能在控制臺上看到打印結果。
3.用戶級緩沖區
引子——觀察下列代碼在bash不同指令下的執行情況:
1 #include<stdio.h>2 #include<unistd.h>3 #include<string.h>4 int main()5 {6 printf("hello Linux\n");//注意沒帶\n7 fprintf(stdout,"hello fprintf\n");8 fputs("hello fpurs",stdout);9 const char *str="hello writie\n";10 write(stdout->_fileno,str,strlen(str));11 12 fork(); 13 return 0;14 }
執行 .?/ test:
執行. / test > log.txt
可以發現. / test > log.txt比. / test多打印了幾行,這多打印的全是C標準庫提供的函數。
這個用戶級緩沖區在哪呢?
通過觀察stdout的類型,我們可以推導出FILE中不僅有文件描述符,還存在緩沖區,所有當我們想要主動刷新緩沖區時,fflush傳入的是FILE*指針。
解釋關于fork再加重定向“>”后數據會打印兩份的原因
①沒有進行>時,我們看到打印了四條數據。stdout默認采用的是行刷新,在進行fork之前三條C函數已經將數據打印到顯示器上了,FILE中不在存有相應數據了;
②如果我們進行了>,寫入的文件不再是顯示器,而是普通文件,采用的刷新策略也不再是行緩沖而是全緩沖,而這三條C打印顯然不能填滿緩沖區,于是數據就沒有被刷新。fork函數之后緊接著就是程序退出,故當fork創建子進程后,無論父子進程誰先退出都必定會發生寫時拷貝(緩沖區刷新就是修改),因此父子進程分別向log.txt中打印了數據。
至于write,他是linux系統調用,不屬于C,且write用的是fd文件描述符沒有使用FILE結構體,所以C提供的緩沖區中就不考慮write,因此無論那種情況write都只打印了一次。
為什么stdout標準輸出默認采用行緩沖?
有關文件描述符的解釋,參看:Linux中有關文件操作的系統接口,文件描述符,重定向的介紹-CSDN博客
有關fork函數和寫時拷貝的解釋,參看:
Linux環境下的進程創建-fork函數的使用與寫時拷貝, 進程退出exit和_exit的區別,以及進程等待waitpid和status數據的提取方法-CSDN博客
4.內核緩沖區簡介
1)內核緩沖區在相應文件的file_struct中。流是文件的特殊或者說流是文件的一種高級抽象。
file_struct與task_struct(PCB)一樣都是內核級數據結構,在task_struct中有著指向file_struct的指針。
2)內核緩沖區的刷新策略完全由OS自主決定;
3)完整的數據刷新過程:
4)fsync函數:強制將內核緩沖區中的數據刷入磁盤。
總結
筆者水平淺薄,對于上述內容難免有疏忽疑錯,還請讀者多多指處。
希望本文對你有所幫助
讀完點贊,手留余香~