使用Linux的文件API,經常看見一個東西,叫做文件描述符. 什麼是文件描述符? (1)文件描述符其實實質是一個數字,這個數字在一個進程中表示一個特定的含義,當我們open打開一個文件時,操作系統在記憶體中構建了一些數據結構來表示這個動態文件,然後返回給應用程式一個數字作為文件描述符,這個數字就和我 ...
使用Linux的文件API,經常看見一個東西,叫做文件描述符.
什麼是文件描述符?
(1)文件描述符其實實質是一個數字,這個數字在一個進程中表示一個特定的含義,當我們open打開一個文件時,操作系統在記憶體中構建了一些數據結構來表示這個動態文件,然後返回給應用程式一個數字作為文件描述符,這個數字就和我們記憶體中維護這個動態文件的這些數據結構掛鉤綁定上了,以後我們應用程式如果要操作這一個動態文件,只需要用這個文件描述符進行區分。
(2)文件描述符就是用來區分一個程式打開的多個文件的。
(3)文件描述符的作用域就是當前進程,出了當前進程這個文件描述符就沒有意義了
(4)文件描述符fd的合法範圍是0或者一個正數,不可能是一個負數
(5)open返回的fd必須記錄好,以後向這個文件的所有操作都要靠這個fd去對應這個文件,最後關閉文件時也需要fd去指定關閉這個文件。如果在我們關閉文件前fd丟了,那麼這個文件就沒法關閉了也沒法讀寫了
1)打開與讀取文件
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 7 int main(int argc, char const *argv[]) { 8 9 int fd = -1; //文件描述符 10 11 //打開文件 12 fd = open( "ghostwu.txt", O_RDWR ); 13 14 if ( -1 == fd ) { 15 printf("文件打開失敗\n"); 16 }else { 17 printf("文件打開成功,fd=%d\n", fd ); 18 } 19 20 //讀取文件 21 int count = 0; 22 char buf[20]; 23 count = read( fd, buf, 50 ); 24 if ( -1 == count ) { 25 printf("文件讀取失敗\n"); 26 }else { 27 printf("文件讀取成功,實際讀取的位元組數目為:%d\n內容為%s\n", count, buf ); 28 } 29 30 //關閉文件 31 close( fd ); 32 33 return 0; 34 }
需要在當前目錄下存在ghostwu.txt這個文件,否則打開的時候失敗,這裡涉及2個api
int open(const char *pathname, int flags);
open非常簡單,第一個參數就是文件路徑, 第二個是文件模式,在man手冊中還提供了其他幾種方式。
ssize_t read(int fd, void *buf, size_t count);
第一個參數為文件描述符,就是open返回的那個值
第二個參數buf用來存儲從文件中讀取的內容
第三個參數,表示希望從文件中讀取的內容( 註:這個count數字可以隨便給,最終以返回的實際數目(read的返回值)為準
2)打開與寫入文件
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <string.h> 7 8 int main(int argc, char const *argv[]) { 9 10 int fd = -1; //文件描述符 11 12 //打開文件 13 fd = open( "ghostwu.txt", O_RDWR ); 14 15 if ( -1 == fd ) { 16 printf("文件打開失敗\n"); 17 }else { 18 printf("文件打開成功,fd=%d\n", fd ); 19 } 20 21 //寫文件 22 char buf[] = "I love Linux, Linux is very very good!!!"; 23 int count = 0; 24 count = write( fd, buf, strlen( buf ) ); 25 if ( -1 == count ) { 26 printf("文件寫入失敗\n"); 27 }else { 28 printf("文件寫入成功,實際寫入的位元組數目為:%d\n", count); 29 } 30 31 //關閉文件 32 close( fd ); 33 34 return 0; 35 }
ssize_t write(int fd, const void *buf, size_t count);
第一個參數為文件描述符,就是open返回的那個值
第二個參數buf用來存儲寫入的內容
第三個參數,表示希望寫入的文件大小
3)open的一些flag參數
1,只讀與只寫許可權
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 7 int main(int argc, char const *argv[]) { 8 9 int fd = -1; //文件描述符 10 11 //打開文件, O_RDONLY:只讀許可權,打開之後的文件只能讀取,不能寫入 12 //打開文件, O_WRONLY:只寫許可權,打開之後的文件只能寫入,不能讀取 13 // fd = open( "ghostwu.txt", O_RDONLY ); 14 fd = open( "ghostwu.txt", O_WRONLY ); 15 16 if ( -1 == fd ) { 17 printf("文件打開失敗\n"); 18 }else { 19 printf("文件打開成功,fd=%d\n", fd ); 20 } 21 22 //讀取文件 23 int count = 0; 24 char buf[41]; 25 count = read( fd, buf, 38 ); 26 if ( -1 == count ) { 27 printf("文件讀取失敗\n"); 28 }else { 29 printf("文件讀取成功,實際讀取的位元組數目為:%d\n內容為%s\n", count, buf ); 30 } 31 32 //關閉文件 33 close( fd ); 34 35 return 0; 36 }
2,清空與追加
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <string.h> 7 8 int main(int argc, char const *argv[]) { 9 10 int fd = -1; //文件描述符 11 12 //打開文件 13 //在O_RDWR模式下,對於一個已經存在的文件,且有內容,那麼寫入文件會覆蓋對應大小的源文件內容【不是完全覆蓋】 14 // fd = open( "ghostwu.txt", O_RDWR ); 15 //在具有寫入許可權的文件中,使用O_TRUNC 會先把原來的內容清除,再寫入新的內容 16 // fd = open( "ghostwu.txt", O_RDWR | O_TRUNC ); 17 //在具有寫入許可權的文件中,使用O_APPEND 會把新內容追加到原來內容的後面 18 // fd = open( "ghostwu.txt", O_RDWR | O_APPEND ); 19 20 //在具有寫入許可權的文件中,使用O_APPEND和O_TRUNC O_TRUNC起作用,會把原來的內容清除,再寫入新的內容 21 fd = open( "ghostwu.txt", O_RDWR | O_APPEND | O_TRUNC ); 22 23 if ( -1 == fd ) { 24 printf("文件打開失敗\n"); 25 return -1; 26 }else { 27 printf("文件打開成功,fd=%d\n", fd ); 28 } 29 30 //寫文件 31 char buf[] = "new content"; 32 int count = 0; 33 count = write( fd, buf, strlen( buf ) ); 34 if ( -1 == count ) { 35 printf("文件寫入失敗\n"); 36 return -1; 37 }else { 38 printf("文件寫入成功,實際寫入的位元組數目為:%d\n", count); 39 } 40 41 //關閉文件 42 close( fd ); 43 44 return 0; 45 }
3,文件存在已否,創建文件與設置許可權
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <errno.h> 7 8 int main(int argc, char const *argv[]) { 9 10 int fd = -1; 11 12 // fd = open( "ghostwu.txt", O_RDWR | O_CREAT | O_EXCL ); 13 14 /* 15 文件不存在: 16 創建這個文件 並打開成功 17 文件存在: 18 再次運行時(文件已經創建成功,存在了), 這時打開失敗 19 */ 20 // fd = open( "ghostwu.txt", O_RDWR | O_CREAT ); 21 22 fd = open( "ghostwu.txt", O_RDWR | O_CREAT | O_EXCL, 666 ); 23 24 if( -1 == fd ) { 25 printf("文件打開失敗,錯誤號:%d\n", errno ); 26 perror( "open" ); 27 return -1; 28 }else { 29 printf("文件打開成功\n"); 30 } 31 32 close( fd ); 33 34 return 0; 35 }
上面用到了一個函數perror,errno和perror:
1)errno就是error number,意思就是錯誤號碼。linux系統中對各種常見錯誤做了個編號,當函數執行錯誤時,函數會返回一個特定的errno編號來告訴我們這個函數到底哪裡錯了
2)errno是由操作系統來維護的一個全局變數,操作系統內部函數都可以通過設置errno來告訴上層調用者究竟剛纔發生了一個什麼錯誤
3)errno本身實質是一個int類型的數字,每個數字編號對應一種錯誤。當我們只看errno時只能得到一個錯誤編號數字,並不知道具體錯在哪裡,所以:linux系統提供了一個函數perror(意思print error),perror函數內部會讀取errno並且將這個不好認的數字直接給轉成對應的錯誤信息字元串,然後列印出來
4,lseek用來移動文件內部指針
簡單應用:統計文件大小
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <errno.h> 7 8 int main(int argc, char const *argv[]) { 9 10 if ( argc != 2 ) { 11 printf("usage:%s %s\n", argv[0], "filename"); 12 return -1; 13 } 14 15 int fd = -1; 16 17 fd = open( argv[1], O_RDWR ); 18 19 if( -1 == fd ) { 20 printf("文件打開失敗,錯誤號:%d\n", errno ); 21 perror( "open" ); 22 return -1; 23 }else { 24 printf("文件打開成功\n"); 25 } 26 27 //把指針移動到文件末尾,就是文件的大小 28 int count = lseek( fd, 0, SEEK_END ); 29 30 printf("文件大小為%d\n", count); 31 32 close( fd ); 33 return 0; 34 }
------------------------------------------分割線------------------------------------------
一、同一個進程,多次打開同一個文件,然後讀出內容的結果是: 分別讀【我們使用open兩次打開同一個文件時,fd1和fd2所對應的文件指針是不同的2個獨立的指針】
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <errno.h> 6 #include <unistd.h> 7 8 int main(int argc, char const *argv[]) { 9 10 int fd1 = -1; 11 int fd2 = -1; 12 char buf1[20] = {0}; 13 char buf2[20] = {0}; 14 int count1 = 0; 15 int count2 = 0; 16 17 fd1 = open( "ghostwu.txt", O_RDWR ); 18 19 if ( -1 == fd1 ) { 20 printf("文件打開失敗\n"); 21 perror( "open" ); 22 return -1; 23 }else { 24 printf("文件打開成功,fd1=%d\n", fd1); 25 } 26 27 count1 = read( fd1, buf1, 5 ); 28 if ( -1 == count1 ) { 29 printf( "文件讀取失敗\n" ); 30 perror( "read" ); 31 }else { 32 printf( "文件讀取成功,讀取的內容是%s\n", buf1 ); 33 } 34 35 fd2 = open( "ghostwu.txt", O_RDWR ); 36 37 if ( -1 == fd1 ) { 38 printf("文件打開失敗\n"); 39 perror( "open" ); 40 return -1; 41 }else { 42 printf("文件打開成功,fd2=%d\n", fd1); 43 } 44 45 count2 = read( fd2, buf2, 10 ); 46 if ( -1 == count2 ) { 47 printf( "文件讀取失敗\n" ); 48 perror( "read" ); 49 }else { 50 printf( "文件讀取成功,讀取的內容是%s\n", buf2 ); 51 } 52 53 close( fd1 ); 54 close( fd2 ); 55 56 return 0; 57 }
二、同一個進程,多次打開同一個文件,然後寫入內容的結果是: 分別寫,當使用O_APPEND,就是接著寫
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <errno.h> 6 #include <unistd.h> 7 #include <string.h> 8 9 int main(int argc, char const *argv[]) { 10 11 int fd1 = -1; 12 int fd2 = -1; 13 char buf1[] = "ghost"; 14 char buf2[] = "wu"; 15 int count1 = 0; 16 int count2 = 0; 17 18 fd1 = open( "ghostwu.txt", O_RDWR ); 19 20 if ( -1 == fd1 ) { 21 printf("文件打開失敗\n"); 22 perror( "open" ); 23 return -1; 24 }else { 25 printf("文件打開成功,fd1=%d\n", fd1); 26 } 27 28 count1 = write( fd1, buf1, strlen( buf1 ) ); 29 if ( -1 == count1 ) { 30 printf( "文件寫入失敗\n" ); 31 perror( "write" ); 32 }else { 33 printf( "文件寫入成功,寫入的內容是%s\n", buf1 ); 34 } 35 36 fd2 = open( "ghostwu.txt", O_RDWR ); 37 38 if ( -1 == fd1 ) { 39 printf("文件打開失敗\n"); 40 perror( "open" ); 41 return -1; 42 }else { 43 printf("文件打開成功,fd2=%d\n", fd1); 44 } 45 46 count2 = write( fd2, buf2, strlen( buf2 ) ); 47 if ( -1 == count2 ) { 48 printf( "文件寫入失敗\n" ); 49 perror( "write" ); 50 }else { 51 printf( "文件寫入成功,寫入的內容是%s\n", buf2 ); 52 } 53 54 close( fd1 ); 55 close( fd2 ); 56 57 return 0; 58 }View Code
上面代碼保持不變,再寫入的時候加入flag標誌:
fd1 = open( "ghostwu.txt", O_RDWR | O_APPEND );
fd2 = open( "ghostwu.txt", O_RDWR | O_APPEND );
三、 dup後的fd和原來打開文件的fd指向的是同一個文件,同時對這個文件寫入時,是接著寫
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <string.h> 7 8 int main(int argc, char const *argv[]) { 9 10 int fd1 = -1; 11 int fd2 = -1; 12 13 fd1 = open( "ghostwu.txt", O_RDWR ); 14 15 if ( -1 == fd1 ) { 16 perror( "open" ); 17 return -1; 18 }else { 19 printf("文件打開成功:fd=%d\n", fd1); 20 } 21 22 //dup後的文件,同時write 是接著寫入 23 fd2 = dup( fd1 ); 24 printf("文件dup成功:fd=%d\n", fd2); 25 26 //分別向fd1和fd2指向的文件寫入 27 28 char buf1[] = "ghost"; 29 char buf2[] = "wu"; 30 31 int count1 = -1, count2 = -1; 32 33 while ( 1 ) { 34 count1 = write( fd1, buf1, strlen( buf1 ) ); 35 if ( -1 == count1 ) { 36 perror( "buf1->write" ); 37 return -1; 38 }else { 39 printf("buf1->文件寫入成功\n"); 40 } 41 42 sleep( 1 ); 43 44 count2 = write( fd2, buf2, strlen( buf2 ) ); 45 if ( -1 == count2 ) { 46 perror( "buf2->write" ); 47 return -1; 48 }else { 49 printf("buf2->文件寫入成功\n"); 50 } 51 } 52 53 close( fd1 ); 54 close( fd2 ); 55 return 0; 56 }View Code
在linux系統中,內核占用了0、1、2這三個fd,當我們運行一個程式得到一個進程時,內部就預設已經打開了3個文件,
對應的fd就是0、1、2。分別叫stdin、stdout、stderr。也就是標準輸入、標準輸出、標準錯誤。接下來,我們把標準輸出關閉,printf就不會輸出,如果用dup複製原來的fd,那麼新dup出來的fd就是1(對應標準輸出)
之後標準輸出的內容都會被寫入到原來fd對應的那個文件
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <string.h> 7 8 int main(int argc, char const *argv[]) { 9 10 int fd = -1; 11 12 fd = open( "ghostwu2.txt", O_RDWR ); 13 if ( -1 == fd ) { 14 perror( "open" ); 15 return -1; 16 }else { 17 printf( "文件打開成功fd=%d\n", fd ); 18 } 19 20 //fd=0 對應stdin fd=1 對應 stdout fd=2 對應stderror 21 close( 1 ); //關閉fd=1的標準輸出之後,printf輸出看不見 22 23 int newFd = -1; 24 25 newFd = dup( fd ); //newFd一定是1, 因為分配後的fd從最小的沒被占用的開始 26 char buf[3]; 27 sprintf( buf, "%d", newFd ); //newFd轉字元串型 28 printf( "這是一段輸出,由於newFd和fd關聯到標準輸出(newFd=1),會被寫入到文件\n" ); 29 write( fd, buf, 1 ); 30 31 return 0; 32 }View Code