標籤:style blog color 使用 os strong
1. dup 函數
1.1 dup 函數用來複製一個檔案描述符,複製後的檔案描述符可以正常使用(見例1)。dup函數返回當前檔案描述符表中一個最小的可用的檔案描述符(Linux下分配檔案描述符的規則是:尋找最小可用),這個過程由系統來完成。dup函數成功執行後,兩個檔案描述符fd_1 和 fd_2 指向同一個檔案表項,因它們共用位移量(檔案資料結構圖見Unix環境進階編程),在核心中的資料結構表示為:1個進程表項,1個檔案表項(這裡兩個檔案描述符指向同一個檔案表項),1個V結點。檔案表項中有一個屬性是引用計數,調用dup後,兩個檔案描述符指向的是同一個檔案表項,因而該表項中的引用計數為2,close 一個fd 的時候引用計數減1,只有當引用計數為 0 時,該檔案表項才會被銷毀。
例1. dup 複製後的檔案描述符可以正常使用。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* * dup 的用法 複製檔案描述符 * */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int fd_1 = open("test.txt", O_RDONLY); if(fd_1 == -1){ ERR_EXIT("open"); } int fd_2 = dup(fd_1); printf("fd_1 = %d\t fd_2 = %d\n", fd_1, fd_2); char buf[1024] = {0}; //這裡一定要初始化為0 read(fd_2, buf, 1024); printf("buf:%s\n", buf); close(fd_1); close(fd_2); return 0;}
例2. dup複製後的兩個描述符共用位移量,因此二者交替讀取時,檔案內容連續。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* * dup 複製後兩個檔案描述符指向同一個檔案表項 * 因而共用檔案位移量 */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int fd_1 = open("test.txt", O_RDONLY); if(fd_1 == -1){ ERR_EXIT("open"); } int fd_2 = dup(fd_1); printf("fd_1 = %d\t fd_2 = %d\n", fd_1, fd_2); char buf[1024] = {0}; //從兩個檔案描述符表中交替讀檔案 read(fd_1, buf, 3); printf("fd_1 buf:%s\n", buf); memset(buf, 0, sizeof(buf)); read(fd_2, buf, 3); printf("fd_2 buf:%s\n", buf); close(fd_1); //關閉一個後另一個還能讀 memset(buf, 0, sizeof(buf)); read(fd_2, buf, 3); printf("fd_2 buf:%s\n", buf); close(fd_2); return 0;}
2. dup2
2.1 dup2和dup 函數功能相同,都是複製檔案描述符,但是二者的區別在於,dup由系統分配fd,而dup2由手工指定。如果dup2指定的檔案描述符已經被佔用,那麼會先關閉該fd;如果二者相等,那麼仍然返回該fd。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* * dup2用於複製時 可以手工指定被複製的fd * 如果該fd 已經被佔用,那麼要先關閉 */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int fd_1 = open("test.txt", O_RDONLY); if(fd_1 == -1){ ERR_EXIT("open"); } int fd_2 = 5; dup2(fd_1, fd_2); char buf[1024] = {0}; read(fd_2, buf, 20); printf("fd_2 buf:%s\n", buf); close(fd_1); close(fd_2); return 0;}
2.2 兩個要注意的點
a)兩個常用命令 od -c filename 以ASCII 碼顯示檔案的內容; du -h filename 顯示檔案中在磁碟中的大小,其中od 可以指定多種格式 如二進位 十六進位 浮點數 等等。
b) 數組如果不初始化為0,裡面存放的是隨機的值。因此定義的時候一定要初始化為0。
3.重新導向標準輸入輸出
3.1實現標準流重新導向是通過複製fd實現的。複製fd有三種方法:
a) dup
b) dup2
c) fcntl,設定選項為F_DUPFD
3.2 用 dup 重新導向標準輸入
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>/* * 重新導向標準輸入 */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int fd_1 = open("test.txt", O_RDONLY); if(fd_1 == -1){ ERR_EXIT("open"); } close(STDIN_FILENO); int fd_2 = dup(fd_1); //此時0 和 3 指向同一個檔案表項 //printf("fd_1 = %d\t fd_2 = %d\n", fd_1, fd_2); // fd_1 = 3 fd_2 = 0 char buf[1024] = {0}; fgets(buf, 1024, stdin); fputs(buf, stdout); close(fd_1); return 0;}
3.3 用 dup2 重新導向標準輸入
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>/* *用 dup2 重新導向標準輸入 */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int fd_1 = open("test.txt", O_RDONLY); if(fd_1 == -1){ ERR_EXIT("open"); } dup2(fd_1, STDIN_FILENO); // 這裡 首先 關閉 STDIN_FILENO , 然後將該fd 指向 fd_1 char buf[1024] = {0}; fgets(buf, 1024, stdin); fputs(buf, stdout); close(fd_1); return 0;}
3.4 用 fcntl 重新導向標準輸入
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>/* *用 fcntl 重新導向標準輸入 */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int fd_1 = open("test.txt", O_RDONLY); if(fd_1 == -1){ ERR_EXIT("open"); } close(STDIN_FILENO); int fd_2 = fcntl(fd_1, F_DUPFD, 0); char buf[1024] = {0}; fgets(buf, 1024, stdin); fputs(buf, stdout); close(fd_1); return 0;}
4. fcntl 函數
4.1 將標準輸入描述符設定為非阻塞。這裡解釋一下阻塞和非阻塞。以本例中的read函數為例,預設情況下,STDIN_FILENO是阻塞的,即如果當前沒有資料可讀時,本進程會進入睡眠狀態,一直等待,知道有資料可讀時才返回。而當我們用fcntl 將該描述符設定為非阻塞時,如果當前沒有資料可讀,read 函數會立即返回-1,並設定相應的errno值。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>/* * 設定標準輸入為非阻塞 * */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)int main(int argc, const char *argv[]){ int flag = fcntl(STDIN_FILENO, F_GETFL, 0); flag |= O_NONBLOCK; fcntl(STDIN_FILENO, F_SETFL, flag); char buf[1024] = {0}; int read_n = read(STDIN_FILENO, buf, 1024); if(read_n == -1){ ERR_EXIT("read"); } return 0;}
4.2 fcntl 的傳回值根據第二個參數的不同而不同,比如當參數為F_DUPFD時,若成功,返回新的檔案描述符;當參數為F_GETFL時,返迴文件的狀態標誌,這裡的檔案狀態標誌是O_RDONLY,O_WRONLY,O_TRUNC 等等,增加一個標誌和去掉一個標誌的方式分別為 flag |= file_flag; flag &= ~file_flag;
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>/* *將設定非阻塞 和 阻塞封裝成函數 */#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0)void set_nonblock(int fd);void set_block(int fd);int main(int argc, const char *argv[]){ set_nonblock(STDIN_FILENO); set_block(STDIN_FILENO); char buf[1024] = {0}; int read_n = read(STDIN_FILENO, buf, 1024); if(read_n == -1){ ERR_EXIT("read"); } printf("buf: %s", buf); return 0;}void set_block(int fd){ int flag = fcntl(fd, F_GETFL, 0); if(flag == -1){ ERR_EXIT("fcntl getfl"); } flag &= ~O_NONBLOCK; if(fcntl(fd, F_SETFL,flag) == -1){ ERR_EXIT("fcntl setfl"); }}void set_nonblock(int fd){ int flag = fcntl(fd, F_GETFL, 0); if(flag == -1){ ERR_EXIT("fcntl getfl"); } flag |= O_NONBLOCK; if(fcntl(fd, F_SETFL, flag) == -1){ ERR_EXIT("fcntl setfl"); }}