我希望能够原子地写入文件,我试图使用write()
函数,因为它似乎在大多数linux / unix系统中授予原子写入。
由于我有可变的字符串长度和多个printf,我被告知使用snprintf()
并将其作为参数传递给write函数,以便能够正确地执行此操作,这个函数的文档我做了一个测试实现如下:
int file = open("file.txt", O_CREAT | O_WRONLY);
if(file < 0)
perror("Error:");
char buf[200] = "";
int numbytes = snprintf(buf, sizeof(buf), "Example string %s" stringvariable);
write(file, buf, numbytes);
从我的测试看起来它似乎有效,但我的问题是,如果这是最正确的实现方式,因为我正在创建一个相当大的缓冲区(我100%确定将适合我的所有printfs)之前存储它传递给写作。
答案 0 :(得分:1)
不,write()
不是原子的,即使在写入一次调用中提供的所有数据时也是如此。
在所有读者和作者中使用建议记录锁定(fcntl(fd, F_SETLKW, &lock)
)来实现原子文件更新。
基于fcntl()的记录锁在Linux和BSD上通过NFS工作;基于flock()的文件锁可能不会,具体取决于系统和内核版本。 (如果在某些Web托管服务上禁用NFS锁定,则锁定将不可靠。)只需使用struct flock
初始化.l_whence = SEEK_SET, .l_start = 0, .l_len = 0
以引用整个文件。
使用asprintf()
打印到动态分配的缓冲区:
char *buffer = NULL;
int length;
length = asprintf(&buffer, ...);
if (length == -1) {
/* Out of memory */
}
/* ... Have buffer and length ... */
free(buffer);
添加锁定后,请将write()
包裹在循环中:
{
const char *p = (const char *)buffer;
const char *const q = (const char *)buffer + length;
ssize_t n;
while (p < q) {
n = write(fd, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n != -1) {
/* Write error / kernel bug! */
} else
if (errno != EINTR) {
/* Error! Details in errno */
}
}
}
虽然有一些本地文件系统保证write()
不会返回短计数,除非你的存储空间不足,不是所有的都做;尤其不是联网的。使用上面的循环可以使您的程序在这些文件系统上工作。在我看来,为可靠和稳健的操作添加的代码并不多。
在Linux中,您可以对文件执行a write lease以排除暂时打开该文件的任何其他进程。
基本上,您无法阻止文件打开,但您可以将其延迟最多/proc/sys/fs/lease-break-time
秒,通常为45秒。仅当没有其他进程打开文件时才授予租约,如果任何其他进程尝试打开文件,则租约所有者会收到信号。 (如果租赁所有者没有释放租约,例如关闭文件,内核将在租约中断时间结束后自动中断租约。)
不幸的是,这些仅适用于Linux,仅适用于本地文件,因此用途有限。
如果读者不保持文件打开,但每次阅读时都打开,阅读和关闭文件,您可以编写完整的替换文件(必须在同一个文件系统上;我建议使用锁定 - 这个子目录),并将其硬链接到旧文件。
所有读者都会看到旧文件或新文件,但保持文件打开的文件将永远不会看到任何更改。