Почему этот союз имеет массив символов в конце? - PullRequest
0 голосов
/ 14 января 2019

Пока я читал реализацию ядра Linux для примитивов блокировки RCU, я дошел до следующего кода.

#define WRITE_ONCE(x, val) \
({                                                      \
        union { typeof(x) __val; char __c[1]; } __u =   \
                { .__val = (__force typeof(x)) (val) }; \
        __write_once_size(&(x), __u.__c, sizeof(x));    \
        __u.__val;                                      \
})


static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
        switch (size) {
        case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
        case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
        case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
        case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
        default:
                barrier();
                __builtin_memcpy((void *)p, (const void *)res, size);
                barrier();
        }
}

Насколько я могу судить, объединение, заканчивающееся массивом char, имеющим размер предыдущей части объединения, позволяет получить доступ к памяти, назначенной объединению, в байтовой гранулярности.

Например,

#define UNION_PARTIAL \
    int a;            \
    int b;            \
    char *c;          \

union union_partial {
    UNION_PARTIAL
};

union union_full {
    UNION_PARTIAL
    char bytes[sizeof(union union_partial)];
};

Однако, похоже, что объединение, используемое в макросе WRITE_ONCE, не объявлено, чтобы обеспечить точный гранулярный доступ к памяти объединения. Вместо этого можно использовать приведенный ниже код, но я не знаю, зачем нам нужен символ __c [1].

#define WRITE_ONCE(x, val) \
({                                                      \
        union { typeof(x) __val; char __c[1]; } __u =   \
                { .__val = (__force typeof(x)) (val) }; \
        __write_once_size(&(x), &(__u.val), sizeof(x)); \
        __u.__val;                                      \
})

только потому, что для программиста необходимо уменьшить нагрузку на & перед __u.val?

1 Ответ

0 голосов
/ 14 января 2019

Давайте остановимся на READ_ONCE(). Компилятор C будет жаловаться, если передать указатель const в функцию, которая принимает указатель void *. __read_once_size объявлен как static __always_inline void __read_once_size(volatile void *p, void *res, int size). Макрос был объявлен как:

#define READ_ONCE(x) \
    ({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; })

вызовет предупреждения, когда в typedef есть const, я думаю, что:

typedef const int type_t;
struct a_s {
   type_t m;
};
struct a_s a = { .m = 1; };
type_t b = READ_ONCE(a.m);

При таком использовании typeof(x) - это const int, поэтому &__val - это const int*, что приведет к появлению предупреждений / ошибок при приведении к void*. При таком использовании const не отбрасывается на typeof, поэтому мы передаем указатель const * на функцию __write_once_size. Поэтому автор решил использовать «трюк объединения», чтобы отбросить константу, передав указатель на массив, начинающийся с того же места, что и значение. (Можно также сделать несколько странных приведений (const void*)(uintptr_t)(void*), но это не было бы так переносимо).

Автор в этом коммите объяснил это и изменил макрос READ_ONCE:

- ({ typeof(x) __val; __read_once_size(&x, &__val, sizeof(__val)); __val; })
+ ({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })

Вероятно, чтобы быть согласованным и соответствовать изменениям READ_ONCE(), макрос WRITE_ONCE() был изменен соответствующим образом в этой фиксации .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...