博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dup,dup2
阅读量:4207 次
发布时间:2019-05-26

本文共 3809 字,大约阅读时间需要 12 分钟。

dup() 和 dup2() 都可以用来复制一个现有的文件描述符,这两个函数声明如下:

#include 
int dup(int oldfd);int dup2(int oldfd, int newfd);

dup() 函数返回的新文件描述符一定是当前可用文件描述符中的最小值。


dup2() 函数可以将第 1 个参数 oldfd 指定的文件描述符复制到第 2 个参数 newfd 中,newfd 是由用户指定的一个整数,并不一定是可用文件描述符中最小的那个。需要注意的是:

1. 如果 newfd 已经打开,那么会先将其关闭。

2. 如果 oldfd 是个无效的文件描述符,则调用失败,而且 newfd 在已经打开的情形下也不会被关闭。

3. 如果 oldfd 有效,且等同于 newfd ,那么函数什么都不做,直接返回 newfd 。


在函数成功返回后,老的和新的文件描述符可以互换使用,它们共享同一个文件表项(文件偏移,文件状态标志等)。例如,如果使用 lseek() 函数在一个描述符上修改了文件偏移,那么对于另外一个描述符来说这个偏移也是同样被修改了的。


每个文件描述符都有它自己的一套文件描述符标志(注意,不是文件表项;比如 close-on-exec 文件描述符标志 FD_CLOEXEC)。


另外,复制一个描述符的另一种方法是通过 fcntl() 函数,比如 dup2(oldfd, newfd) 等同于 close(newfd); fcntl (oldfd, F_DUPFD, newfd);


测试代码

#include 
#include
#include
#include
#include
#include
#include
print_line(int n) { char buf[32]; const char *ptr = "hello"; snprintf(buf, sizeof(buf), "Line #%d\n", n); write(1, buf, strlen(buf)); } int main() { int fd; print_line(1); print_line(2); print_line(3); /*重定向标准输出 stdout 到文件 dup.out 中*/ fd = open("dup.out", O_WRONLY|O_CREAT,0666); assert(fd>=0); printf("fd=%d\n", fd); dup2(fd, 1); print_line(4); print_line(5); print_line(6); close(fd); close(1); return 0; }

程序运行以及输出结果

beyes@linux-beyes:~/C/call> ./dup.exe 
Line #1
Line #2
Line #3
fd=3
beyes@linux-beyes:~/C/call> cat dup.out 
Line #4
Line #5
Line #6

说明

在 print_line() 函数中,snprintf() 把格式好的字符串复制到 buf 指向的空间中;然后用 write 把 buf 缓冲区中的数据写到标准输出。

27行:经过 dup2() 函数后,标准输出就成为了 fd 的一个副本,也就是说,操作标准输出描述符 1 也就相当于操作到了 fd 上( 新旧文件描述符指向同一个文件,共享所有的锁定、读写指针和各项权限或标志位)。效果如像上面所示,标准输出被的重定向到文件中去了。

