Элемент связного списка описывается структурой:
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, т.к. он съел бы дополнительные байты памяти, и его необходимо было бы инициализировать адресом первого элемента массива.