Поймите выравнивание предложения в примере inotify - PullRequest
0 голосов
/ 22 октября 2018

Я читаю справочную страницу inotify здесь , и я пытаюсь понять следующий комментарий в примере

Некоторые системы не могут читать целочисленные переменные, если они не выровнены должным образом,В других системах неправильное выравнивание может снизить производительность.Следовательно, буфер, используемый для чтения из дескриптора файла inotify, должен иметь такое же выравнивание, как struct inotify_event.

И это объявление буфера + определение

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

Я прочитал следующий pdf документ, который дает общее представление о проблемах доступа к выравниванию памяти.

Я хотел бы понять как комментарий inotify, так и наличие некоторых подсказок и ссылок для дальнейшего понимания проблемы выравнивания.Например, я всегда слышу, что нет проблем выравнивания для переменных, размещенных в стеке.Проблема возникает только для буферов, данные которых интерпретируются заново.

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

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, нам понадобится дополнительное место для имени после структуры.Динамическое распределение может выглядеть следующим образом:

    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) = 8 и sizeof(struct inotify_event) = 16 и char name[] = "A";, так что strlen(name) = 1 (strlen исключает подсчет завершающего нулевого байта) и то, что мы хотим сохранить массив inotfiy_events внутри буфера.Сначала мы копируем структуру - 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 и гибкий член структуры массива.

0 голосов
/ 22 октября 2018

Это старый нестандартный способ GCC, указывающий, что массив buf должен начинаться с адреса, который будет подходящим начальным адресом для struct inotify_event.

Это может быть в C11,C17 записывается как

char _Alignas(struct inotify_event) buf[4096];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...