许多 Linux 内核接口(inotify 等)通过read(2)
从某个文件描述符中以结构形式获取数据来工作。这样做的代码通常是这样的:
#include <unistd.h>
#include <sys/inotify.h>
int main() {
// all error checking omitted for brevity
int inotify_fd = inotify_init();
inotify_add_watch(inotify_fd, "file_to_watch", IN_ALL_EVENTS);
char c[4096];
for(;;) {
ssize_t len = read(inotify_fd, c, sizeof(c));
struct inotify_event *s;
for(char* p = c; p < c + len; p += sizeof(struct inotify_event) + s->len) {
s = (struct inotify_event *)p;
// do stuff with s
}
}
}
当我用 clang 编译上面的代码时,我收到了这个警告:
inotify.c:13:15: warning: cast from 'char *' to 'struct inotify_event *' increases required alignment from 1 to 4 [-Wcast-align]
s = (struct inotify_event *)p;
^~~~~~~~~~~~~~~~~~~~~~~~~
我第一次尝试修复此警告是修复对齐:我尝试使用#include <stdalign.h>
and alignas(struct inotify_event)
,但无济于事。
我想实际修复此警告,而不仅仅是使其静音。我该怎么做?
编辑:这是 inotify fd 上的 read(2) 的工作方式,如其手册页所述:
每次成功的 read(2) 都会返回一个包含以下一个或多个结构的缓冲区:
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 */ };
[...]
此文件名以空值结尾,并且可能包含更多空字节 ('\0') 以将后续读取对齐到合适的地址边界。
len 字段计算 name 中的所有字节,包括空字节;因此,每个 inotify_event 结构的长度是 sizeof(struct inotify_event)+len。
当给 read(2) 的缓冲区太小而无法返回有关下一个事件的信息时的行为取决于内核版本:在 2.6.21 之前的内核中,read(2) 返回 0;从内核 2.6.21 开始,read(2) 失败并出现错误 EINVAL。指定大小的缓冲区
sizeof(struct inotify_event) + NAME_MAX + 1
足以阅读至少一个事件。
我无法读取部分结构,例如将名称与固定大小的片段分开读取。如果我没有指定足够大的缓冲区来读取整个结构,我什么也得不到。