【Linux】系统级文件I/O与文件描述符深度剖析
目录
1. 引言
2. 系统调用接口
2.1 open 函数
2.2 write 与 read
2.3 close
3. 文件描述符(File Descriptor, fd)
3.1 默认打开的三个文件描述符
3.2 文件描述符的本质
3.3 文件描述符分配规则
4. 重定向的原理
4.1 输出重定向示例
4.2 使用 dup2 系统调用
5. 小结
1. 引言
C标准库的fopen等函数最终调用的是系统调用。本文直接使用系统调用接口open、read、write、close进行文件操作,并重点讲解文件描述符的概念、分配规则以及重定向的底层原理。
2. 系统调用接口
2.1open函数
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.2write与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.3close
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添加完整的重定向功能。
