UNIX环境高级编程APUE练习3.2-不用fcntl实现dup2的功能
1 题面
编写与dup2功能相同的函数,要求不调用fcntl函数,并且要有正确的出错处理。
2 基本思路
不能用fcntl,能够返回一个文件描述符的只有open和dup。而open会创建一个新的文件表项,返回的fd指向新的文件表项,与dup2的表现不符。dup基本能满足要求,但是返回的是最小的可用fd,需要进一步操作满足要求。另外需要自己添加错误处理,以及处理oldfd与newfd相等的情况等。具体地,
- 当dup返回出错时,直接返回出错
- 当dup返回值等于newfd时,直接返回
- 当dup返回值小于newfd时,记录返回值,循环调用dup直到返回值等于newfd。关闭前面记录的所有fd,返回newfd
- 当dup返回值大于newfd时,关闭返回值的fd。如果oldfd等于newfd,直接返回newfd;如果不相等,关掉newfd,然后再dup(因为不是原子的,返回值需要再判断)
3 出错处理
- oldfd的出错处理可以直接交给dup
- newfd的出错处理,需要判断是否超出文件描述符范围(RLIMIT_NOFILE in getrlimit)
- 对于dup返回EMFILE的情况,newfd如果没超过进程可打开的最大文件数,则不影响
- 另外还有一个判断顺序问题,是先判断参数是否合法还是oldfd==newfd, 这个可以根据dup2函数实测来确定
4 测试用例
进程打开的文件数没满的情况下
- 都超出范围,相同(MAX+1,MAX+1)
- 未打开描述符,相同 (100, 100)
- newfd超出范围 (1, MAX+1)
- newfd正好没超出 (1, MAX)
- oldfd和newfd相同 (2, 2)
进程打开的文件数满的情况下
- newfd正好超出范围 (1, MAX+1)
- newfd正好没超出 (1, MAX)
- 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的功能的更多相关文章
- (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (四) 一起学 Unix 环境高级编程(APUE) 之 系统数据文件和信息
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (八) 一起学 Unix 环境高级编程 (APUE) 之 信号
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
随机推荐
- 关于 C#的一些记录
1, 注意: 使用Linq to Sql 查询数据库的时候,进行where 判断需要注意.我使用的EF,以下为我的记录使用Contain 需要 使用 *.Contains("" + ...
- Java:Java实例化(new)过程
实例化过程(new) 1.首先去JVM 的方法区中区寻找类的class对象,如果能找到,则按照定义生成对象,找不到 >>如下2.所示 2.加载类定义:类加载器(classLoader)寻找 ...
- Springboot:单元测试日志打印@Slf4j 注解的使用方法
当自己写日志的时候,肯定需要: private final Logger logger = LoggerFactory.getLogger(LoggerTest.class); 每次写新的类,就需要重 ...
- mybatis源码简单分析
mybatis入门介绍 /** * 1. 接口式编程 * 原生: Dao =====> DaoImpl * mybatis : Mapper =====> xxxMapper * 2. S ...
- XCTF Hello CTF
一.查壳 二.拖入ida x86静态分析 shift +F12找到字符串. 发现关键字please input your serial 点击进入 这是我们的主函数,F5反编译一下,看一下逻辑. 这里有 ...
- dart pub上传失败如何解决
问题: Flutter Exception: Pub will wait for a while before trying to connect again. 解决 1.设置终端代理 export ...
- YARN调度器(Scheduler)详解
理想情况下,我们应用对Yarn资源的请求应该立刻得到满足,但现实情况资源往往是有限的,特别是在一个很繁忙的集群,一个应用资源的请求经常需要等待一段时间才能的到相应的资源.在Yarn中,负责给应用分配资 ...
- mysql - 按条件统计
在表中分别统计mt =0 和 mt>0 的个数 方法一:select count(if(mt=0,1,null)) as a,count(if(mt>0,1,null)) as b fro ...
- 【洛谷P1816 忠诚】线段树
题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意.但是由于一些人的挑拨,财主还是对管家产生了 ...
- javascript学习笔记-(三)
ES6标准新增了一种新的函数:Arrow Function(箭头函数) 案例: 为什么叫Arrow Function?因为它的定义用的就是一个箭头: x => x * x 上面的箭头函数相当于: ...