dup 与 管道
dup 与 管道
#include 
#include
#include
#include
#include
#include
int main(int argc, char **argv, char **environ){ pid_t pid; int fd[2]; if (pipe(fd)) { printf("create pipe failed!\n"); exit(1); } pid = fork(); switch(pid) { case -1: printf("fork error!\n"); exit(1); case 0: close(fd[0]); close(1); dup(fd[1]); sleep(5); system("ls -l"); exit(0); default: close(fd[1]); char message[4096]; read(fd[0], message, 4096); printf("hello world\n"); printf("%", message); } return 0;}
说明
上面程序的目的是子进程通过管道向父进程发送 "ls -l" 命令输出的内容。因为管道为半双工通信,而现在的情形是子进程写,父进程读,所以在子进程里需要先将 fd[0] 读端关闭,而在父进程里要将 fd[1] 写端关闭。因为 system() 函数执行的 "ls -l" 命令后的内容是向标准输出,而我们又要将标准输出的内容输出到管道中,所以这里要进行标准输出重定向。重定向的办法就是使用 dup() 函数为标准输出建立一个副本。因此,在子进程中,我们先将标准输出的描述符 1 关掉,此时最小可用的描述符自然为 1,所以 dup(fd[1]) 后,子进程的管道写入端 fd[1] 成为了标准输出的副本。这样,写标准输出,也就是写管道。当然,也可以使用 dup2() 来完成,如 dup2(fd[1], 1);
程序执行输出如下:
beyes@linux-beyes:~/C/pipe> ./dup2.exe 
hello world
总计 84
-rw-r--r-- 1 beyes users  1446 07-23 17:38 dual_pipe.c
-rwxr-xr-x 1 beyes users 12407 07-23 17:38 dual_pipe.exe
-rw-r--r-- 1 beyes users   736 07-23 22:03 dup2.c
-rwxr-xr-x 1 beyes users 11932 07-23 22:03 dup2.exe
-rw-r--r-- 1 beyes users   769 07-22 01:40 pipe.c
-rwxr-xr-x 1 beyes users 12266 07-22 01:41 pipe.exe
-rw-r--r-- 1 beyes users   153 07-22 01:17 strlen.c
-rwxr-xr-x 1 beyes users 11373 07-22 01:17 strlen.exe
在子进程中睡眠 5 秒和在父进程中故意输出 hello world ,这是为了测试管道读写的阻塞性质。由于建立了管道,在子进程里如果没有向标准输出内容(已经调用 dup() 后)或者子进程没有退出,那么父进程会被阻塞,直到能从管道里读到些什么东西为止。如果子进程并没有向管道里写东西,而是直接退出了,那么父进程检测到子进程已经退出,则管道不再阻塞,直接从自定义的缓冲区里读些东西(遇到‘\0‘为止)也就跟着退出了。

如果将上面程序的子进程的
close(1);
dup(fd[1]);
改成:
dup2(1, fd[1]);
会出现什么情况呢(注意,不是 dup2(fd[1], 1)? 在这种方式下,我们错误的进行描述符复制,也就是使得标准输出描述符 1 变成了管道描述符 fd[1] 的一个副本。在这种情况下,父进程会先打印出 hello world 字串,然后调用 read() 试图去读管道(此时子进程睡眠),但实际上它读取的是子进程的标准输出,然而睡眠中的子进程并不会有什么输出,所以父进程在无堵塞的情况下推出了!注意,默认情况下,读标准输出是不会阻塞的,而读管道则会堵塞。当父进程退出,子进程则变成了孤儿进程,但是它马上会找到新的父亲,即 pid 为 1 的 init 进程。这个过程可以在程序的父进程程序段通过 getpid() 和在子进程程序段使用 getppid() 的打印看到。在被 init 领养后,它接着执行 system() 函数,然后 exit() 退出。这里有个小问题,就是在父进程退出时,出现终端提示符(如 [beyes@localhost syscall]$),但接着被子进程的 "ls -l" 输出给冲洗掉,此时需要再按一下回车换行才能刷出新的中断提示符。

转载地址:http://tvlli.baihongyu.com/

你可能感兴趣的文章
收缩数据库
查看>>
数据库修复
查看>>
找出执行时间最长的10条SQL
查看>>
主键就是聚集索引吗
查看>>
一、存储结构   在SQL Server中,有许多不同的可用排列规则选项。   二进制:按字符的数字表示形式排序(ASCII码中,用数字32表示空格,用68表示字母"D")。因为所有内容都表示为数字
查看>>
SqlServer2008数据备份以及远程备份
查看>>
SQL Server 非聚集索引的覆盖,连接,交叉和过滤 (第二篇)
查看>>
备份恢复
查看>>
sqlserver 备份
查看>>
sqlserver语句总结
查看>>
informatica
查看>>
sqlserver 文章
查看>>
test
查看>>
SQLSERVER排查CPU占用高的情况
查看>>
Informatica中实现count(distinct)
查看>>
Informatica Powercenter调优
查看>>
增量聚集
查看>>
INCREMENTAL AGGREGATION IN INFORMATICA
查看>>
INFORMATICA1
查看>>
INFORMATICA2
查看>>