Linux 下的open函数
参数flag:O_APPEND
如果打开文件以O_APPEND,则在多线程下,该函数是原子操作。
问题:打开文件操作,会发生指定文件不存在的情况,如果打开以O_CREAT方式,则会创建文件,然后再打开该文件,这是两步操作。多线程下,可能open函数创建了文件之后,时间片用光了,其它线程抢占CPU运行,这会发生不可预知的事情。
如果以追加方式打开则保证open函数原子性。
下面以有趣的课后题来分析这个参数:
《linux编程手册》课后题5-3:
本习题的设计目的在于展示为何以 O_APPEND 标志打开文件来保障操作的原子性是必要的。请编写一程序,可接收多达 3 个命令行参数:
$ atomic_append filename num-bytes [x]
该程序应打开所指定的文件(如有必要,则创建之),然后以每次调用 write()写入一个字节的方式,向文件尾部追加 num-bytes 个字节。缺省情况下,程序使用 O_APPEND标志打开文件,但若存在第三个命令行参(x),那么打开文件时将不再使用O_APPEND 标志,代之以在每次调用 write()前调用 lseek(fd,0,SEEK_END)。同时运行该程序的两个实例,不带 x 参数,将 100 万个字节写入同一文件:
$ atomic_append f1 1000000 & atomic_append f1 1000000
重复上述操作,将数据写入另一文件,但运行时加入 x 参数:
$ atomic_append f1 1000000 x & atomic_append f1 1000000 x
代码
#include <stdio.h>
#include<unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<errno.h>
int main(int argc, char const *argv[])
{
if(argc > 4 || 0 == strcmp(argv[1],"--help"))
{
printf("%s test write O_APPEND\n",argv[0]);
}
//char* filename;
long long num_byte;
char buf = 'x';
int fd ;
if(argc == 3)
{
fd = open(argv[1],O_CREAT|O_WRONLY|O_APPEND,S_IRUSR|S_IWUSR);
num_byte = atoll(argv[2]);
while (num_byte--)
{
if(-1 == write(fd,&buf,1)) //write one byte once;
{
perror("write");
}
}
if(-1 == close(fd))
{
perror("close file");
}
}
if(argc == 4)
{
fd = open(argv[1],O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR);
num_byte = atoll(argv[2]);
while (num_byte--)
{
if(lseek(fd,0,SEEK_END) == -1)
{
perror("lseek");
}
if(-1 == write(fd,&buf,1)) //write one byte once;
{
perror("write");
}
}
if(-1 == close(fd))
{
perror("close file");
}
}
return 0;
}
结果
生成两个文件WriteFileOne.txt和WriteFileTwo.txt,同样是写入200万个字节,但是如果不以 O_APPEND打开文件,而是用lseek函数来移动写入到末尾,则会出现写入覆盖,导致WriteFileTwo.txt小于200万个字节。这说明了O_APPEND可以保证open函数的原子性。