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

结果都符合预期

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

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

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  2. (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  3. (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  4. (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  5. (四) 一起学 Unix 环境高级编程(APUE) 之 系统数据文件和信息

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  6. (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

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

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

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

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  9. (八) 一起学 Unix 环境高级编程 (APUE) 之 信号

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

随机推荐

  1. Java核心基础第3篇-Java流程控制

    Java流程控制 本章一起来探讨下Java的流程控制语句.主要从以下几个方面展开: Java分支语句 Java循环语句 Java其实和其他任何的开发语言一样,分支语句和循环语句是必不可少的,有个这两个 ...

  2. rabbitmq概念简介

    AMQP协议 AMQP: Advanced Message Queue,高级队列协议. 特征: 这是一个在进程间传递异步消息的网络协议,因此数据的发送方.接收方以及容器(MQ)都可以在不同的设备上. ...

  3. Flask(9)- 蓝图的基本使用

    前言 在前面的例子中,所有的页面处理逻辑都是放在同一个文件中,随着业务代码的增加,将所有代码都放在单个程序文件中是非常不合适的 不仅会让阅读代码变得困难,而且会给后期维护带来麻烦 Flask 中使用蓝 ...

  4. gitlab用户,组,项目权限管控

    前言:gitlab上的权限管控是非常重要的,尤其是很多研发人员开发一个项目.这个是我总结的权限管控. 1.这个是创建项目时开放权限设置   2.这个创建用户设置的权限   3.用户权限,5种类型用户是 ...

  5. nmcli device and nmcli connection

    NetworkManager是RHEL 7之后推出的有效管理网络的服务 NetworkManager通过工具nmcli来管理网卡 网卡属于物理硬件,NetworkManager属于软件层面,如何通过软 ...

  6. C语言:Unicode字符集

    Unicode 也称为统一码.万国码:看名字就知道,Unicode 希望统一所有国家的字符编码.Unicode 于 1994 年正式公布第一个版本,现在的规模可以容纳 100 多万个符号,是一个很大的 ...

  7. 两人团队项目-石家庄地铁查询系统(web版)

    大二上学期做过只有两号线的地铁查询系统,但是只能在控制台操作.这一次将线路加到了六条,并且要求web实现,下面简述一下设计思路和具体代码实现: 1.数据库建表 于我自己习惯而言,我写javaweb项目 ...

  8. React组件三大属性之 refs

    React组件三大属性之 refs refs属性 1) 组件内的标签都可以定义ref属性来标识自己 a. <input type="text" ref={input => ...

  9. Java基础之反射生成JDK动态代理

    在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口.通过这个类和接口可以生成JDK动态代理类或动态代理对象. JDK动态代理例子: / ...

  10. Leetcode春季活动打卡第三天:面试题 10.01. 合并排序的数组

    Leetcode春季活动打卡第三天:面试题 10.01. 合并排序的数组 Leetcode春季活动打卡第三天:面试题 10.01. 合并排序的数组 思路 这道题,两个数组原本就有序.于是我们采用双指针 ...