在 C 中有 fread、fwrite 等读写文件的库函数,Linux 系统也提供了操作文件的系统调用。

fopen、fclose、fread、fwrite 都是 C 标准库中的函数,libc。

Linux 中关于文件 IO 的系统调用:open、close、read、write、lseek。

注意缓冲策略:

write 无缓冲,printf 行缓冲。

常见缓冲策略:

  1. 无缓冲

    例如:write 系统调用

  2. 行缓冲:遇到 \n 就刷新,或者缓冲区满才刷新,或者手动花心(fflush)

    例如:打印到显示器

  3. 全缓冲:一直到缓冲区满才刷新,或者手动刷新

    例如:输出到文件

文件描述符 fd

Linux 上一个进程运行了后,默认打开三个文件:stdin、stdout、stderr。

用什么来确定这三个文件在哪里呢?或者说如果我要从键盘读入一些东西,应该从哪个文件读呢?

文件描述符、文件描述符表。

文件描述符表是一个指针数组,每一个元素都是一个 file* 指针,指向一个已经打开的文件。

文件描述符是这个指针数组的下标,所以只要拿着文件描述符,就可以找到对应的文件。

我在网上看到别人说在 Linux 内核中并不是简单的数组下标,是通过 hash 计算出来的,咱现在也不懂,后面看到了再补充。

文件描述符表

文件描述符分配规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

int main(void) {
// close(0);
int fd = open("baseCIO.c", O_RDONLY);
if (fd < 0) {
perror("open error");
exit(0);
}
printf("fd : %d\n", fd);
close(fd);

return 0;
}

//close(0)

close(0)

文件描述符分配规则:在数组中找到当前没有被使用的最小的下标,作为新的文件描述符。

open

man open:

1
2
3
4
5
6
7
8
9
SYNOPSIS
#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);

int creat(const char *pathname, mode_t mode);

参数

pathname:要打开或创建的目标文件

flags:打开文件时,可以传入多个参数选项(位图的方式),用下面的一个或者多个常量进行”或”运算,构成 flags。

  • O_RDONLY 只读打开

  • O_WRONLY 只写打开

  • O_RDWR 读,写打开

    上面这三个常量,必须指定并且只能指定一个。

  • O_CREAT 若文件不存在,则创建。需要使用 mode 选项来指明新文件的访问权限。

  • O_APPEND 追加写

返回值

成功:新打开的文件描述符

失败:-1

栗子

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
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

// 写文件
void writeFile() {
int fd = open("test", O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
perror("open error");
exit(-1);
}

const char* msg = "hello sys io\n";
for (size_t i = 0; i < 5; i++) {
// msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数据。
// 返回值:实际写了多少字节数据
ssize_t real_len = write(fd, msg, strlen(msg));
printf("real len = %lu\n", real_len);
}

close(fd);
}

// 读文件
void readFile() {
int fd = open("test", O_RDONLY);
if (fd < 0) {
perror("open error");
exit(-1);
}

const char* msg = "hello sys io\n";
char buf[1024] = {0};
while (1) {
ssize_t s = read(fd, buf, strlen(msg));
if (s > 0) {
printf("buf = %s", buf);
} else {
break;
}
}
}

int main(void) {
writeFile();

readFile();

return 0;
}

sysIO.c 读写文件