進程 A 讀取文件大小 → 進程 B 寫入文件(大小變化) → 進程 A 按舊大小讀取 → 數據截斷/混亂 → 寫回破壞文件。
stat(filename, &sb); // 競態窗口打開
read(fd, buf, sb.st_size);
int fd = open(filename, O_RDWR);
flock(fd, LOCK_SH); // 或 fcntl(F_SETLK, F_RDLCK)
fstat(fd, &sb); // 基於已鎖定的 fd 獲取大小,原子
read(fd, buf, sb.st_size);
flock(fd, LOCK_UN);
| 特性 | BSD flock | POSIX fcntl |
|---|---|---|
| 粒度 | 整個文件 | 字節範圍 |
| 繼承 | fork 繼承 | 不繼承 |
| 網絡文件系 | NFS 支持差 | NFS 支持較好 |
| API | flock(fd, op) | fcntl(fd, F_SETLK, &flk) |
/* 讀 */
int read_ini(const char *file, IniData *out) {
int fd = open(file, O_RDONLY);
if (fd < 0) return -1;
if (flock(fd, LOCK_SH)) { close(fd); return -2; }
struct stat st; fstat(fd, &st);
char *buf = malloc(st.st_size + 1);
read(fd, buf, st.st_size); buf[st.st_size] = 0;
flock(fd, LOCK_UN); close(fd);
return parse_ini(buf, out);
}
/* 寫 */
int write_ini(const char *file, const IniData *in) {
char tmp[PATH_MAX]; snprintf(tmp, sizeof(tmp), "%s.tmp.%d", file, getpid());
int fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0) return -1;
if (flock(fd, LOCK_EX)) { close(fd); unlink(tmp); return -2; }
char *buf = serialize_ini(in);
write(fd, buf, strlen(buf));
flock(fd, LOCK_UN); close(fd);
rename(tmp, file); // 原子替換
return 0;
}