Элемент связного списка описывается структурой:
struct poll_list { struct poll_list *next; int len; struct pollfd entries[0]; };
struct poll_list *next - указатель на следующий элемент списка; int len - длина массива дескрипторов; struct pollfd entries[0] - указатель на массив;
Структура pollfd:
struct pollfd { int fd; short events; short revents; };
Для указателя на массив дескрипторов используется массив нулевой длины. Почему не struct pollfd* entries будет понятно ниже.
Далее имеем вот такой код (несколько упрощённый вариант)
#define POLL_STACK_ALLOC 256 /*Количество элементов массива pollfd, которое поместится на стеке Также учитывается размер элемента списка, т.к. он тоже размещается на стеке*/ #define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list)) / \ sizeof(struct pollfd)) #define POLLFD_PER_PAGE ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd)) int do_sys_poll(struct pollfd *ufds, unsigned int nfds, struct timespec *end_time) { struct poll_wqueues table; int err = -EFAULT, fdcount, len, size; /*Память под элемент списка и массив pollfd*/ long stack_pps[POLL_STACK_ALLOC/sizeof(long)]; /*Указатель на первый элемент списка*/ struct poll_list *const head = (struct poll_list *)stack_pps; /*Указатель для прохода по списку*/ struct poll_list *walk = head; /*Сколько ещё элементов необходимо скопировать*/ unsigned long todo = nfds; /*При перво проходе цикла len содержит количество элементов, которые будут размещены на стеке*/ len = min_t(unsigned int, nfds, N_STACK_PPS); for (;;) { walk->next = NULL; walk->len = len; if (!len) break; /*Копируем данные из пространства пользователя в пространство ядра*/ if (copy_from_user(walk->entries, ufds + nfds-todo, sizeof(struct pollfd) * walk->len)) goto out_fds; /*Сколько ещё осталось скопировать*/ todo -= walk->len; if (!todo) break; /*Все элементы, которые не поместились на стеке, мы размещаем в куче постранично*/ len = min(todo, POLLFD_PER_PAGE); size = sizeof(struct poll_list) + sizeof(struct pollfd) * len; walk = walk->next = kmalloc(size, GFP_KERNEL); if (!walk) { err = -ENOMEM; goto out_fds; } } ................................ return err; }
Макрос N_STACK_PPS вычисляет количество элементов + размер элемента списка, которые могут поместиться на стеке. Получается, что в массиве stack_pps первые sizeof(poll_list) байт отводится под элемент списка, а sizeof(stack_pps) - sizeof(poll_list) байт под массив pollfd.
Надо как то ссылаться на массив pollfd из элемента списка. Известно, что массив располагается сразу после структуры poll_list в массиве stack_pps и в выделяемой в куче памяти. Для того, чтобы получить указатель на область сразу за poll_list используется массив нулевой длинны struct pollfd entries[0]. Это эффективней чем указатель struct pollfd* entries, т.к. он съел бы дополнительные байты памяти, и его необходимо было бы инициализировать адресом первого элемента массива.