许多Linux内核接口(inotify等)通过read(2)
ing数据以某种文件描述符的结构形式工作。这样做的代码经常是这样的:
#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>
和alignas(struct inotify_event)
,但无济于事。
我想实际修复这个警告,而不是沉默它。我怎么能这样做?
编辑:以下是关于inotify fd的read(2)如何工作,如its man page所记录:
每个成功的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字段计算名称中的所有字节,包括空字节;因此,每个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
足以阅读至少一个事件。
我无法读取部分结构,例如从固定尺寸的部分中单独读取名称。如果我没有指定一个足够大的缓冲区来读取整个结构,我就不会得到任何结构。
union {
char buf[1];
struct some_struct mine;
} b;
确保b.buf和b.mine具有相同的地址;此外,编译器保证任何所需的对齐。几乎从不需要属性扩展(例如,alignas),并且在源代码中没有任何价值。