zoukankan      html  css  js  c++  java
  • UNIX环境高级编程APUE练习3.2-不用fcntl实现dup2的功能

    1 题面

    编写与dup2功能相同的函数,要求不调用fcntl函数,并且要有正确的出错处理。

    2 基本思路

    不能用fcntl,能够返回一个文件描述符的只有open和dup。而open会创建一个新的文件表项,返回的fd指向新的文件表项,与dup2的表现不符。dup基本能满足要求,但是返回的是最小的可用fd,需要进一步操作满足要求。另外需要自己添加错误处理,以及处理oldfd与newfd相等的情况等。具体地,

    1. 当dup返回出错时,直接返回出错
    2. 当dup返回值等于newfd时,直接返回
    3. 当dup返回值小于newfd时,记录返回值,循环调用dup直到返回值等于newfd。关闭前面记录的所有fd,返回newfd
    4. 当dup返回值大于newfd时,关闭返回值的fd。如果oldfd等于newfd,直接返回newfd;如果不相等,关掉newfd,然后再dup(因为不是原子的,返回值需要再判断)

    3 出错处理

    1. oldfd的出错处理可以直接交给dup
    2. newfd的出错处理,需要判断是否超出文件描述符范围(RLIMIT_NOFILE in getrlimit)
    3. 对于dup返回EMFILE的情况,newfd如果没超过进程可打开的最大文件数,则不影响
    4. 另外还有一个判断顺序问题,是先判断参数是否合法还是oldfd==newfd, 这个可以根据dup2函数实测来确定

    4 测试用例

    进程打开的文件数没满的情况下

    1. 都超出范围,相同(MAX+1,MAX+1)
    2. 未打开描述符,相同 (100, 100)
    3. newfd超出范围 (1, MAX+1)
    4. newfd正好没超出 (1, MAX)
    5. oldfd和newfd相同 (2, 2)

    进程打开的文件数满的情况下

    1. newfd正好超出范围 (1, MAX+1)
    2. newfd正好没超出 (1, MAX)
    3. oldfd和newfd相同 (2, 2)

    5 开始撸码实测

    5.1 先验证dup2的判断顺序问题

    • 测试源码
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/resource.h>
    
    int main()
    {
        int r;
        struct rlimit old_rlim={0};
        getrlimit(RLIMIT_NOFILE, &old_rlim);
        printf("NOFILE limits: soft=%lld; hard=%lld
    ",
                       (long long) old_rlim.rlim_cur, (long long) old_rlim.rlim_max);
    
        r = dup2(10000, 10000);
        if(r == -1) {
            perror("dup2(10000, 10000) fail: ");
        }
        else {
            printf("dup2(10000, 10000) success return %d
    ", r);
        }
    
        r = dup2(100, 100);
        if(r == -1) {
            perror("dup2(100, 100) fail: ");
        }
        else {
            printf("dup2(100, 100) success return %d
    ", r);
        }
    
        r = dup2(1, 10000);
        if(r == -1) {
            perror("dup2(1, 10000) fail: ");
        }
        else {
            printf("dup2(1, 10000) success return %d
    ", r);
        }
    
        r = dup2(1, 100);
        if(r == -1) {
            perror("dup2(1, 100) fail: ");
        }
        else {
            printf("dup2(1, 100) success return %d
    ", r);
        }
    
        return 0;
    }
    
    • MAC OSX下运行结果
    ^_^$ ./a.out
    NOFILE limits: soft=7168; hard=9223372036854775807
    dup2(10000, 10000) fail: : Bad file descriptor
    dup2(100, 100) fail: : Bad file descriptor
    dup2(1, 10000) fail: : Bad file descriptor
    dup2(1, 100) success return 100
    

    可见是参数出错判断是先于oldfd == newfd判断的

    5.2 测试进程打开的最大文件数到上限时,dup2是否能成功

    • 测试源码
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/resource.h>
    
    int main()
    {
        int r;
        int max_fd = 0;
        struct rlimit old_rlim={0};
        getrlimit(RLIMIT_NOFILE, &old_rlim);
        printf("NOFILE limits: soft=%lld; hard=%lld
    ",
                       (long long) old_rlim.rlim_cur, (long long) old_rlim.rlim_max);
        while((r = dup(0))!= -1)
        {
            max_fd = r;
        }
        perror(NULL);
        printf("max fd is %d
    ", max_fd);
    
        r = dup2(1, 10000);
        if(r == -1) {
            perror("dup2(1, 10000) fail: ");
        }
        else {
            printf("dup2(1, 10000) success return %d
    ", r);
        }
    
        r = dup2(1, 100);
        if(r == -1) {
            perror("dup2(1, 100) fail: ");
        }
        else {
            printf("dup2(1, 100) success return %d
    ", r);
        }
    
        return 0;
    }
    
    • MAC OSX下运行结果
    ^_^$ ./a.out
    NOFILE limits: soft=7168; hard=9223372036854775807
    Too many open files
    max fd is 7167
    dup2(1, 7168) fail: : Bad file descriptor
    dup2(1, 7167) success return 7167
    

    可见在进程打开文件数达到上限时,dup2替换已经打开的文件是可以的

    5.3 实现dup2的功能

    • 源码
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/resource.h>
    #include <sys/errno.h>
    
    /*dup实现dup2的功能*/
    int dup2_(int oldfd, int newfd) {
        int ret;
        int stack[7168];
        int count = 0;
        struct rlimit old_rlim={0};
        getrlimit(RLIMIT_NOFILE, &old_rlim);
        if (newfd < 0 || newfd > old_rlim.rlim_cur - 1) {
            errno = EBADF;
            return -1;
        }
        while(1) {
            ret = dup(oldfd);
            if(ret == -1 && errno != EMFILE) {
                break;
            }
            else if(ret == -1 && errno == EMFILE) {
                if(oldfd == newfd) {
                    return newfd;
                }
                printf("close(newfd)
    ");
                close(newfd);
            }
            else {
                if(oldfd == newfd) {
                    close(ret);
                    return newfd;
                }
                if(ret == newfd) {
                    break;
                }
                else if(ret < newfd) {
                    stack[count++] = ret;
                }
                else {
                    close(ret);
                    printf("close(newfd)
    ");
                    close(newfd);
                }
            }
        }
        while(count) {
            close(stack[--count]);
        }
        return ret;
    }
    
    int main()
    {
        int r, max_fd;
        struct rlimit old_rlim={0};
        getrlimit(RLIMIT_NOFILE, &old_rlim);
        printf("NOFILE limits: soft=%lld; hard=%lld
    ",
                       (long long) old_rlim.rlim_cur, (long long) old_rlim.rlim_max);
    
        r = dup2_(7168, 7168);
        if(r == -1) {
            perror("dup2_(7168, 7168) fail: ");
        }
        else {
            printf("dup2_(7168, 7168) success return %d
    ", r);
        }
    
        r = dup2_(100, 100);
        if(r == -1) {
            perror("dup2_(100, 100) fail: ");
        }
        else {
            printf("dup2_(100, 100) success return %d
    ", r);
        }
    
        r = dup2_(1, 7168);
        if(r == -1) {
            perror("dup2_(1, 7168) fail: ");
        }
        else {
            printf("dup2_(1, 7168) success return %d
    ", r);
        }
    
        r = dup2_(1, 7167);
        if(r == -1) {
            perror("dup2_(1, 7167) fail: ");
        }
        else {
            printf("dup2_(1, 7167) success return %d
    ", r);
        }
    
        r = dup2_(2, 2);
        if(r == -1) {
            perror("dup2_(2, 2) fail: ");
        }
        else {
            printf("dup2_(2, 2) success return %d
    ", r);
        }
        while((r = dup(0))!= -1)
        {
            max_fd = r;
        }
        perror(NULL);
        printf("max fd is %d
    ", max_fd);
    
        r = dup2_(1, 7168);
        if(r == -1) {
            perror("dup2_(1, 7168) fail: ");
        }
        else {
            printf("dup2_(1, 7168) success return %d
    ", r);
        }
    
        r = dup2_(1, 7167);
        if(r == -1) {
            perror("dup2_(1, 7167) fail: ");
        }
        else {
            printf("dup2_(1, 7167) success return %d
    ", r);
        }
    
        r = dup2_(2, 2);
        if(r == -1) {
            perror("dup2_(2, 2) fail: ");
        }
        else {
            printf("dup2_(2, 2) success return %d
    ", r);
        }
    
        return 0;
    }
    
    • MAC OSX下的运行结果
    NOFILE limits: soft=7168; hard=9223372036854775807
    dup2_(7168, 7168) fail: : Bad file descriptor
    dup2_(100, 100) fail: : Bad file descriptor
    dup2_(1, 7168) fail: : Bad file descriptor
    dup2_(1, 7167) success return 7167
    dup2_(2, 2) success return 2
    Too many open files
    max fd is 7167
    dup2_(1, 7168) fail: : Bad file descriptor
    close(newfd)
    dup2_(1, 7167) success return 7167
    close(newfd)
    dup2_(1, 100) success return 100
    dup2_(2, 2) success return 2
    

    结果都符合预期

  • 相关阅读:
    一条长为L的绳子,一面靠墙,另外三边组成矩形,问此矩形最大面积能是多少?
    幸运的背后,总是靠自身的努力在支撑
    ZT:没有谁的成功是横空出世
    Node.js abaike图片批量下载爬虫1.02
    Node.js nvshens图片批量下载爬虫1.01
    Node.js meitulu图片批量下载爬虫1.051
    JDBC学习再小结
    JDBC学习小结
    day06_JDBC学习笔记
    MySQL学习小结
  • 原文地址:https://www.cnblogs.com/logchen/p/12056351.html
Copyright © 2011-2022 走看看