一、概述
二、當頭棒喝
去掉代碼中的註釋掉的 fflush 再試下:
系統默認會打開三個文件描述符(stdin,stdout,stderr),在程序中 close(1) 關掉了標準輸出,此時 open 打開返回的是最小可用的文件描述符,也就是 fd = 1,因此 printf 本應該打印到文件中,但是 close(fd) 不會觸發 buffer 刷新,因此既不會輸出到屏幕也沒有輸出到 msg.log 中。
當打開 fflush,則會刷新 buffer,因此就可以看到 msg.log 文件中"hello world"。
三、函數概述
Linux C庫IO函數工作流程:
pcb和文件描述符:
- 文件描述符
- int 類型
- 一個進程最多可打開多少文件
- pcb
- 進程控制塊
- 在其中有一個文件描述符表 – 數組[1024]
虛擬地址空間就是程序啓動起來之後,從硬盤上會有一塊虛擬內存分配出來。
CPU 爲什麼要使用虛擬地址空間與物理地址空間映射?解決了什麼樣的問題?
1)方便編譯器和操作系統安排程序的地址分佈
程序可以使用一系列相鄰的虛擬地址來訪問物理內存中不相鄰的大內存緩衝區。通過虛擬地址空間與物理地址空間映射,解決不連續的緩衝區的問題。
2)方便進程之間隔離
不同進程使用的虛擬地址彼此隔離。一個進程中的代碼無法更改正在由另一進程使用的物理內存。
3)方便OS使用你那可憐的內存。
程序可以使用一系列虛擬地址來訪問大於可用物理內存的內存緩衝區。當物理內存的供應量變小時,內存管理器會將物理內存頁(通常大小爲 4 KB)保存到磁盤文件。數據或代碼頁會根據需要在物理內存與磁盤之間移動。
虛擬地址空間的佈局如下:
C庫函數與系統函數的關係
- FD:文件描述符
- FP_POS:文件指針
- BUFFER:緩衝區
- write:對0-3G的用戶空間進行操作
- sys_write:對3-4G的內核空間進行操作
四、IO函數介紹
1)open
-
作用:打開文件(可能創建文件)
-
頭文件:
-
參數說明:
- pathname 文件名
- flags 標誌位
- 必選項:
- O_RDONLY 只讀
- O_WRONLY 只寫
- O_RDWR 讀寫
- 可選項:
- O_APPEND 追加
- O_CREAT 創建文件
- O_EXCL和O_CREATE一起使用,如果文件存在則報錯
- O_NONBLOCK 非阻塞
- 必選項:
- Mode 權限位,最終(mode&~umask)
-
返回值:
- 成功:返回最小的可用文件描述符
- 失敗:返回-1,並且設置errno
-
open 函數中的 errno:
- 使用 open 實現一個 touch 功能
- 一個進程打開的最大文件數(1024)
2)close
- 作用:關閉文件描述符
- 頭文件:
- 參數說明:
- fd 文件描述符
- 返回值:
- 成功:返回 0
- 失敗:返回 -1,並且設置 errno
3)read
- 作用:從文件描述符讀
- 頭文件:
- 參數說明
- fd 文件描述符
- buf 緩衝區
- count 緩衝區大小
- 返回值
- 失敗:返回 -1,設置 errno
- 成功:
- 返回讀到的字節數
- 0 代表讀到文件末尾
- 非阻塞的情況下 read 返回 -1,但是此時需要判斷 error 的值。
4)write
- 作用:寫到文件描述符
- 頭文件:
- 參數說明:
- fd 文件描述符
- buf 緩衝區
- count 緩衝區大小
- 返回值
- 失敗:返回-1,設置 errno
- 成功:
- 返回寫入的字節數
- 0 代表未寫入
- 實現一個 cat 功能
- 需求:給一個文件中寫入內容,寫完之後打開該文件再讀取寫入的內容?(bug版本)
- 結果:內容寫入到文件中,但是並未按預期輸出到屏幕上。
- 原因:是由於 write 完成之後,fd 到了文件末尾,因此 read 時到了文件末尾,無法讀取文件數據
- 解決方法:寫完之後將文件指針設置到文件開頭,使用請看下文要介紹的 lseek 函數。
5)lseek
- 作用:重定位讀/寫文件de偏移量
- 頭文件:
- 參數說明
- fd 文件描述符
- offset 偏移量
- whence:
- SEEK_SET 文件開始位置
- SEEK_CUR 文件當前位置
- SEEK_END 文件結尾
- 返回值
- 失敗:返回 -1,設置 errno
- 成功:返回當前位置到文件開頭的長度
- lseek 作用
- 移動文件讀寫位置
- 計算文件大小
- 拓展文件
- 移動文件讀寫位置(修改上例的 bug(寫入文件內容並讀取文件內容打印到屏幕))
- 計算文件大小(輸出文件字節數)
- 拓展文件
6)fcntl
- 作用:操作文件描述符
- 頭文件:
- 參數說明:
- fd 文件描述符
- cmd 命令
- 返回值
- 不同的 cmd 返回值不同
- 使用 fcntl 函數實現讀非阻塞
五、利用IO函數實現一個copy函數