檔案IO
2021-05-31 12:46:14 星期一
目錄- 檔案IO
- 基礎IO
- open
- 錯誤
- creat
- read
- 一個例子
- write
- close
- lseek
- 檔案空洞
- unlink洗掉
- iotcl
- open
- 檔案和目錄
- 鏈接
- 錯誤列印
- perror
- strerror
- 原子IO
- fcntl
- 檔案IO緩沖
- stdio的緩沖
- 基礎IO
檔案描述符:是有限資源
| 檔案描述符 | POSIX名稱 | 用途 | stdio流 |
|---|---|---|---|
0 |
STDIN_FILENO |
標準輸入 | stdin |
1 |
STDOUT_FILENO |
標準輸出 | stdout |
2 |
STDERR_FILENO |
標準錯誤 | stderr |
基礎IO
open
#include <fcntl.h>
int open(const char *path, int oflag, mode_t mode);
path檔案的路徑和名稱flag檔案的打開模式,組合使用時需要使用位運算或- 基本模式
-
只讀 只寫 讀寫 O_RDONLYO_WRONLYO_RDWR000102
-
- 附加模式(只列常用)
O_APPEND:總是在檔案末尾添加資料O_EXCL:配合O_CREAT標志- 表明如果檔案存在則不會打開檔案,并使open呼叫失敗,否則能夠創建并打開檔案,這樣確保了呼叫
open()的行程即為創建檔案的行程. - 同時不允許path是符號鏈接
- 表明如果檔案存在則不會打開檔案,并使open呼叫失敗,否則能夠創建并打開檔案,這樣確保了呼叫
O_CREAT:沒有檔案存在時會創建檔案,需要mode引數指明檔案權限,一共9個O_TRUNC:如果檔案存在且為普通檔案且該行程對改檔案有寫權限,則清空檔案內容
- 基本模式
modeS_IRUSR:檔案所有者有讀權限S_IWUSR:檔案所有者有寫權限S_IXUSR:檔案所有者有執行權限S_IRGRP:同組用戶有讀權限S_IWGRP:同組用戶有寫權限S_IXGRP:同組用戶有執行權限S_IROTH:其他用戶有讀權限S_IWOTH:其他用戶有寫權限S_IXOTH:其他用戶有執行權限
- 回傳檔案描述符
fd或者-1
錯誤
creat
舊版使用,新版都用open進行創建檔案
read
將open回傳的fd檔案描述符中讀取bytes位元組的資料到buf中,回傳實際讀取的位元組數,不成功回傳-1
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t bytes);
- 當
read普通檔案:呼叫成功回傳實際讀取的位元組數,遇到檔案EOF時回傳0,出現錯誤回傳-1, - 當
read讀取終端,遇到\n即回傳
注意:read系統呼叫是逐位元組讀取的,所以無法遵守C語言中的字串的規則,比如C語言中字串以\0(0x0)作為結束,但是read認為這里的位元組值是0x0并繼續讀下去.所以使用read讀資料時,通常的方式是:每次讀取資料,再將的實際讀取到的size處設定成C語言認可的字串結束符,即buffer[size] = '\0';
#define MAX_READ 16
char buffer[MAX_READ + 1];
ssize_t size;
size = read(fd, buffer, MAX_READ);
if (size == -1)
exit(0);
buffer[size] = '\0';
close(fd);
一個例子
該檔案從終端讀取一行(因為read讀終端時以\n作為結束)字符并列印出來,同時列印每一個字符
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define MAX_READ 16
int main(int argc, char **argv)
{
char buffer[MAX_READ + 1];
memset(buffer, 0x23, sizeof(buffer)); // fill with #
buffer[MAX_READ] = '\0';
ssize_t numRead, i;
numRead = read(STDIN_FILENO, buffer, MAX_READ);
if (numRead == -1)
perror("read");
// buffer[numRead] = '\0';
printf("the input data was:%s\n", buffer);
for (i = 0; buffer[i] != '\0' && i < MAX_READ; i++)
printf("%ld->%x\n", i, buffer[i]);
return 0;
}
$ ./4.4
abcd
the input data was:abcd
############
0->61
1->62
2->63
3->64
4->a
5->23
6->23
...
16->0
由此可見read只是以二進制的形式照搬資料,并不對資料進行處理,因此,對資料的處理留給了程式員
write
將bytes位元組的buf資料寫到open回傳的fd檔案描述符所指的檔案中,回傳實際寫的位元組數,不成功回傳-1,寫入已打開的檔案,呼叫成功并不代表已經寫入磁盤,可能先進入快取(這樣減少磁盤活動量、加快write呼叫),
#include <unistd.h>
ssize_t write(int fd, void *buf, size_t bytes);
close
#include <unistd.h>
int close (int fd);
close函式也有錯誤處理,編程時也應該錯誤檢查,
lseek
內核打開的檔案時會記錄檔案偏移量,第一位元組的偏移量為0,檔案打開時,會將偏移量設定為0
十分重要:有時候讀檔案讀不出來,可能就是因為檔案偏移量在檔案末尾處,這時候需要重置
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
fd檔案描述符offset表示偏移量- 正值為正向移動,向繼續往下讀的方向
wherece表示檔案讀寫指標從哪里開始計數SEEK_SET表示起始位置SEEK_CUR表示當前位置SEEK_END表示末尾位置的后一個位元組(這里直接寫資料的話是恰好和檔案連接)
- 回傳新的檔案偏移量或
-1(執行失敗)
檔案空洞
從檔案結尾后到新寫入的資料間的空間,他不占用磁盤空間,直到寫入了資料,這時檔案的名義的大小可能比磁盤存盤的總量大,具體的在14節
ls -l file 查看檔案邏輯大小
du -c file 查看檔案實際占用的存盤塊多少
od -c file 查看檔案存盤的內容
unlink洗掉
只是洗掉path到檔案的一個鏈接,其檔案對于的i-node減1,為0時,改檔案才從磁盤洗掉,
#include <unistd.h>
int unlink(const char *path);
iotcl
檔案和目錄
目錄是另一種檔案,只是內容是包含的檔案資訊和目錄資訊,
鏈接
每一個檔案對應一個inode,檔案的鏈接數對應inode中的鏈接數,記錄著這個檔案的鏈接數值,即指向該inode的檔案數,檔案和inode是多對一的關系,本質上rm指令呼叫系統呼叫unlink函式,將這個檔案的inode的鏈接數-1,為0時才真正洗掉
- 硬鏈接
- 相鏈接的檔案總是同步
- 軟鏈接
- 理解為Windows的快捷方式
$ touch a.txt
$ echo "hello" > a.txt
$ ls -li
total 4
2104443 -rw-rw-r-- 1 dwr dwr 6 Apr 10 22:20 a.txt
# inode編號 檔案權限 用戶 組用戶 不知道 創建月 日 時 檔案名
$ ln a.txt a.txt.bak # 建立硬鏈接
$ ls -li
total 8
2104443 -rw-rw-r-- 2 dwr dwr 6 Apr 10 22:20 a.txt
2104443 -rw-rw-r-- 2 dwr dwr 6 Apr 10 22:20 a.txt.bak
$ ln -s a.txt a.txt.s # 建立軟鏈接
$ ls -li
total 8
2104443 -rw-rw-r-- 2 dwr dwr 6 Apr 10 22:20 a.txt
2104443 -rw-rw-r-- 2 dwr dwr 6 Apr 10 22:20 a.txt.bak
2099642 lrwxrwxrwx 1 dwr dwr 5 Apr 10 22:25 a.txt.s -> a.txt
#include <unistd.h>
int link(const char *__from, const char *__to);
int symlink(const char *__from, const char *__to);
錯誤列印
perror
<errno.h>
根據設定的errno值列印對應的錯誤資訊,列印規則是先列印s中用戶定義的錯誤輸出,在列印系統呼叫錯誤的輸出提示,一定要在系統呼叫之后緊跟列印,否則會被覆寫
void perror(const char *s);
strerror
將錯誤代碼轉換為字串錯誤資訊,
char *strerror(int errno);
原子IO
fcntl
#include <fcntl.h>
int fcntl(int __fd, int __cmd, ...)
檔案IO緩沖
這里是Unix系統編程手冊第13章內容,不全待完善
read()和write()系統呼叫在操作磁盤檔案時不會直接發起磁盤訪問,而是僅僅在用戶空間緩沖區與內核緩沖區高速快取(kernel buffer cache)之間復制資料,write()在后續某個時刻,內核會將其緩沖區中的資料寫入(重繪至)磁盤,
Linux 內核對緩沖區高速快取的大小沒有固定上限,內核會分配盡可能多的緩沖區高速快取頁,而僅受限于兩個因素;可用的物理記憶體總量,以及出于其他目的對物理記憶體的需求(例如,需要將正在運行行程的文本和資料頁保留在物理記憶體中),若可用記憶體不足,則內核會將
解釋一下書中對IO系統呼叫的實驗:
- 總用時=CPU用時+磁盤讀寫用時
- CPU用時=用戶CPU用時(用戶模式下執行的代碼)+系統CPU用時(內核模式(系統呼叫和資料在用戶和內核模式下傳輸)下執行的代碼)
stdio的緩沖
C語言中IO函式可以理解為系統IO呼叫+資料緩沖,免于撰寫者自己處理對資料的緩沖
int setvbuf(FILE *stream, char *buf, int modes, size_t n)
控制stdio庫函式的緩沖形式,需要最先呼叫,之后的stdio操作才有效
stream- 表示配置緩沖的檔案流
buf- 不為空則使用size大小作為緩沖區(這個buf空間應該是堆記憶體上,避免函式呼叫和回傳對堆疊進行修改)
- 為空則自動分配(根據mode選擇是否分配)size大小空間
modes_IONBF:not,不緩沖,每一次呼叫stdio函式都立即呼叫系統呼叫_IOLBF:line,行緩沖,遇到換行符或緩沖區滿則呼叫系統呼叫,指向終端設備的流默認使用該模式_IOFBF:file,全緩沖,指向磁盤的流默認使用該模式
- 出錯回傳非0,成功回傳0
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/285522.html
標籤:C
上一篇:左值與右值
下一篇:Linux C 信號
