I. Review
In linux poll mechanism uses (a) wrote a simple example of an implementation mechanism of the poll. In the drive modules need to implement struct file_operations
a .poll
member. In the drive module xxx_poll函数
role is to add to the current process waiting queue; and then judge whether the event occurred, occurred return POLLIN | POLLRDNORM
, otherwise it returns 0 (you can look at the example of the previous chapter); Then analyze the linux kernel poll mechanism implementation.
Two, poll Mechanism
1, the system calls
When the application calls the poll function layer, linux generation system call (
系统调用入口CALL(sys_poll)
), the program is switched from the application space to the kernel space, and then performsys_poll
the function (sys_poll function in thefs\select.c
file).sys_poll
Function returns the number of frequency is calculated based on the time tick, and then callsdo_sys_poll
the function. Code block is as follows:
/* 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 function
do_sys_poll
Function in thefs\select.c
file;do_sys_poll
primary role initialization function Table(table->pt->qproc = __pollwait)
, then initializedpoll链表
(action poll list is located in user spacestruct pollfd
) then callsdo_poll
the function. The following code block is omitted part of the source code. Code block is as follows:
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 function
do_sys_poll
Function in thefs\select.c
file.do_poll
The main function of the role is to set the current process task status, and then traverse the poll list, call thedo_pollfd
function, and then whether the current process to sleep or wake up the current process based on the actual situation, code blocks as follows:
//参数说明
/*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
Function in thefs\select.c
file, the function of the role is, to find the file descriptorstruct file
structure, and then calls the driver'spoll函数
//参数说明
/* 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;
}
Third, the summary
- poll> sys_poll> do_sys_poll> poll_initwait, poll_initwait callback function registered at __pollwait, it is when our driver performs poll_wait, real function is called.
- Next to perform file-> f_op-> poll, that we are in the driver poll function own implementation of
it will call poll_wait hang himself into a queue which is driving our own definition;
it also determine what equipment is ready- If the device is not ready, do_sys_poll process will sleep in a certain time
- Process to be awakened two conditions: 1, "some time" to; 2, the second is to be awakened driver. When the driver found the conditions in place, put the hanging of the wake-up process, "a queue", the queue is put in front of this process by poll_wait hang past the queue.
- If the driver does not have to wake up process, then chedule_timeout (__ timeou) after a timeout, repeat 2,3 action until the calling application poll incoming time of arrival.