lseek + SET_SEEK_END 与 open + O_APPEND 的区别杂谈

本文通过lseek+SET_SEEK_END和open+O_APPEND实验,揭示了在多进程环境下如何确保文件追加写入的安全性。lseek+END可能导致覆盖,而O_APPEND提供原子操作,确保数据按序追加。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先说结论

lseek + SET_SEEK_END 将文件指针置到最后后,无法保证写入时仍然在最后(即,指针移动和写入两个动作不是原子的),如果有多个进程打开同一个文件读写,则有可能发生覆盖

open + O_APPEND打开文件的话,每次write时,都会自动先移动指针,然后写入,可以视为原子操作。多个进程可以安全的追加写同一个文件。

lseek + SET_SEEK_END 实验

lseek1打开一个文件,设置文件指针到末尾,等待20秒后写入字符串“hello”

/* lseek1.c */
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main()
{
    /* 打开文件 */
	int fd = open("./testfile.txt", O_RDWR);
	if (fd == -1) {
		perror("Open testfile.txt failed");
		return 1;
	}
	
	/* 设置文件指针到末尾 */
	off_t off = lseek(fd, 0, SEEK_END);
	if (off == -1) {
		perror("lseek failed");
		return 1;
	}
	/* 休眠,模拟进程竞争,中途拉起lseek2进程 */
	sleep(20);
	
	/* 写入 “hello” */
	ssize_t wb = write(fd, "hello", sizeof("hello"));
	if (wb != sizeof("hello")) {
		perror("write failed");
		return 1;
	}
	
	close(fd);
	
	return 0;
}

lseek2lseek1 几乎一样,区别在于他写入 “what?”

/* lseek2.c  */
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main()
{
    /* 打开文件 */
	int fd = open("./testfile.txt", O_RDWR);
	if (fd == -1) {
		perror("Open testfile.txt failed");
		return 1;
	}
	
	/* 设置文件指针到末尾 */
	off_t off = lseek(fd, 0, SEEK_END);
	if (off == -1) {
		perror("lseek failed");
		return 1;
	}
	
	/* 写入 “what?” */
	ssize_t wb = write(fd, "what?", sizeof("what?"));
	if (wb != sizeof("what?")) {
		perror("write failed");
		return 1;
	}
	
	close(fd);
	
	return 0;
}

先运行 lseek1,在 lseek1 休眠期间 运行 lseek2:

# 文件原来的内容是“0123456789”
velscode@velscode:~/code/blog_temp$ cat testfile.txt
0123456789
# 先运行 lseek1
velscode@velscode:~/code/blog_temp$ ./lseek1 &
[1] 50747
# lseek 休眠期间运行 lseek2
velscode@velscode:~/code/blog_temp$ ./lseek2
# 查看文件内容,已被写入“what?”
velscode@velscode:~/code/blog_temp$ cat testfile.txt
0123456789what?
# lseek1 休眠结束,执行写入
velscode@velscode:~/code/blog_temp$
[1]+  Done                    ./lseek1
# 再次查看文件内容,发现“waht?”被覆盖写成了“hello”
velscode@velscode:~/code/blog_temp$ cat testfile.txt
0123456789hello

so bad

open() + O_APPEND 实验

open1打开一个文件,设置文件指针到末尾,等待20秒后写入字符串“hello”

/* open1.c */
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main()
{
	int fd = open("./testfile.txt", O_RDWR | O_APPEND);
	if (fd == -1) {
		perror("Open testfile.txt failed");
		return 1;
	}
	
	sleep(20);
	
	ssize_t wb = write(fd, "hello", sizeof("hello"));
	if (wb != sizeof("hello")) {
		perror("write failed");
		return 1;
	}
	
	close(fd);
	
	return 0;
}

open2open1 几乎一样,区别在于他写入 “what?”

/* open2.c */
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main()
{
	int fd = open("./testfile.txt", O_RDWR | O_APPEND);
	if (fd == -1) {
		perror("Open testfile.txt failed");
		return 1;
	}
	
	ssize_t wb = write(fd, "what?", sizeof("what?"));
	if (wb != sizeof("what?")) {
		perror("write failed");
		return 1;
	}
	
	close(fd);
	
	return 0;
}
# testfile.txt 原来的内容是“01234567890”
velscode@velscode:~/code/blog_temp$ cat testfile.txt
0123456789
# 拉起open1进程
0123456789velscode@velscode:~/code/blog_temp$ ./open1 &
[1] 50798
# 在open1休眠期间,执行open2
velscode@velscode:~/code/blog_temp$ ./open2
# open2成功追加写入“what?”
velscode@velscode:~/code/blog_temp$ cat testfile.txt
0123456789what?
# open1休眠完毕,开始写入
velscode@velscode:~/code/blog_temp$
[1]+  Done                    ./open1
# open1在open2的字符串后面追加写入
velscode@velscode:~/code/blog_temp$ cat testfile.txt
0123456789what?hello

其他注意事项

仅在POSIX系统上,打开带有O_APPEND标志的文件将保证附加写入始终是安全的。

两个进程通过O_APPEND追加写入同一个文件,虽然能够保证字节顺序,但是不一定是连续的,即,有可能发生交错

参考资料

[1] Appending to a File from Multiple Processes
[2] lseek(2) — Linux manual page
[3] open(2) — Linux manual page

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值