0%

你或他是否可读写 chmod

dup2 函数的使用详解

dup2 是 Unix/Linux 系统中用于复制文件描述符的函数,它允许将一个文件描述符复制到另一个文件描述符上,实现输入输出流的重定向操作,比如将stdout重定向到一个文件。

函数原型与头文件

1
2
3
#include <unistd.h>

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* @file linux-dup2-to-file.c
* @brief redirect stdout to file
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

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;
}

编译使用的方法如下所示:

1
2
3
4
5
6
7
8
$ 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)强制刷新,不然可能不是你希望的现象。

使用示例:重定向标准输入从文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
int fd, old_stdin;
char *filename = "input.txt";
char buffer[1024];

// 1. 保存原始标准输入描述符
old_stdin = dup(STDIN_FILENO);
if (old_stdin == -1) {
perror("dup failed");
exit(EXIT_FAILURE);
}

// 2. 打开文件用于读取
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}

// 3. 将标准输入重定向到文件
if (dup2(fd, STDIN_FILENO) == -1) {
perror("dup2 failed");
exit(EXIT_FAILURE);
}
close(fd); // 关闭fd,因为STDIN_FILENO已指向它

// 4. 此时scanf从文件读取数据
printf("Reading from %s:\n", filename);
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
printf("Read: %s", buffer);
}

// 5. 恢复原始标准输入
if (dup2(old_stdin, STDIN_FILENO) == -1) {
perror("dup2 restore failed");
exit(EXIT_FAILURE);
}
close(old_stdin);

// 6. 恢复后从终端输入
printf("Enter something from terminal: ");
fgets(buffer, sizeof(buffer), stdin);
printf("You entered: %s", buffer);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
cat dup2-from-file-input.txt 
Roses are red,
Violets are blue,
Testing dup2,
And stdin too.%
➜ build git:(main) ✗ ./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)
    
    1
    2
    3
    4
    5
    6



    ```c
    close(newfd);
    return dup(oldfd);

应用场景

  1. IO 重定向:如将程序输出重定向到文件(类似 Shell 中的> file)。
  2. 管道通信:在父子进程间通过管道传递数据时,重定向输入 / 输出。
  3. 日志系统:将程序日志输出同时写入终端和日志文件。
  4. 网络编程:将套接字描述符重定向为标准输入 / 输出,实现网络 IO 与标准 IO 的统一处理。

注意事项

  1. 文件描述符关闭顺序:重定向时若newfd已打开,dup2会先关闭它,可能导致数据丢失。
  2. 原子性dup2是原子操作,无需担心多线程环境下的竞争条件。
  3. 资源管理:复制的描述符共享文件表项,需确保正确关闭(避免文件描述符泄漏)。
  4. 错误处理:始终检查dup2的返回值,处理可能的错误(如文件不存在、权限不足)。

通过合理使用dup2,可以灵活控制程序的输入输出流向,实现复杂的系统编程需求。在实际开发中,它常与管道、套接字等结合使用,是构建进程间通信和重定向机制的核心工具。

处无为之事,行不言之教;作而弗始,生而弗有,为而弗恃,功成不居!