文件读取¶
本文通过一个精简版 cat 命令的实作,演示如何使用 Linux 系统调用读取文件内容。 精简版 cat 命令功能非常单一,但五脏俱全,足以用来演示。其用法如下:
$ echo hello > foo
$ ./cat foo
hello
如上, cat 接受一个命令行参数——被读取文件路径,打开并读取文件,最后将文件内容输出到标准输出。 如果文件不存在, cat 命令提示错误并退出。
精简版 cat 命令源码如下:
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 | #include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 10240
int main(int argc, char *argv[])
{
// 检查参数
if (argc != 2) {
fprintf(stderr, "Usage: cat [filepath]\n");
exit(1);
}
// 打开文件
int fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("Open file");
exit(2);
}
// 循环输出
for (;;) {
// 缓冲区
char buffer[BUFFER_SIZE];
// 读文件
int bytes = read(fd, buffer, BUFFER_SIZE);
if (bytes == -1) {
perror("Read file");
exit(3);
}
else if (bytes == 0) {
break;
}
// 写到标准输出
// 文件描述符0代表标准输出
if (write(0, buffer, bytes) == -1) {
perror("Write stdout");
exit(4);
}
}
close(fd);
return 0;
}
|
这是一个非常简短的程序,代码逻辑都在 main 函数里。
第 10-14 行处,先检查命令行参数。如果参数个数不符合预期,输出错误并退出。
第 17-21 行处,调用 open 系统调用打开文件。 open 系统调用返回一个 文件描述符 ,后续可以通过该描述符操作文件。
如果文件打开失败, open 将返回 -1 ,程序需要对异常情况进行处理。 导致打开文件的原因又很多,例如:文件不存在;抑或权限不足等等。
注意到,我们以只读模式打开文件—— flags 参数为 O_RDONLY 。 flags 参数用于指定不同的访问模式,可以是以下三者之一:
- O_RDONLY ,只读模式
- O_WRONLY ,只写模式
- O_RDWR ,读写模式
更多关于 open 系统调用使用方法,请参考 open(2) - Linux manual page 。 这是一份唾手可得的编程文档:
$ man 2 open
第 24-44 行,在一个循环内不停读取文件并写到标准输出,直到文件读完。
在调用 read 系统调用前,需要准备一个 缓冲区 ,用于存储文件内容。 read 负责将文件内容从磁盘读取到缓冲区中,并返回成功读取字节数。
read 需要传入 3 个参数: 文件描述符 、 缓冲区地址 以及 读取字节数 (受缓冲区限制)。 read 返回值为零,意味着文件读取完毕,可以退出循环。
同样,更多关于 read 系统调用使用方法,请参考 read(2) - Linux manual page 。
第 46 行,在程序退出前,关闭文件描述符。 其实,这个步骤可以省略,因为程序退出时,内核会关闭其所有文件描述符。