[英]Why is data written to a file opened with O_APPEND flag, always written at the end, even with `lseek`?
我被分配了一个编程任务:
编写一个程序,使用 O_APPEND 标志打开现有文件进行写入,然后在写入一些数据之前查找到文件的开头。 数据出现在文件中的什么位置? 为什么?
我想出的是:
main() {
int fd = open("test.txt", O_WRONLY | O_APPEND);
lseek(fd, 0, SEEK_SET);
write(fd, "abc", 3);
close(fd);
}
尝试上面的方法后,我发现数据总是写在文件的末尾。 这是为什么? 是因为我指出了O_APPEND
吗?
实际上, O_APPEND 只影响write
的行为,而不影响read
的行为。 无论lseek
如何改变文件的当前位置, write
总是append-only
。
当你用O_RDWR | O_APPEND
open
文件时O_RDWR | O_APPEND
O_RDWR | O_APPEND
, read
仍将从文件的开头开始。
在open
( man 2 open
) 的手册中,
O_APPEND 文件以追加模式打开。 在每次写入(2) 之前,文件偏移量位于文件末尾。
在write
手册( man 2 write
)中,
如果设置了文件状态标志的 O_APPEND 标志,则文件偏移量应在每次写入之前设置为文件末尾。
在 Linux 内核fs/ext4 syscall write
-> vfs_write
-> ext4_file_write_iter
, ext4_file_write_iter
会调用ext4_write_checks
你会找到设置pos
= file.size
/* FIXME: this is for backwards compatibility with 2.4 */
if (iocb->ki_flags & IOCB_APPEND)
iocb->ki_pos = i_size_read(inode);
pos = iocb->ki_pos;
下面的demo可以验证一下。
cat open_append.cc
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <string>
#include <iostream>
int main(int argc, char *argv[]) {
std::string path = "./test.txt";
std::string content = "hello_world";
std::string read_buf(content.size(), 0x0);
struct stat st_buf;
ssize_t bytes_read = -1;
ssize_t bytes_write = -1;
int ret = -1;
off_t cur_off = -1;
int fd = ::open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd < 0) {
std::cerr << "open err path " << path
<< " errno " << errno << std::endl;
return -1;
}
std::cout << "open ok path " << path
<< " fd " << fd << std::endl;
// Step 1 write some data into an empty file
bytes_write = ::write(fd, content.data(), content.size());
if (bytes_write < 0) {
std::cerr << "write err fd " << fd
<< " errno " << errno << std::endl;
goto out;
}
std::cout << "write ok fd " << fd
<< " data " << content
<< " nbytes " << bytes_write << std::endl;
::close(fd);
// Step 2 open the file again with O_APPEND
fd = -1;
fd = ::open(path.c_str(), O_CREAT | O_RDWR | O_APPEND, 0644);
if (fd < 0) {
std::cerr << "open again err path " << path
<< " errno " << errno << std::endl;
return -1;
}
std::cout << "open again ok path " << path
<< " fd " << fd << std::endl;
// Step 3 the current position of the file NOT affected by O_APPEND
cur_off = ::lseek(fd, 0, SEEK_CUR);
if (cur_off < 0) {
std::cerr << "lseek err SEEK_CUR fd " << fd
<< " errno " << errno << std::endl;
goto out;
}
// cur_off expected to be 0
std::cout << "lseek ok SEEK_CUR fd " << fd
<< " cur_off " << cur_off << std::endl;
// Step 4 the read will start from the beginning of the file
bytes_read = read(fd, (char*)read_buf.data(), content.size());
if (bytes_read < 0) {
std::cerr << "read err fd " << fd
<< " errno " << errno << std::endl;
goto out;
}
std::cout << "read ok fd " << fd
<< " data " << read_buf
<< " nbytes " << bytes_read << std::endl;
// Step 5 change the position to the half of the file size
cur_off = ::lseek(fd, content.size() / 2, SEEK_SET);
if (cur_off < 0) {
std::cerr << "lseek err SEEK_SET fd " << fd
<< " errno " << errno << std::endl;
goto out;
}
// cur_off expected to be content.size() / 2
std::cout << "lseek ok SEEK_SET fd " << fd
<< " cur_off " << cur_off << std::endl;
// Step 6 write will append data from the end of the file
// the current position is ignored
bytes_write = ::write(fd, content.data(), content.size());
if (bytes_write < 0) {
std::cerr << "append write err fd " << fd
<< " errno " << errno << std::endl;
goto out;
}
std::cout << "append write ok fd " << fd
<< " append data " << content
<< " append nbytes " << bytes_write << std::endl;
// Step 7 the file size is double content.size()
memset((void*)&st_buf, 0x0, sizeof(struct stat));
ret = lstat(path.c_str(), &st_buf);
if (ret < 0) {
std::cerr << "lstat err path " << path
<< " errno " << errno << std::endl;
goto out;
}
std::cout << "lstat ok path " << path
<< " st_size " << st_buf.st_size << std::endl;
ret = 0;
out:
if (fd >= 0) {
close(fd);
}
return ret;
}
输出结果
open ok path ./test.txt fd 3
write ok fd 3 data hello_world nbytes 11
open again ok path ./test.txt fd 3
lseek ok SEEK_CUR fd 3 cur_off 0
read ok fd 3 data hello_world nbytes 11
lseek ok SEEK_SET fd 3 cur_off 5
append write ok fd 3 append data hello_world append nbytes 11
lstat ok path ./test.txt st_size 22
O_APPEND 标志强制文件指针仅指向文件末尾。 因此,如果您从文件的开头执行 lseek,它会将更新后的文件指针位置作为文件的开头,即旧文件的结束位置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.