ftruncate() 总是精确的吗?

Is ftruncate() always precise?

我正在尝试 ftruncate 一个特定长度的共享内存对象。例如,我想使用以下代码片段将其长度设置为 1 个字节:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
    struct stat fd_stat;
    int fd;

    fd = shm_open("NAME", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    fstat(fd, &fd_stat);

    printf("Size before: %lld\n", fd_stat.st_size);

    ftruncate(fd, 1);
    fstat(fd, &fd_stat);

    printf("Size after: %lld\n", fd_stat.st_size);
}

在 Ubuntu 20.04 中打印:

Size before: 0
Size after: 1

这是我期望的输出。

然而,在 macOS X Big Sur 中我得到:

Size before: 0
Size after: 4096

如您所见,它似乎正在将尺寸扩展到一页的尺寸。

ftruncate Linux man page 内容为:

The truncate() and ftruncate() functions cause the regular file named by path or referenced by fd to be truncated to a size of precisely length bytes.

尽管如此,POSIX specification 并不具体(双关语):

If fildes refers to a regular file, the ftruncate() function shall cause the size of the file to be truncated to length. [...] If the file previously was smaller than this size, ftruncate() shall increase the size of the file.

这是否意味着 ftruncate 总是将长度设置为恰好指定的字节数?如果确实如此,则意味着 macOS X Big Sur 不 完全 POSIX 兼容 (即使它被证明是这样)。如果不是,我如何保证它会将 fd 截断为我想要的大小?

简而言之,您无法保证 共享内存对象 的大小与您要求 ftruncate 的大小完全相同。这是因为,正如 @user3386109 所说,“您引用的 POSIX 规范部分以“If fildes 引用常规文件”开头”。 =25=].

如果您想将自己限制在任意长度,您始终可以使用辅助变量来跟踪您假设的大小(即使实际大小可能实际上不同,这可能并不那么重要毕竟)。您的代码如下所示:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
    struct stat fd_stat;
    int fd;
    off_t fd_size;

    fd = shm_open("NAME", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    fstat(fd, &fd_stat);

    printf("Size before: %lld\n", fd_stat.st_size);

    fd_size = 1;

    ftruncate(fd, fd_size);
    fstat(fd, &fd_stat);

    printf("Actual size: %lld\n", fd_stat.st_size);
    printf("Perceived size: %lld\n", fd_size);
}

最重要的是,如果你想在不同进程之间共享大小,你可以将 fd_size 变成一个 mmaped 共享内存对象来跟踪你假设它的大小跨越他们。