理解 inotify 示例中的对齐语句

Understand alignment sentence in inotify example

我正在阅读 inotify 手册页 here,但我很难理解示例中的以下注释

Some systems cannot read integer variables if they are not properly aligned. On other systems, incorrect alignment may decrease performance. Hence, the buffer used for reading from the inotify file descriptor should have the same alignment as struct inotify_event.

这是缓冲区声明+定义

char buf[4096]
        __attribute__ ((aligned(__alignof__(struct inotify_event))));

我阅读了 following pdf 文档,其中提供了对内存对齐访问问题的基本理解。

我想了解 inotify 评论,也想了解一些提示和链接以进一步了解对齐问题。例如,我总是听说在堆栈上分配的变量没有对齐问题。问题仅出现在数据被重新解释的缓冲区中。

这是旧的 GCC 非标准方式,指定数组 buf 需要从适合 struct inotify_event.

的起始地址开始

这个可以在C11,C17写成

char _Alignas(struct inotify_event) buf[4096];

struct inotify_event 声明如下:

     struct inotify_event {
           int      wd;       /* Watch descriptor */
           uint32_t mask;     /* Mask describing event */
           uint32_t cookie;   /* Unique cookie associating related
                                 events (for rename(2)) */
           uint32_t len;      /* Size of name field */
           char     name[];   /* Optional null-terminated name */
       };

问题出在灵活的数组成员 name 上。名称在大括号内没有数字,这意味着 &((struct inotify_event*)0)->name[0] == offsetof(struct inotify_event, name),即。 name 成员内部元素的内存在结构之后开始。

现在,如果我们要讲述一个 inotify 事件,我们需要在结构后添加额外的 space 作为名称。动态分配可能如下所示:

    char name[] = "this is the name of this event";
    struct inotify_event *obj = malloc(sizeof(*obj) * (strlen(name) + 1));
    obj->wd = smth;
    obj->mask = smth2;
    obj->len = strlen(name) + 1;
    // fun fact: obj->name = &obj[1] . So if you ware to place array of inotify_events, you would overwrite the second member here.
    memcpy(obj->name, name, obj->len);

name 结构成员的内存紧跟在 struct inotify_event 之后,并使用相同的 malloc 分配。所以如果我们想要一个 inotify_events 的数组并复制它们包括名称,下一个结构 inotify_event 可能是未对齐的。

让我们假设 alignof(struct inotify_event) = 8sizeof(struct inotify_event) = 16char name[] = "A"; 所以 strlen(name) = 1strlen 不包括计算终止零字节)并且我们想要存储一个缓冲区内的 inotfiy_events 数组。首先我们复制 struct - 16 字节。然后我们复制名称 - 2 个字节(包括零字节)。如果我们要复制下一个结构,它将是未对齐的,因为我们将从缓冲区中的第 18 个字节 (sizeof(struct inotfy_event) + strlen(name) + 1) 开始复制它,它不能被 alignof(struct inotify_event) 整除。我们需要插入 5 个字节的额外填充,我们需要在第一个数组成员之后手动执行此操作,因此下一个 struct inotify_event 将被复制到第 24 个字节。

但是,我们还需要通知用户/应用程序代码需要将指针递增多少才能到达下一个结构数组成员。所以我们增加 obj->len 填充字节数。所以 obj->len 等于 strlen(name) + 1 + number of padding bytes inserted to make the next array member aligned 或等于 0,在没有名字的情况下。

检查手册页中的示例代码。在我们遍历 struct inotify_events 的循环中,有一行:

ptr += sizeof(struct inotify_event) + event->len

ptr 是指向当前/下一个数组成员的 char* 指针。我们不仅需要将指针增加 sizeof(struct inotify_event),还需要增加 strlen(name) + 1 字节数 + 插入到下一个数组成员的填充。这样我们就可以保持数组成员对齐到他们需要的对齐方式。在下一个位置就是下一个struct inotify_event.

有关更多信息,请浏览 C 中的指针算法和灵活的数组结构成员。