Time: 2021.02.25
Tags: 开发,Linux
编程时涉及到文件操作,需要通过 r/w/a
模式来打开文件,这对于我们太熟悉了;最近在使用时和预想的不太一样,这里对 r/w/a
模式进行详细的记录。
r
: 以只读的方式打开文本文件,文件必须存在;打开后文件指针位于开头
w
: 以只写的方式打开文本文件,文件不存在则新建,文件存在则清空;打开后文件指针位于开头
a
: 以只写的方式打开文本文件,文件不存在则新建,文件存在则从文件尾部以追加的方式开始写;打开后文件指针位于末尾,并且不能通过 seek()
更改。
便于记忆的表格:
| r r+ w w+ a a+
------------------|--------------------------
read | + + + +
write | + + + + +
write after seek | + + +
create | + + + +
truncate | + +
position at start | + + + +
position at end | + +
以上都是使用文本模式(textmode
)打开文件,如果在其上添加 b
,如:rb / wb / ab
就是以二进制模式(binarymode
)打开文件。
二进制模式与文本模式操作一致,只不过是数据以二进制流的形式读写。
对于文本模式,写进去、读出来的,和实际存储的数据,两者不一定相同
对于二进制模式,写进去、读出来的,和实际存储的数据,两者一定相同
比如在 windows 上使用文本模式写入
\n
,将会变成\r\n
补充一个特别的点。
在并发写文件时我们会习惯性的进行加锁,实际上不是必要的。
对于 O_APPEND
模式在类 Unix 系统上还有个特性:原子操作;由操作系统内核在写入文件时保证其原子操作;实际上在大量开源代码中的日志写入不加锁就是用的这个特性。
PS:
1.Windows 下也同样拥有这个特性;
2.在 Linux 系统相关实现中,除文件写入外,对于 PIPE 或 FIFO 的写入,只保证小于PIPE_BUF
的长度字节写入是原子性的,一般为 512/1024/4096/8192 等大小,受操作系统和文件实现的不同实现而不同;
也就是说当多线程/多进程都是以 O_APPEND
模式打开文件时,同时写入文件不会造成文件混乱,如下:
void* write_with_append(void* args) {
sleep(1);
int tid = pthread_self();
char buf[32] = {0};
sprintf(buf, "0x%08x\n", tid);
int fd = open("./test", O_CREAT | O_RDWR | O_APPEND, 0644);
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
详细测试文件可以参考: append_test.c,多线程执行是随机的,可以使用 bash 多次执行来观察:
#!/bin/bash
for i in {1..10}
do
echo $i
./xx
wc -l ./test
done
PS: 早期都是通过
seek+write
追加文件的,而O_APPEND
正是因为这个问题而引入的;
除此之外,Linux 从 3.14 起read/write
才是原子性的。
还需要注意某些语言标准库的实现,其优化部分可能会在标准库层对写入进行拆分,从而导致底层为多次 write,引发覆盖的问题;比如 C 语言的fputs
。
我们使用 https://elixir.bootlin.com/linux/latest/source 分析 linux 5.19 的代码。
通过分析 write
调用链,找到实际调用函数 link:
继续跟进 vfs_write
最终会通过 file_operations
调用实际的 write
函数,由各个文件系统实现,link
这里我们参考 ext2
的实现,link
跟进 generic_file_write_iter
函数,可以看到其中 inode_lock/inode_unlock
函数,其保证了写入文件的互斥;link
我们再进入到 generic_write_checks
函数内,最终定位到 generic_write_checks_count
,其中当设置了 APPEND
时,此处会重新刷新 pos
的值指向文件末尾;link
根据如上源码分析,我们可以 Linux 文件写入的互斥性以及并发时 APPEND 是如何保证准确写到文件末尾的;以此应证了上文中说到的 APPEND 特性。
1.https://stackoverflow.com/questions/1466000/difference-between-modes-a-a-w-w-and-r-in-built-in-open-function
2.https://www.zhihu.com/question/24662572
3.《UNIX环境高级编程》
https://github.com/torvalds/linux
https://codeantenna.com/a/jBWk5CERwx