dup,dup2

2024-03-31 08:58
文章标签 dup2 dup

本文主要是介绍dup,dup2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

#include <unistd.h>
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 <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <assert.h>#include <string.h>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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>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" 输出给冲洗掉,此时需要再按一下回车换行才能刷出新的中断提示符。

这篇关于dup,dup2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863950

相关文章

linux dup函数源码剖析

这是我新的专栏的第一篇,其实我这种水平的人还写专栏,实在是没有自知之明,夜郎自大。这个专栏只是希望起到一个抛砖引玉的作用,欢迎大家对我的文章多提宝贵的意见。 好了,先说说我为什么要写这样一个专栏,其实研究源码的朋友都应该经历过这个过程,特别是研究linux内核源码的朋友,一开始可能都是从几本经典的讲解内核的书籍开始,什么《linux内核设计与实现》、《深入理解linux内核》,但这几本书看下来还

(P7-P8)文件与IO:文件共享、dup、fcntl

文章目录 1.文件共享2.复制文件描述符 1.文件共享 打开文件内核数据结构 (1)内核中如何表示打开的文件的? 每个进程都有1个文件描述符表; refcnt表示文件当前的引用计数; stat状态返回的信息都保存在v节点信息上面; i节点信息保存了文件存放在磁盘的哪个位置; 一个进程两次打开同一个文件 一个进程两次打开同一个文件,V节点信息是共享的; 文件状态标志是不一样

[Linux系统编程]文件重定向dup和dup2

一.dup和dup2 实现重定向 1.文件描述符表 操作系统在管理文件时,会管理一张文件描述符表,根据打开的文件,分配一个文件描述符(int),根据该文件描述符,锁定指向该文件的指针,从而索取文件。 2.重定向 在linux中,可以使用**> 和 >>命令**来实现重定向,将控制台内容输入到文件中,它的具体实现就是dup函数。 实际操作中,用到的最多是dup2(int oldfd ,int

文件系统的dup和dup2函数

dup和dup2函数 #include <unistd.h>int dup(int oldfd);int dup2(int oldfd, int newfd); dup和dup2都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个file结构体。如果两个文件描述符指向同一个file结构体,File Status Flag和读写位置只保存一份在file结构体中,并且file结构体的引

Linux 函数之 dup 和 dup2

一、概述:    dup 和 dup2 是复制文件描述符的函数,显而易见,这俩兄弟的前缀是 duplicate 的缩写,即复制; 二、头文件: #include <unistd.h> 三、函数形式: int dup(int oldfd);int dup2(int oldfd, int newfd); 四、功能描述: (1)dup( ) :生成一个未使用的最小的文件描述符,相当于两个

文件IO-使用dup2实现错误日志功能及判断文件权限,并终端输出

1:使用 dup2 实现错误日志功能                 使用 write 和 read 实现文件的拷贝功能,注意,代码中所有函数后面,紧跟perror输出错误信息,要求这些错误信息重定向到错误日志 err.txt 中去 代码:   #include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd

4.5 代码段、数据段、栈段,dup指令

4.5 代码段、数据段、栈段,dup指令 1. 代码段 1.1 MASM内部以数据位的个数定义了多种数据类型 byte,db,8位(define byte)word,dw,16位(define word)dword,dd,32位(define double word)qword,dq,64位(define,quadra word) 1.2 dw和db 用dw往内存里面存数据时,每一个数占

Linux开发:dup, dup2, dup3

Linux开发:open打开文件-CSDN博客 Linux开发:多进程通过open同时读取文件-CSDN博客 介绍了打开一个文件会获得一个文件描述符,该文件描述符指向内核中打开文件的描述表的一个位置,而该位置记录了当前打开文件的一些信息 dup系列api的主要用途是为指定的文件描述符复制/指定一个新的描述符,使得新旧文件描述符指向内核中打开文件的描述表的同一个位置。因此新旧文件描述符会共

dup源码分析

dup源码 fs/file.c 源码分析 实际是在寻找一个新的描述符然后将这个描述符只想待描述的文件file 对象上面 file结构体 需要关注的是f_pos

LabVIEW开发DUP实时监控系统

LabVIEW开发DUP实时监控系统 该项目采用虚拟仪器设计理念,以LabVIEW作为核心技术平台,开发了一套磁控溅射过程的实时监控系统。实现过程中关键参数的全面数据采集与处理,建立完整的历史数据库,以支持涂层技术的改进和系统向模糊控制的最终进化。 项目背景 真空镀膜技术是表面工程领域的一种创新材料合成与处理方法,涉及通过物理和化学手段在固体表面施加具有特殊性能的涂层,以增强表面的耐磨、耐高