Как правильно избежать предупреждения «приведение увеличивает требуемое выравнивание» при чтении (2) структур? - PullRequest
0 голосов
/ 16 сентября 2018

Многие интерфейсы ядра 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> и alignas(struct inotify_event), но безрезультатно.

Я бы хотел исправить это предупреждение, а не просто заставить его замолчать. Как я могу это сделать?

РЕДАКТИРОВАТЬ: Вот как работает read (2) на inotify fd, как документально подтверждено его справочной страницей :

Каждое успешное чтение (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.

Поведение, когда буфер для чтения (2) слишком мал для возврата Информация о следующем событии зависит от версии ядра: в ядра до 2.6.21, read (2) возвращает 0; начиная с ядра 2.6.21, читать (2) не удается с ошибкой EINVAL. Указание буфера размером

sizeof(struct inotify_event) + NAME_MAX + 1

будет достаточно, чтобы прочитать хотя бы одно событие.

Я не могу прочитать частичные структуры, такие как чтение имени отдельно от фрагмента фиксированного размера. Если я не укажу достаточно большой буфер для чтения всей структуры, я ничего не получу.

1 Ответ

0 голосов
/ 16 сентября 2018
union {
     char    buf[1];
     struct some_struct mine;
} b;

Гарантирует, что b.buf и b.mine имеют один и тот же адрес;Более того, любое требуемое выравнивание гарантируется компилятором.Почти никогда не нужны расширения атрибутов (например, alignas), и большое значение имеет наличие источника, свободного от этого crud.

...