复制文件描述符的dup2 函数
dup2 是 Unix/Linux 系统中用于复制文件描述符的函数,它允许将一个文件描述符复制到另一个文件描述符上,实现输入输出流的重定向操作,比如将stdout重定向到一个文件,亦或者是把一个文件作为输入来对待。
函数原型与头文件
include
int dup2(int oldfd, int newfd);
参数说明
oldfd:需要复制的源文件描述符(比如已经打开的文件、管道、套接字等)。
newfd:目标文件描述符,用于接收oldfd的副本或者重定向的描述符。若newfd已打开,系统会先关闭它,再进行复制。
返回值
成功时:返回新的文件描述符(即newfd,若newfd之前已关闭则可能返回其他值)。
失败时:返回-1,并设置errno错误码(如EBADF表示oldfd无效,EMFILE表示文件描述符耗尽)。
核心功能与原理
dup2 的核心作用是让newfd成为oldfd的副本,即让两个文件描述符指向同一个文件表项,从而共享文件偏移量和状态标志。例如:
将newfd重定向为oldfd的副本后,对newfd的读写操作等同于对oldfd的操作。
常见场景是重定向标准输入(STDIN_FILENO=0)、标准输出(STDOUT_FILENO=1)、标准错误(STDERR_FILENO=2)。
示例1:重定向标准输出到文件
下面的这个程序会将原本输出到终端的重定向输出到一个文件。
/**
- @file linux-dup2-to-file.c
- @brief redirect stdout to file
*/
include
include
include
include
int main() {
int fd, old_stdout;
char *filename = “dup2-to-file-output.txt”;
// keep the original stdout for restore
old_stdout = dup(STDOUT_FILENO);
if (old_stdout == -1) {
perror(“dup failed”);
exit(EXIT_FAILURE);
}
// open the file for writing
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror(“open failed”);
exit(EXIT_FAILURE);
}
// redirect stdout to file
if (dup2(fd, STDOUT_FILENO) == -1) {
perror(“dup2 failed”);
exit(EXIT_FAILURE);
}
close(fd); // close fd, because STDOUT_FILENO is now pointing to it
// now printf’s output will be written to file
printf(“This text will be written to %s\n”, filename);
printf(“Another line for testing…\n”);
// Remember to flush the buffer otherwise the output will be buffered
fflush(stdout);
// restore original stdout
if (dup2(old_stdout, STDOUT_FILENO) == -1) {
perror(“dup2 restore failed”);
exit(EXIT_FAILURE);
}
// close the backup descriptor
close(old_stdout);
// after restore, output to terminal
printf(“Now back to terminal output\n”);
return 0;
}
编译使用的方法如下所示:
$ gcc -o linux-dup2-to-file linux-dup2-to-file.c
$ ./linux-dup2-to-file
Now back to terminal output
$ cat dup2-to-file-output.txt
This text will be written to dup2-to-file-output.txt
Another line for testing…
需要注意的是,在执行完操作后,需要调用fflush(stdout)强制刷新,不然可能不是你希望的现象。
示例2:重定向标准输入从文件
下面的这个程序会将文件的内容作为终端输入的信息。
/**
- @file linux-dup2-from-file.c
- @brief redirect stdin from file
*/
include
include
include
include
include
int main() {
int fd, old_stdin;
char *filename = “dup2-from-file-input.txt”;
char buffer[1024];
// keep the original stdin for restore
old_stdin = dup(STDIN_FILENO);
if (old_stdin == -1) {
perror(“dup failed”);
exit(EXIT_FAILURE);
}
// open the file for reading
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror(“open failed”);
exit(EXIT_FAILURE);
}
// redirect stdin to file
if (dup2(fd, STDIN_FILENO) == -1) {
perror(“dup2 failed”);
exit(EXIT_FAILURE);
}
close(fd); // close fd, because STDIN_FILENO is now pointing to it
// now scanf will read data from file
printf(“Reading from %s:\n”, filename);
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
printf(“[Read] : %s”, buffer);
}
fflush(stdin);
// restore original stdin
if (dup2(old_stdin, STDIN_FILENO) == -1) {
perror(“dup2 restore failed”);
exit(EXIT_FAILURE);
}
close(old_stdin);
// after restore, input from terminal
printf(“\n\n[Terminal] Enter something: “);
strcpy(buffer, “hello world”);
printf(“\n[Terminal] You entered: %s\n”, buffer);
fflush(stdout);
return 0;
}
编译使用的方法如下所示:
$ cat dup2-from-file-input.txt
Roses are red,
Violets are blue,
Testing dup2,
And stdin too.%
$ gcc -o linux-dup2-from-file linux-dup2-from-file.c
$ ./linux-dup2-from-file
Reading from dup2-from-file-input.txt:
[Read] : Roses are red,
[Read] : Violets are blue,
[Read] : Testing dup2,
[Read] : And stdin too.
[Terminal] Enter something:
[Terminal] You entered: hello world
与dup函数的区别
dup 函数(int dup(int oldfd))也是用于复制文件描述符,但两者还是有一些区别的。比如dup 返回最小的未使用文件描述符,而dup2可以指定目标描述符newfd。
dup2(newfd, oldfd)
close(newfd);
return dup(oldfd);
应用场景
这个函数主要用在比如,IO重定向(类似于shell的> file)、管道通信(父子进程的通信)、日志系统(同时写到终端和日志文件)或者网络编程中(将网络套接字重定向到输入和输出)。
注意事项
文件描述符关闭顺序:重定向时若newfd已打开,dup2会先关闭它,可能导致数据丢失。
原子性:dup2是原子操作,无需担心多线程环境下的竞争条件。
资源管理:复制的描述符共享文件表项,需确保正确关闭(避免文件描述符泄漏)。
错误处理:始终检查dup2的返回值,处理可能的错误(如文件不存在、权限不足)。
通过合理使用dup2,可以灵活控制程序的输入输出流向,实现复杂的系统编程需求。
在实际开发中,它常与管道、套接字等结合使用,是构建进程间通信和重定向机制的核心工具。
声明:来自我们编程吧,仅代表创作者观点。链接:https://eyangzhen.com/1945.html