【Linux】檔案相關係統呼叫介面(IO)
早期在寫C語言介面的時候,我們可以通過fopen來開啟一個檔案,下面這段兩段程式碼為例:
hello.c寫檔案
1 #include <stdio.h> 2 #include <string.h> 3 int main () 4 { 5 FILE* fp=fopen("myfile","w"); 6 if(!fp){ 7 printf("fopen error!\n"); 8 } 9 const char *msg="hello world!\n"; 10 int count = 5; 11 while(count--){ 12 fwrite(msg,strlen(msg),1,fp); 13 } 14 fclose(fp); 15 return 0; 16 }
hello.c讀檔案
1 #include <stdio.h> 2 #include <string.h> 3 int main () 4 { 5 FILE* fp=fopen("myfile","r"); 6 if(!fp){ 7 printf("fopen error!\n"); 8 } 9 char buf[1024]; 10 const char *msg="hello world!\n"; 11 while(1){ 12 ssize_t s=fread(buf,1,strlen(msg),fp); 13 if(s>0){ 14 buf[s]=0; 15 printf("%s",buf); 16 } 17 if(feof(fp)){ 18 break; 19 } 20 } 21 fclose(fp); 22 return 0; 23 }
顯而易見,是要往“myfile”檔案中寫五條“helloworld”,再通過讀檔案介面讀出來。這裡我們運用了fwrite、fread這些C語言介面。
如果我們想把一個字串顯示在顯示器上,我們可以用 printf、putchar、fputchar、fputs、fprintf、fwrite.......等等一系列介面。而我們也知道任何一個程序在啟動之後,預設開啟三個輸入輸出流,分別為stdin、stdout、stderr,分別對應的裝置是鍵盤、顯示器、顯示器。仔細觀察發現,這三個流的型別都是FILE*,我們用fopen開啟檔案該返回值的型別就是FILE*。而這個FILE*
這是我們的作業系統結構,通過這張圖以及之前的知識我們知道,作業系統不會直接將自己的每個管理模組暴露出來給使用者,而是通過一系列的系統呼叫介面。而剛剛提到的C語言提供的那些介面都是處於使用者操作介面的lib中。
但是剛才的程式碼表明我們最終將訊息列印到了顯示器上,即硬體。可是C庫操作介面在上層的使用者操作介面處,而硬體是在下面的硬體部分,所以說明我們必定要將要寫入的資料自頂向下的交付給底層的硬體,但是要交付肯定不是C庫直接去交付,因為我們沒有這個權利,這就必定要通過作業系統來完成,也就是要通過系統呼叫介面。也就是說,我們的C庫中,必定封裝了對應的系統呼叫介面。
所以,我們現在可以以open為例看看系統呼叫介面,並且用系統呼叫介面來實現剛才的程式碼。
open
#include <sys/sypes.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname , int flags);
int open(const char *pathname , int flags , mode_t mode);
pathname: 要開啟或建立的目標檔案
flags: 開啟檔案時,可以傳入多個引數選項,用下面的一個或者多個常量進行“或”運算,構成flags。
引數:O_RDONLY: 只讀開啟
O_WRONLY: 只寫開啟
O_RDWR : 讀,寫開啟
這三個常量,必須指定一個且只能指定一個
O_CREAT : 若檔案不存在,則建立它。需要使用mode選項,來指明新檔案的訪問許可權
O_APPEND: 追加寫
返回值:
成功:新開啟的檔案描述符
失敗:-1
類比C庫呼叫介面,我們明白fopen底層必定呼叫了open。 下面用檔案的系統呼叫介面來實現剛剛的程式碼
hello.c寫檔案
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7
8 int main ()
9 {
10 umask(0);//設定允許當前程序建立檔案或者目錄最大可操作的許可權
11 int fd = open("myfile",O_WRONLY|O_CREAT,0644);
12 if(fd<0) {
13 perror("open");
14 return 1;
15 }
16 int count = 5;
17 const char *msg = "hello world!\n";
18 int len = strlen(msg);
19 while (count--){
20 write(fd,msg,len);//fd:檔案描述符 msg:緩衝區首地址 len:本次讀取期
望寫入多少位元組的資料 返回值:實際寫了多少位元組的資料
21 }
22 close(fd);
23 return 0;
24 }
hello.c讀檔案
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7
8 int main ()
9 {
10 int fd = open("myfile",O_RDONLY);
11 if(fd<0) {
12 perror("open");
13 return 1;
14 }
15 const char *msg = "hello world!\n";
16 char buf[1024];
17 while(1){
18 ssize_t s=read(fd,buf,strlen(msg));//類比write
19 if(s>0){
20 printf("%s",buf);
21 }else{
22 break;
23 }
24 }
25 close(fd);
26 return 0;
27 }
open、close、read、write、lseek 都屬於系統提供的介面,稱之為系統呼叫介面。所以,可以認為,f#系列的函式,都是對系統呼叫的封裝,方便二次開發。