0723------Linux基礎----------檔案 IO 之 dup、dup2 和 fcntl 函數

來源:互聯網
上載者:User

標籤: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");    }}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.