首先分析下应用程序的执行过程:
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret;
struct pollfd fds[1];
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!
");
}
fds[0].fd = fd;
fds[0].events = POLLIN;
while (1)
{
ret = poll(fds, 1, 5000); ///本处的应用程序在while(1)中持续的调用poll函数
if (ret == 0)
{
printf("time out
");
}
else
{
read(fd, &key_val, 1);
printf("key_val = 0x%x
", key_val);
}
}
return 0;
}
/**********************************************************************************************************************************/
应用程序中在while(1)循环中持续的调用poll函数,poll函数根据file->op 结构体在其中寻找成员函数poll ->通过调用成员函数poll
由用户空间进入系统内核,内核自动调用sys_poll();函数,接下来介绍内核调用函数顺序过程,缩进代表子函数调用:
app:poll
kernel: sys_poll
do_sys_poll(......, timeout_jffies);
poll_initwait(&table); 设置一个变量 table->qproc=_pollwait 以后会用到
init_poll_funcptr(&pwq->pt,__pollwait); //就是将table->qproc=__pollwait
do_poll(nfds,head,&table,timeout);
for(;;)//死循环 也就是说 根本不给外层用户空间while(1)循环调用的机会,加上后面的休眠机制,所以对系统CPU资源的消耗会比较小
{
if(do_pollfd(pfd,pt))// 这个地方函数分析:实际执行 mask= file->f_op->poll(file,pwait); return mask;
{
count++; //所以 如果 驱动程序中的 poll的返回值非零,那么可以让count++:否则count=0;
pt= NULL;
}
}
//break的条件来了
if(count|| !*timeout||signal_pending(current))
break;
//休眠
_timeout= schedule_timeout(_timeout);
到这里 poll机制的内核调用部分框架结束;
/********************************再将内核驱动程序中的poll函数贴出来*********************************************/
static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不会立即休眠
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
内核调用 sys_poll以后,会调用驱动中的poll函数
然后poll函数主要做两件事情:
1 执行poll_wait(file,&button_waitq,wait); 函数
分析:p->qproc(filp, wait_address, p); 而这个 q->proc 就是之前设置的__pollwait
所以执行_pollwait();函数 作用是将当前进程挂载到运行队列中
2 查询一下(ev_press)这个标志位 检测是否发生了中断,如果是的,那么MASK返回非零值,
内核中就可以break出这个死循环,进而处理别的
如果当时没有发生中断 MASK值为零,那么会进入休眠状态 _timeout*****();这个函数
此时它仍然可以被唤醒 两种情况 一种计时时间到,自动跳出死循环,来到外部应用程序死循环中;第二次执行上述过程
第二种,发生中断情况,此时进入中断处理函数,将ev_press的值变为1. 同时唤醒进程(唤醒以后从哪里开始执行不是很了解!!!)
唤醒进程以后,驱动程序继续运行,继续判断if(ev_press)是否为真,此时poll函数返回非零值 应用程序获取到非零值,执行相应的操作
现在的理解是 _pollwait();函数就是将当前执行代码加载到可执行队列中,也就意味着一旦唤醒 从此处继续执行!!!!
希望会对没有接触过操作系统学linux的同学一点理解poll的思路!