【问题标题】:Is ftruncate() always precise?ftruncate() 总是精确的吗?
【发布时间】:2021-06-24 10:44:26
【问题描述】:

我正在尝试将ftruncate 共享内存对象设置为特定长度。例如,我想使用以下 sn-p 将其长度设置为 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

如您所见,它似乎正在将大小扩大到页面大小。

ftruncateLinux man page 内容如下:

truncate() 和 ftruncate() 函数会导致由 path 命名或由 fd 引用的常规文件被截断为 length 个字节。。 p>

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

如果 fildes 引用常规文件,则 ftruncate() 函数将导致文件的大小截断为长度。 [...] 如果之前的文件小于这个大小,ftruncate() 将增加文件的大小

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

【问题讨论】:

  • 从表面上看,macOS 的行为似乎有所不同。有没有检查调用是否失败,errno 是否表示什么?
  • 共享内存对象不是常规文件。规范谈论常规文件。对于共享内存对象来说,一页(4 KiB)似乎并不合理。
  • SHM 上的 ftruncate 实际上在 POSIX 中是可选的,即使这样 it only says: "如果 ftruncate() 的效果是减小内存映射文件或共享内存对象的大小并且新端之外的整个页面先前已映射,那么新端之外的整个页面将被丢弃。”。 这是您在 macOS 上观察到的。所以 macOS 并非不符合 POSIX。
  • 您引用的 POSIX 规范部分以 “如果 fildes 引用常规文件” 开头。该引用的其余部分与您的问题无关,因为共享内存不是常规文件。
  • 除了来自fstat 的不同返回值之外,您希望在长度为 1 的共享内存对象和长度为 4096 的共享内存对象之间观察到什么实际区别?底层硬件机制意味着映射、权限、共享,无论如何都只能通过页面粒度来控制。我认为这就是为什么您会遇到所有 XY 问题 cmets - 您似乎非常关心在其他人看来毫无意义的差异。

标签: c linux posix shared-memory


【解决方案1】:

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

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

#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 共享内存对象,以跟踪您假设它在所有进程中的大小。

【讨论】:

  • POSIX 规范还说:“如果 fildes 引用共享内存对象,则 ftruncate() 应将共享内存对象的大小设置为长度。”
  • 你能发一个链接到那个部分吗?
  • 你是对的!我没有读过...所以我想这毕竟是不符合 macOS 上的 POSIX 规范...
猜你喜欢
  • 2012-02-27
  • 1970-01-01
  • 2012-11-05
  • 2019-10-23
  • 1970-01-01
  • 1970-01-01
  • 2013-09-19
  • 1970-01-01
  • 2013-05-06
相关资源
最近更新 更多