C 程式設計/低階 I/O
雖然 C 標準沒有指定,但許多作業系統都提供了 **檔案描述符** (有時縮寫為 **fd**)的概念。雖然 stdio.h 中的 FILE 型別及其相關函式 封裝了流的底層細節,但檔案描述符是一個整數,它引用作業系統正在跟蹤的流。
本節將探討檔案描述符在 POSIX 系統(如 Linux)中的實現方式。
當一個程序被建立時,作業系統會為程序分配(除其他資源外)三個流:標準流 stdin、stdout 和 stderr。通常,標準流是使用 stdio.h 中的基於 FILE 的定義來互動的,如前一節所述。這些流也可以透過其原始檔案描述符進行互動,這些檔案描述符對於每個程序都是相同的。
unistd.h 符號 |
流 | 檔案描述符 |
|---|---|---|
STDIN_FILENO
|
stdin
|
0
|
STDOUT_FILENO
|
stdout
|
1
|
STDERR_FILENO
|
stderr
|
2
|
請注意,這些檔案描述符對於每個程序都是相同的,即使標準流在每個程序中包含不同的資料。這意味著檔案描述符不一定在系統範圍內是唯一的;每個程序可能有不同的檔案描述符對映到哪些流的檢視,就像每個程序對系統虛擬地址空間有不同的檢視一樣。
可以使用以下函式讀取檔案描述符並向檔案描述符寫入資料:[1]
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
將這些定義與 FILE 基於函式進行比較和對比:[2]
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);
三個區別顯而易見
- 從流中讀取和寫入流的資料不假定為字串。
- 檔案描述符作為引數而不是
FILE。 - 返回型別使用一致的型別。
read 和 fgets 採用類似的引數集:表示流、緩衝區和大小的某些東西;此外,如果讀取的資料量等於請求的大小,則緩衝區將具有相同的內容,無論使用哪個函式。但是,這些函式在讀取的資料量與請求的大小不匹配的情況下表現不同。fgets 旨在用於字串,如果遇到換行符,它將提前停止讀取,並且如果它正在等待字串的其餘部分出現在流中,該函式可能會阻塞。另一方面,read 不會在遇到特殊值時提前停止讀取,但如果管道中尚未寫入所有請求的資料,它將停止讀取。由於 read 無法保證已寫入緩衝區中完全可用的內容(如果它提前停止讀取),因此返回值包含寫入緩衝區的位元組數。這使得 read 更適合於程式設計師需要更多地控制讀取的資料型別或願意以接收部分讀取的資料來減少阻塞 I/O 操作的情況。
類似地,write 需要一個顯式的大小引數,因為它不能假設正在寫入一個以 NULL 結尾的字串,並且它將返回寫入的位元組數,以便程式可以確定傳遞的資料是否已完全寫入流。