目录
[1. 引言](#1. 引言)
[2. 系统调用接口](#2. 系统调用接口)
[2.1 open 函数](#2.1 open 函数)
[2.2 write 与 read](#2.2 write 与 read)
[2.3 close](#2.3 close)
[3. 文件描述符(File Descriptor, fd)](#3. 文件描述符(File Descriptor, fd))
[3.1 默认打开的三个文件描述符](#3.1 默认打开的三个文件描述符)
[3.2 文件描述符的本质](#3.2 文件描述符的本质)
[3.3 文件描述符分配规则](#3.3 文件描述符分配规则)
[4. 重定向的原理](#4. 重定向的原理)
[4.1 输出重定向示例](#4.1 输出重定向示例)
[4.2 使用 dup2 系统调用](#4.2 使用 dup2 系统调用)
[5. 小结](#5. 小结)
1. 引言
C标准库的 fopen 等函数最终调用的是系统调用。本文直接使用系统调用接口 open、read、write、close 进行文件操作,并重点讲解文件描述符的概念、分配规则以及重定向的底层原理。
2. 系统调用接口
2.1 open 函数
c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数:
-
pathname:文件路径。 -
flags:必须包含O_RDONLY、O_WRONLY、O_RDWR之一,可与其他标志按位或,如O_CREAT(不存在则创建)、O_APPEND(追加)、O_TRUNC(截断)。 -
mode:当使用O_CREAT时,指定新文件的权限(如0644),会受到进程umask的影响。可通过umask(0)临时清除掩码。
返回值: 成功返回一个非负整数------文件描述符;失败返回 -1。
2.2 write 与 read
c
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
-
fd:文件描述符。 -
返回值:成功返回实际读/写的字节数,出错返回 -1。
2.3 close
c
int close(int fd);
关闭文件描述符,释放相关资源。
3. 文件描述符(File Descriptor, fd)
3.1 默认打开的三个文件描述符
Linux进程启动时,默认打开三个文件描述符:
-
0:标准输入(stdin) -
1:标准输出(stdout) -
2:标准错误(stderr)
它们对应的物理设备通常是键盘和显示器。我们可以直接使用 read(0, buf, size) 从键盘读入,使用 write(1, buf, len) 输出到屏幕。
3.2 文件描述符的本质
文件描述符是一个小整数 ,它是进程打开文件表的索引。内核为每个进程维护一个 files_struct 结构体,其中包含一个指针数组 fd_array[],每个元素指向一个 struct file 对象(表示一个打开的文件)。文件描述符就是这个数组的下标。
3.3 文件描述符分配规则
规则: 在当前进程中,fd_array[] 中找到最小的未被使用的下标,分配给新打开的文件。
验证实验:
-
正常打开一个文件,输出
fd通常为3(因为0、1、2已被占用)。 -
先调用
close(0)或close(2),再打开文件,则新文件的fd将成为0或2。
4. 重定向的原理
4.1 输出重定向示例
c
close(1); // 关闭标准输出
int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
printf("fd: %d\n", fd); // 这里 fd 为 1
fflush(stdout);
现象: printf 的内容没有出现在屏幕上,而是写入了 myfile 文件。这是因为 close(1) 释放了下标1,随后 open 分配了最小的可用下标1,于是文件描述符1现在指向了 myfile 对应的 struct file。printf 底层向 stdout(即 fd=1)写入数据,数据便进入了文件。
重定向的本质: 改变文件描述符下标对应的 struct file 指针,使其指向不同的打开文件。
4.2 使用 dup2 系统调用
dup2 可以更便捷地实现重定向:
c
#include <unistd.h>
int dup2(int oldfd, int newfd);
功能:让 newfd 指向 oldfd 所指向的文件,如果 newfd 已打开,则先关闭它。
示例:实现输出重定向
c
int fd = open("log", O_CREAT | O_RDWR, 0644);
dup2(fd, 1); // 将标准输出重定向到 fd 对应的文件
printf("这条消息将写入 log 文件\n");
同理,输入重定向使用 dup2(fd, 0),追加重定向使用 O_APPEND 标志打开文件后再 dup2。
5. 小结
本文详细介绍了系统级文件I/O接口、文件描述符的概念、分配规则以及重定向的底层原理。下一篇我们将把这些知识应用到之前实现的迷你Shell中,为Shell添加完整的重定向功能。