我在 Linux 上使用打开文件描述 (OFD) 拥有的锁(fcntl
使用命令F_OFD_SETLK
)。锁定文件后,我对其进行了内存映射,然后关闭了文件描述符。另一个进程尝试锁定同一个文件,但直到第一个进程取消内存映射后才成功。看来 Linux 至少在映射仍处于活动状态时保留了对打开文件描述的引用。
POSIX.1-2024 文档mmap
添加了对“与文件描述符关联的文件”的引用。
mmap() 函数应向与文件描述符 fildes 关联的文件添加额外的引用,该文件描述符上的后续 close() 不会删除该文件。当文件不再有映射时,应删除此引用。
这里的字面解释是引用文件本身,但我不知道这是否是编写文档时的意图。
我希望能够依赖这种行为。POSIX 中是否有我遗漏的地方?这可能是缺陷报告吗?如果它是 Linux 独有的,是否有任何地方提到这是他们的预期行为(并且可能是他们对 POSIX 标准的解释)?
测试程序(在其他平台上可能需要不同的功能测试宏):
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
char filename[] = "/tmp/ofd-test.XXXXXX";
int fd = mkstemp(filename);
if (fd < 0) {
perror("mkstemp");
return 1;
}
fprintf(stderr, "created file '%s'\n", filename);
struct flock lock = {
.l_len = 0,
.l_pid = 0,
.l_whence = SEEK_SET,
.l_start = 0,
.l_type = F_WRLCK,
};
if (fcntl(fd, F_OFD_SETLK, &lock) < 0) {
perror("first lock");
return 1;
}
void *ptr = mmap(0, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
return 1;
}
close(fd);
int newfd = open(filename, O_RDWR);
if (newfd < 0) {
perror("re-open");
return 1;
}
lock.l_pid = 0;
if (fcntl(newfd, F_OFD_SETLK, &lock) == 0) {
fputs("locking after mmap worked\n", stderr);
return 1;
}
perror("locking after mmap");
munmap(ptr, 1024);
lock.l_pid = 0;
if (fcntl(newfd, F_OFD_SETLK, &lock) < 0) {
perror("locking after munmap");
return 1;
}
fputs("locking after munmap worked\n", stderr);
if (unlink(filename) < 0) {
perror("unlink");
return 1;
}
return 0;
}
对我来说,输出如下:
created file '/tmp/ofd-test.Pyf3oj'
locking after mmap: Resource temporarily unavailable
locking after munmap worked
文件描述符是对打开文件的引用。
引用计数在文件本身上维护,而不是在描述符上维护。
生成
mmap
另一个引用,但不需要附加到描述符——描述符的唯一状态几乎就是它的编号和 close-on-exec 标志,而映射区域则不需要它们。手册页
dup(2)
说是的,这种行为是预料之中的。