• 0723------Linux基础----------文件 IO 之 dup、dup2 和 fcntl 函数


    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	 fd_2 = %d
    ", fd_1, fd_2);
        char buf[1024] = {0}; //这里一定要初始化为0
        read(fd_2, buf, 1024);
        printf("buf:%s
    ", 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	 fd_2 = %d
    ", fd_1, fd_2);
    
        char buf[1024] = {0}; //从两个文件描述符表中交替读文件
        read(fd_1, buf, 3);
        printf("fd_1  buf:%s
    ", buf);
    
        memset(buf, 0, sizeof(buf));
        read(fd_2, buf, 3);
        printf("fd_2  buf:%s
    ", buf);
    
        close(fd_1); //关闭一个后另一个还能读
    
        memset(buf, 0, sizeof(buf));
        read(fd_2, buf, 3);
        printf("fd_2  buf:%s
    ", 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
    ", 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	 fd_2 = %d
    ", 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");
        }
    }
    

     

  • 相关阅读:
    Hacker's guide to Neural Networks
    Backbone Collection 源码简谈
    Backbone Model 源码简谈 (版本:1.1.0 基础部分完毕)
    Android系统架构概述
    关于 Android 程序员最近的状况
    调查:周末iPhone用户喜欢出去玩 Android喜欢宅家看电影/看书
    调查:周末iPhone用户喜欢出去玩 Android喜欢宅家看电影/看书
    Android进阶必学retrofit源码解析
    Android进阶必学retrofit源码解析
    移动互联网资料图
  • 原文地址:https://www.cnblogs.com/monicalee/p/3863879.html
Copyright © 2020-2023  润新知