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\n",
                   (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\n", r);
    }

    r = dup2(100, 100);
    if(r == -1) {
        perror("dup2(100, 100) fail: ");
    }
    else {
        printf("dup2(100, 100) success return %d\n", r);
    }

    r = dup2(1, 10000);
    if(r == -1) {
        perror("dup2(1, 10000) fail: ");
    }
    else {
        printf("dup2(1, 10000) success return %d\n", r);
    }

    r = dup2(1, 100);
    if(r == -1) {
        perror("dup2(1, 100) fail: ");
    }
    else {
        printf("dup2(1, 100) success return %d\n", 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\n",
                   (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\n", max_fd);

    r = dup2(1, 10000);
    if(r == -1) {
        perror("dup2(1, 10000) fail: ");
    }
    else {
        printf("dup2(1, 10000) success return %d\n", r);
    }

    r = dup2(1, 100);
    if(r == -1) {
        perror("dup2(1, 100) fail: ");
    }
    else {
        printf("dup2(1, 100) success return %d\n", 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)\n");
            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)\n");
                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\n",
                   (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\n", r);
    }

    r = dup2_(100, 100);
    if(r == -1) {
        perror("dup2_(100, 100) fail: ");
    }
    else {
        printf("dup2_(100, 100) success return %d\n", r);
    }

    r = dup2_(1, 7168);
    if(r == -1) {
        perror("dup2_(1, 7168) fail: ");
    }
    else {
        printf("dup2_(1, 7168) success return %d\n", r);
    }

    r = dup2_(1, 7167);
    if(r == -1) {
        perror("dup2_(1, 7167) fail: ");
    }
    else {
        printf("dup2_(1, 7167) success return %d\n", r);
    }

    r = dup2_(2, 2);
    if(r == -1) {
        perror("dup2_(2, 2) fail: ");
    }
    else {
        printf("dup2_(2, 2) success return %d\n", r);
    }
    while((r = dup(0))!= -1)
    {
        max_fd = r;
    }
    perror(NULL);
    printf("max fd is %d\n", max_fd);

    r = dup2_(1, 7168);
    if(r == -1) {
        perror("dup2_(1, 7168) fail: ");
    }
    else {
        printf("dup2_(1, 7168) success return %d\n", r);
    }

    r = dup2_(1, 7167);
    if(r == -1) {
        perror("dup2_(1, 7167) fail: ");
    }
    else {
        printf("dup2_(1, 7167) success return %d\n", r);
    }

    r = dup2_(2, 2);
    if(r == -1) {
        perror("dup2_(2, 2) fail: ");
    }
    else {
        printf("dup2_(2, 2) success return %d\n", 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

结果都符合预期

原文地址:https://www.cnblogs.com/logchen/p/12056351.html

时间: 2024-10-04 17:48:13

UNIX环境高级编程APUE练习3.2-不用fcntl实现dup2的功能的相关文章

Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字 . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APU

(十二) 一起学 Unix 环境高级编程 (APUE) 之 进程间通信(IPC)

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

(十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

(十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

(九) 一起学 Unix 环境高级编程 (APUE) 之 线程

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

(七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程

. . . . . 目录 (一) 一起学 Unix 环境高级编程(APUE) 之 标准IO (二) 一起学 Unix 环境高级编程(APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程(APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程(APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程(APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程(APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系

(六) 一起学 Unix 环境高级编程(APUE) 之 进程控制

. . . . . 目录 (一) 一起学 Unix 环境高级编程(APUE) 之 标准IO (二) 一起学 Unix 环境高级编程(APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程(APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程(APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程(APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程(APUE) 之 进程控制 上一篇博文中我们讨论了进程环境,相信大家对进程已经有了初步的认识

(十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

. . . . . 之前我们在创建线程的时候都是使用的默认属性,本章主要讨论的是自定义线程的属性. 使用默认属性基本上能解决掉遇到的大部分问题,所以自定义属性在实际项目中用得比较少. 1.线程属性 <APUE>第三版 P341 表中的属性可以用来限定一个进程能创建线程的最大数量,但是限定线程数量的宏不必太当真,因为在上一篇博文中我们说过了一个线程能创建的线程的数量是受很多因素影响的,并非一定是以这几个宏值为准的. 线程属性使用 pthread_attr_t 类型表示. 1 #include &

UNIX环境高级编程APUE练习4.6-实现类似cp(1)的程序,保留文件中的空洞

1 题面 编写类似cp(1)的程序,它复制包含空洞的文件,但是不将字节0写到输出文件中去. 2 基本思路 首先要搞清楚空洞的性质以判断一个文件是否有空洞,以及空洞的位置 知道了空洞的位置之后,读到源文件中的空洞部分时,在目标文件中lseek相应的长度 3 创建空洞文件,同时探索空洞性质 交替lseek和write,逐渐增大间隔长度.比较文件的大小和实际占用的block数目 测试源码 #include <stdio.h> #include <fcntl.h> #include <