Linux檔案IO open、dup、fork核心原理圖解
1、open一個檔案
一個Linux程式啟動後,會在核心空間建立一個PCB程式控制塊,PCB是一個程式的私有財產。
這個PCB中有一個已開啟檔案描述符表,記錄著所有該程式開啟的檔案描述符以及對應的file結構體地址。
預設情況下,啟動一個Linux程式後,會開啟三個檔案,分別是標準輸入、標準輸出、標準錯誤分別使用了0、1 、2號檔案描述符。
當該程式使用函式open開啟一個新的檔案時,一般會在核心空間申請一個file結構體,並且把3號檔案描述符對應的file指標指向file結構體。
程式碼如下:
testOpen.c
int main(int argc, char *argv[]) { int fd = open("./log.txt", O_RDWR); printf("new fd = %d\n", fd); }
原理圖如下:
process table entry就是程式的檔案描述符表,file table entry用來記錄檔案的讀寫開啟模式,當前檔案偏移量,以及v-node指標。
v-node table entry是虛擬檔案系統對應的檔案節點,i-node是磁碟檔案系統對應的檔案節點。通過這兩個節點就能找到最終的磁碟檔案。
每一個程式只有一個process table entry,一般情況下預設使用 fd 0、fd1、fd2,新開啟的檔案log.txt將使用
fd 3。
2、兩個程式同時open一個檔案
兩個程式同時open一個檔案,這個時候的原理圖如下:
因為現在是兩個程式,所以process table entry程式控制塊也是兩個,每個程式控制塊中各自維護一個張檔案描述符表,同時開啟一個檔案的時候,都各自申請了一個file table entry。
兩個file table entry,怎麼去證明呢?
test2open.c
int main(int argc, char *argv[]) { int fd = open("./log.txt", O_RDWR); printf("new fd = %d\n", fd); printf("%ld\n", lseek(fd, 0, SEEK_CUR)); write(fd, "123", 3); sleep(5); printf("%ld\n", lseek(fd, 0, SEEK_CUR)); close(fd); }
file table entry中都儲存了一個檔案讀寫偏移量,如果是兩個file table entry,那麼兩個程式讀寫位置是獨立的,不受影響的。
上面的程式碼執行結果是:
#先啟動程式0 $ ./a.out new fd = 3 0 3 #在5秒時間內,啟動程式1 $ ./a.out new fd = 3 0 3
兩個程式都分配了fd 3 給新開啟個檔案,並且讀寫位置不受其他程式的影響 。如果受影響了話,程式1的讀寫位置要變成3和6.
3 一個程式open兩次同一個檔案
一個程式open兩次同一個檔案,其實跟兩個程式open一次的原理相同,都是呼叫了兩次open,反正只要記住,呼叫一次open函式,就會建立一個file table entry。
原理圖如下:
由於只有一個程式,所以只有一個process table entry,open了兩次,所以是兩個file table entry 分別分配了fd 3與fd 4指向這兩個結構體。
程式碼如下:
int main(int argc, char *argv[]) { int fd0 = open("./log.txt", O_RDWR); int fd1 = open("./log.txt", O_RDWR); printf("new fd0 = %d\n", fd0); printf("new fd1 = %d\n", fd1); write(fd0, "123", 3); printf("fd0 lseek %ld\n", lseek(fd0, 0, SEEK_CUR)); printf("fd1 lseek %ld\n", lseek(fd1, 0, SEEK_CUR)); close(fd0); close(fd1); }
上面程式碼open了兩次log.txt,建立了兩個file結構體,驗證方法還是通過判斷讀寫位置是否是獨立的。
執行結果:
new fd0 = 3 new fd1 = 3 fd0 lseek 3 fd1 lseek 0
結果已經說明一切了,修改fd0的讀寫位置不會影響fd1的讀寫位置。
4、使用dup複製檔案描述符
dup函式與open函式不同,open函式會建立一個file table,但是dup只是申請一個fd來指向一個已經存在的file table。原理圖如下:
程式碼 testdup.c
int main(int argc, char *argv[]) { int fd, copyfd; fd = open("./log.txt", O_RDWR); /*複製fd*/ copyfd = dup(fd); write(fd, "123", 3) /*列印出fd和copyfd的偏移量,經過上面的寫操作,都變成3了*/ printf("fd lseek %ld\n", lseek(fd, 0, SEEK_CUR)); printf("copyfd lseek %ld\n", lseek(copyfd, 0, SEEK_CUR)); close(fd); close(copyfd); return 0; }
執行結果:
$ ./a.out fd lseek 3 copyfd lseek 3
結果證明只要操作了fd 或copyfd這兩個檔案描述符中一個的讀寫位置,就會影響到另一個檔案描述符的讀寫位置。說明這兩個檔案描述符指向的是同一個file table。
需要注意的是,一旦dup了一次,就會file table引用計數加一,如果想要釋放file table的記憶體,必須要把open以及所有dup出來的檔案描述符都關閉掉。
5、fork之後open
如果在呼叫fork之後呼叫一次open函式,由於fork之後會返回兩次,一次父程式返回,一次子程式返回,那麼這個時候其實是相當與兩個程式分別呼叫了一次open函式開啟同一個檔案,與第二節中的原理相同。
程式碼如下:testforkopen.c
int main(int argc, char *argv[]) { int pid = fork(); int fd = open("./log.txt", O_RDWR); printf("pid %d %ld\n", pid, lseek(fd, 0, SEEK_CUR)); write(fd, "123", 3); sleep(5); printf("pid %d %ld\n", pid, lseek(fd, 0, SEEK_CUR)); close(fd); }
執行結果:
$ ./a.out pid 6112 lseek 0 #父程式 pid 0 lseek 0 #子程式 pid 6112 lseek 3 #父程式 pid 0 lseek 3 #子程式
可以看到父子程式的讀寫位置都是3,並不受影響。
6 fork之前open
fork之前呼叫open函式,也就是隻呼叫了一次,產生了一個fd以及file table,fork之後子程式的process table entry會從父親程式中複製過來,檔案描述表也複製過來了,那麼子程式的fd指向的是同一個file table。
原理圖如下:
程式碼如下:testopenfork.c
int main(int argc, char *argv[]) { int fd = open("./log.txt", O_RDWR); int pid = fork(); printf("pid %d %ld\n", pid, lseek(fd, 0, SEEK_CUR)); write(fd, "123", 3); sleep(5); printf("pid %d %ld\n", pid, lseek(fd, 0, SEEK_CUR)); close(fd); }
執行結果:
$ ./a.out pid 6388 lseek 0 pid 0 lseek 3 pid 6388 lseek 6 pid 0 lseek 6
父子程式都各自寫入3位元組,如果是兩個file table,那麼最終都應該列印的是3,而不是6,請與第5節進行對比。
需要注意的是:如果想要釋放這個file table,也必須父子程式都close一次fd才會釋放,如果不close,程式退出的時候會自動close掉所有的檔案描述符。
相關文章
- linux核心檔案IO的系統呼叫實現分析(open)Linux
- linux系統程式設計之檔案與IO(八):檔案描述符相關操作-dup,dup2,fcntlLinux程式設計
- linux系統程式設計之檔案與IO(一):檔案描述符、open,closeLinux程式設計
- Linux C 檔案IOLinux
- Linux檔案IO操作Linux
- Semaphore 使用&核心原理 圖解圖解
- <摘錄>linux檔案IOLinux
- Linux核心檔案Linux
- Linux下開發-dup和dup2Linux
- dup函式:複製檔案描述符函式
- 物聯網學習教程Linux系統程式設計之檔案描述符的複製:dup()和dup2()Linux程式設計
- Linux系統程式設計-檔案IOLinux程式設計
- 檔案IO操作
- Linux系統程式設計(2)——檔案與IO之系統呼叫與檔案IO操作Linux程式設計
- Linux核心中讀寫檔案Linux
- 檔案讀寫網路IO簡單瞭解,同步IO和非同步IO非同步
- python IO模組【二】:open函式詳解Python函式
- Linux系統程式設計之檔案IOLinux程式設計
- socket.io 原理詳解
- engine.io 原理詳解
- cadence 開啟原理圖檔案
- linux中fork()函式詳解Linux函式
- Linux fork 炸彈解析,Linux Fork BombLinux
- Java 檔案 IO 操作Java
- iOS檔案IO操作iOS
- 檔案讀寫IO
- Java IO 建立檔案解決檔名重複問題Java
- 8、Linux下檔案伺服器搭建及原理講解Linux伺服器
- linux核心初始化階段-fork內嵌問題Linux
- oracle io 等待圖解Oracle圖解
- 使用LINUX dup2 複製檔案描述符到標準輸出STDOUT_FILENOLinux
- 圖解Linux的IO模型和相關技術圖解Linux模型
- 標準IO與檔案IO 的區別
- Office檔案的Open Xml 格式XML
- 將PDF檔案Open In MyAppAPP
- 關於檔案的open方法
- Python IO檔案管理Python
- IO流-檔案拷貝