I. Обзор
В Linux механизм опроса использует (а) написал простой пример механизма реализации опроса. В приводе модули необходимо реализовать struct file_operations
на .poll
член. В модуле привода xxx_poll函数
роли является добавление текущей очереди процесса ожидания, а затем судить , произошло ли событие, произошло возвращение POLLIN | POLLRDNORM
, в противном случае она возвращает 0 (вы можете посмотреть на примере предыдущей главы), а затем проанализировать механизм ядра Linux опроса осуществление.
Во-вторых, опрос механизм
1, системные вызовы
Когда приложение вызывает функцию слой опроса, система генерации линукса вызов (
系统调用入口CALL(sys_poll)
), программа переключается из приложения пространства в пространство ядра, а затем выполнитьsys_poll
функцию (sys_poll функции вfs\select.c
файле).sys_poll
Функция возвращает число частоты рассчитывается на основе времени клеща, а затем вызываетdo_sys_poll
функцию. Блок кода выглядит следующим образом :
/* ufds:应用层传递过来的struct pollfd结构体数组
* nfds:应用层传递过来的struct pollfd结构体个数
* timeout_msecs:超时时间*/
asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs)
{
s64 timeout_jiffies;
if (timeout_msecs > 0) {
/* 大于0,根据时间计算滴答频率数 */
timeout_jiffies = msecs_to_jiffies(timeout_msecs);
} else {
/* Infinite (< 0) or no (0) timeout */
timeout_jiffies = timeout_msecs;
}
/* 然后调用do_sys_poll函数*/
return do_sys_poll(ufds, nfds, &timeout_jiffies);
}
2, функция do_sys_poll
do_sys_poll
Функция вfs\select.c
файле;do_sys_poll
первичная инициализация роли функция Таблица(table->pt->qproc = __pollwait)
, а затем инициализируетсяpoll链表
(список действий опроса находится в пользовательском пространствеstruct pollfd
) , а затем вызываетdo_poll
функцию. Следующий блок кода опущена часть исходного кода. Блок кода выглядит следующим образом :
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
{
struct poll_wqueues table;
struct poll_list *head;
struct poll_list *walk;
.....//省略了部分源代码
if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
return -EINVAL;
/* 初始化 table */
/* table->pt->qproc = __pollwait
* __pollwait 主要将当前进程挂到等待队列中,这里只是做初始化而已。
* 在驱动程序里调用的 poll_wait(file, &button_waitq, wait); 函数里面最终
* 调用的就是table->pt->qproc()函数,也就是调用__pollwait()函数。
*/
poll_initwait(&table);
.....//省略部分源代码(省略的源代码其实就是分配空间和初始化head链表)
/* do_poll函数的参数说明:
*
* nfds: 应用层传递过来的struct pollfd结构体个数
* head: poll链表头,链表里面包含应用层传进来的struct pollfd数组的信息
* table: table->pt->qproc指向__pollwait函数(主要将当前进程挂到等待队列中)
* timeout: 等待超时
*/
/* 调用do_poll函数 */
fdcount = do_poll(nfds, head, &table, timeout);
.....//省略部分源代码(这部分的源代码是将revents(返回的事件) copy 到应用层)
return err;
}
3, функция do_sys_poll
do_sys_poll
Функция вfs\select.c
файле.do_poll
Основной функцией роли является , чтобы установить текущее состояние задачи процесса, а затем траверс список опроса, вызовитеdo_pollfd
функцию, а затем ли текущий процесс сна или просыпаются текущий процесс , основанный на реальной ситуации, блоки кода следующим образом :
//参数说明
/*nfds: 应用层传递过来的struct pollfd结构体个数
*list:poll链表头,链表里面包含应用层传进来的struct pollfd数组的信息
*wait:wait->pt->qproc指向__pollwait函数(主要将当前进程挂到等待队列中)
*timeout:等待超时时间
*/
static int do_poll(unsigned int nfds, struct poll_list *list,
struct poll_wqueues *wait, s64 *timeout)
{
int count = 0;
poll_table* pt = &wait->pt;
/* Optimise the no-wait case */
if (!(*timeout))
pt = NULL;
/* 执行poll的时候有一个大循环 */
for (;;) {
struct poll_list *walk;
long __timeout;
/* 设置当前的任务状态为可中断休眠 */
set_current_state(TASK_INTERRUPTIBLE);
/* 遍历poll链表 */
for (walk = list; walk != NULL; walk = walk->next) {
struct pollfd * pfd, * pfd_end;
/* 获取头部的地址 */
pfd = walk->entries;
/* 获取尾部指针 */
pfd_end = pfd + walk->len;
/* 遍历struct pollfd结构体数组 */
for (; pfd != pfd_end; pfd++) {
/*------------------------------------------------------------------*/
/* do_pollfd其实就是调用开发者所写的xxx_poll函数了(里面调用的的是struct file_operations .poll函数)
* do_pollfd函数下面的代码块分析
*/
/* 执行完do_pollfd后,这个进程就被挂到等待队列里面了。
* 这里仅仅是挂到等待队列而已,进程的休眠是在下面
* 执行 schedule_timeout 函数的时候休眠的
*/
if (do_pollfd(pfd, pt)) {
/* 如果执行驱动程序的poll返回的是非0值 则count++ */
/* 表名设备有数据要返回给应用程序 */
count++;
pt = NULL;
}
/*------------------------------------------------------------------*/
}
}
pt = NULL;
/* 结束大循环的条件有三个:
* count: 表示设备有数据返回给应用程序
* timeout: 等待超时时间到
* signal_pending(current): 当前进程接收到信号
*/
if (count || !*timeout || signal_pending(current))
break;
/* 发生错误退出 */
count = wait->error;
if (count)
break;
if (*timeout < 0) {
/* Wait indefinitely */
__timeout = MAX_SCHEDULE_TIMEOUT;
} else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) {
/*
* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in
* a loop
*/
__timeout = MAX_SCHEDULE_TIMEOUT - 1;
*timeout -= __timeout;
} else {
__timeout = *timeout;
*timeout = 0;
}
/* 执行到这里之后 timeout 的值已经为零,下一次循环就会超时返回 */
/* 这里让当前进程休眠一会
* 唤醒进程的处理休眠的时间到之外,还可以由驱动程序唤醒
*/
__timeout = schedule_timeout(__timeout);
if (*timeout >= 0)
*timeout += __timeout;
}
/* 将当前进程设置为就绪状态,等待CPU调度 */
__set_current_state(TASK_RUNNING);
return count;
}
4, do_pollfd
do_pollfd
Функция вfs\select.c
файле, функция роли в том, чтобы найти дескриптор файлаstruct file
структуру, а затем вызывает ДЕТСКИЙ водительpoll函数
//参数说明
/* pollfd: struct pollfd结构体
* pwait:pwait->qproc指向__pollwait函数(主要将当前进程挂到等待队列中)
*/
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
{
unsigned int mask;
int fd;
mask = 0;
fd = pollfd->fd;
if (fd >= 0) {
int fput_needed;
struct file * file;
/* 根据文件描述符,获取file结构
* file结构是每打开一个文件就会有一个file
*/
file = fget_light(fd, &fput_needed);
mask = POLLNVAL;
if (file != NULL) {
mask = DEFAULT_POLLMASK;
if (file->f_op && file->f_op->poll)
/* 调用驱动里面的poll函数,这个函数是驱动开发者添加的poll函数 */
/* 如果驱动有数据让应用程序读的话,就返回非0, 否侧返回0 */
mask = file->f_op->poll(file, pwait);
/* Mask out unneeded events. */
/* 根据应用层传递的events,来屏蔽不需要的事件 */
mask &= pollfd->events | POLLERR | POLLHUP;
fput_light(file, fput_needed);
}
}
pollfd->revents = mask;
/* 设备有数据可读则返回非0 */
/* 否则返回0 */
return mask;
}
В-третьих, резюме
- Опрос> sys_poll> do_sys_poll> poll_initwait, poll_initwait функция обратного вызова зарегистрирована __pollwait, это когда наши выполняет водитель poll_wait, реальная функция вызывается.
- Затем выполнить Файл-> f_op-> опрос, что мы находимся в функции драйвера опроса собственной реализации
этого будет называть poll_wait повеситься в очередь , которая является движущей силой собственного определения,
но и определить , что оборудование готово- Если устройство не готово, do_sys_poll процесс будет спать в определенное время
- Процесс пробуждаться два условия: 1, «некоторое время»; 2, во-вторых, чтобы быть пробужденным водителя. Когда водитель нашел условия на месте, повесьте завесу активизировани процесса, «очередь», очередь ставятся перед этим процессом по poll_wait повесьте мимо очереди.
- Если водитель не должен проснуться процесс, то chedule_timeout (__ timeou) после тайм-аута, повторите действия 2,3 до вызывающего приложения опроса входящему время прибытия.