☰
Linux 之 【基础I/O】(重定向、一切皆文件的理解、dup2)
2026/10/1 2:14:58 网站建设 项目流程

目录

1.重定向

1.1引入

1.2dup2

1.3原理

1.4重定向的实现

1.5重定向与进程替换

2.2重定向示例

2.文件描述符1与2

3.一切皆文件的理解


1.重定向

1.1引入

首先使用close接口关闭文件描述符为1的文件(起初是标准输出文件)

然后open函数打开一个文件,根据最小未使用文件描述符策略,该文件的文件描述符为1

接着向文件描述符为1的文件写入5条信息

最后再关闭open函数打开的文件

这样就实现了输出重定向的非常搓的版本(先关闭再打开):原本向标准输出文件写入的内容变为了写入filename文件当中

1.2dup2

实现输入输出追加重定向,调用dup2函数即可,这样就不用先关闭再打开了

类别详情
函数原型#include <unistd.h>
int dup2(int oldfd, int newfd);
功能描述将文件描述符oldfd复制到newfd,使两者指向同一文件表项。若newfd已打开,则先关闭它。
参数说明-oldfd:源文件描述符(必须有效且已打开)。
-newfd:目标文件描述符(可指定,范围通常为[0, OPEN_MAX-1])。
返回值- 成功:返回newfd。
- 失败:返回-1,并设置errno(如EBADF、EMFILE)。

(1)输出重定向

(2)追加重定向

(3)输入重定向

1.3原理

文件描述符实质上是进程文件描述符表fd_array[]的数组下标,表中存储的是指向内核文件对象(struct file)的指针。dup2()的功能表面上是指定文件描述符的复制,实质上是将一个文件描述符表项中的指针复制到另一个表项中。重定向的原理就是:进程通过修改文件描述符表项中的指针指向,使得对特定文件描述符的操作实际作用于另一个文件。

1.4重定向的实现

实现重定向的思路是:在执行需要重定向的操作之前,通过系统调用(如dup2())让内核修改进程的文件描述符表,使得特定的文件描述符指向不同的文件对象,从而永久改变后续所有通过该文件描述符进行的I/O操作的目标

void normalExecute(char* argv[]) { //创建子进程执行普通命令 pid_t id = fork(); if(id == 0) { //打开重定向文件,修改文件描述符的对应内容 //文件描述符 int fd = 0; //输入重定向 if(rdir == IN_RDIR) { fd = open(rdirfilename, O_RDONLY); dup2(fd, 0); } //输出重定向 else if(rdir == OUT_RDIR) { fd = open(rdirfilename, O_CREAT|O_WRONLY|O_TRUNC, 0666); dup2(fd, 1); } //追加重定向 else if(rdir == APPEND_RDIR) { fd = open(rdirfilename, O_CREAT|O_WRONLY|O_APPEND, 0666); dup2(fd, 1); } //子进程执行普通命令 //参数1程序名,参数2命令行参数数组 execvp(argv[0], argv); exit(EXIT_FAILURE);//替换失败返回错误码 } else if(id > 0) { //父进程阻塞等待 int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0)//等待成功 { } else//等待失败 { } } else { perror("fork fail\n"); exit(EXIT_FAILURE);//创建子进程败 } }

1.5重定向与进程替换

结论:程序替换不会影响文件访问(包括重定向)

程序替换(exec)不会改变进程已打开的文件状态,包括重定向设置。这是因为Linux内核将进程管理与文件管理解耦,文件描述符表作为进程的一部分在exec时被保留。通过系统调用(如dup2)修改的文件描述符关联关系会被新程序继承,从而实现重定向的持久效果

2.2重定向示例

  • 仅重定向标准输出

./mytest > log.txt 等价于 ./mytest 1> log.txt 效果:fd=1(标准输出) → log.txt文件
  • 分别重定向
./mytest 1>normal.log 2>err.log 效果: fd=1 → normal.log(正常输出) fd=2 → err.log(错误输出)
  • 合并重定向
方式1: ./mytest >all.log 2>&1 等价于 ./mytest 1>all.log 2>&1 执行顺序: (1) >all.log 或 1>all.log → fd=1指向all.log (2) 2>&1 → fd=2也指向fd=1当前指向的文件(all.log) 方式2: ./mytest &>all.log 等价于 >all.log 2>&1 错误的方式: ./mytest 1>all.log 2>all.log //error 竞争 !!两个描述符独立打开文件,会产生竞争条件,内容可能交错混乱!! ./mytest 2>&1 >all.log //error 顺序错误 2>&1 → fd=2指向fd=1当前指向的文件(此时fd=1仍指向显示器) >all.log → fd=1改为指向all.log 结果: fd=2指向显示器,fd=1指向all.log
  • > 默认重定向fd=1,2> 重定向fd=2
  • 2>&1 不是重定向到文件,而是让fd=2指向fd=1的当前位置
  • 顺序很重要:先重定向目标,再复制描述符
  • &> 是bash的简写,等价于>file 2>&1

2.文件描述符1与2

  • 文件描述符1(fd=1):标准输出(stdout) - 正常输出信息

  • 文件描述符2(fd=2):标准错误(stderr) - 错误信息输出

  • 默认情况下,程序刚启动时,文件描述符1和2指向不同的文件对象,但这两个文件对象最终都连接到同一个终端设备

3.一切皆文件的理解

自底向上,Linux内核为统一管理各类硬件设备和系统资源,为每种设备类型定义一组操作方法(如读、写、控制等),封装在file_operations结构体中。当设备被访问时,内核创建struct file对象来描述这次打开操作,其中的f_op指针指向该设备对应的操作方法集。进程通过文件描述符访问文件时,内核查找对应的struct file对象,并通过其f_op指针调用具体的设备操作方法。这种抽象层在用户空间表现为统一的文件接口,从而实现了"一切皆文件"的设计哲学

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询