使用场景: 单片机编程中,主程序等待IIC设备一个事件的发生,如果在允许的时间内发生了就返回1(SUCCESS),否则返回0(ERROR)。
解释:主程序等待IIC返回一个回应值,如果回应就代表可以对IIC读写,如果IIC未启动时,是没有响应的,这时候主程序不可能一直等待,所以应该有一个等待
超时机制,到时间后如果没有响应就直接返回失败,主程序继续其他后续操作。
对于类似上面的情况,
linux是通过poll机制实现超时操作的。
linux在调用poll函数时,如果没有事件发生,则进入休眠状态,如果在规定时间内有事件发生,则返回成功,规定时间过后仍然没有事件发生则返回失败。可见,等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。
使用:
main :
读写函数之前调用poll函数
int poll(
struct pollfd *fds, nfds_t nfds,
int timeout)
输入参数:
fds://可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
struct pollfd {
int fd; /*文件描述符 open打开的那个*/
short events; /*请求的事件类型,监视驱动文件的事件掩码*/
short revents; /*驱动文件实际返回的事件*/
}
nfds: //监测驱动文件的个数
timeout://超时时间,单位是ms
事件类型events 可以为下列值:
POLLIN 有数据可读
POLLRDNORM 有普通数据可读,等效与POLLIN
POLLPRI 有紧迫数据可读
POLLOUT 写数据不会导致阻塞
POLLER 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起事件
POLLNVAL 无效的请求,打不开指定的文件描述符
返回值:
有事件发生 返回revents域不为0的文件描述符个数
超时:return 0
失败:return -1
const struct file_operations dev_fops = {
.read = dev_read,
.poll = dev_poll,
.open = dev_open,
.release = dev_release,
};
dev_fops.poll = dev_poll
机制实现
应用程序:
main->poll
内核程序:
CALL(sys_poll) (call.S)
asmlinkage long sys_poll (struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs) (select.c)
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) (select.c)
void poll_initwait(struct poll_wqueues *pwq) (select.c)
init_poll_funcptr(&pwq->pt,
__pollwait);
(
void init_poll_funcptr(poll_table *pt, poll_queue_proc
qproc
)
)
pt->qproc = qproc;
(poll.h)
也就是说
table->
pt->
qproc =
__pollwait .................................................(1)
static int do_poll(unsigned int nfds, struct poll_list *list, struct poll_wqueues *wait, s64 *timeout) (select.c)
{
for(;;)
{
for (walk = list; walk != NULL; walk = walk->next)
{
struct pollfd * pfd, * pfd_end;
pfd = walk->entries;
pfd_end = pfd + walk->len;
for (; pfd != pfd_end; pfd++)
{
if (do_pollfd(pfd, pt))
{//重点是这里的
do_pollfd ,如果这里
返回真,则count++,大循环退出。
count++;
pt = NULL;
}
}
}
if (count || !*timeout || signal_pending(current)) // count不为零或者
超时直接跳出循环
不执行休眠
break;
__timeout = schedule_timeout(__timeout); //
程序休眠在此处
}
}
void poll_freewait(struct poll_wqueues *pwq) (select.c)
int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
mask =
file->f_op->poll(file, pwait); //这就是驱动中定义的(2) dev_fops.poll = dev_poll
void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
poll_table *p)
entry = poll_get_entry(p)
init_waitqueue_entry(&entry->wait, current);
add_wait_queue(wait_address, &entry->wait);
实际上
__pollwait的内容
就是将poll_table放入wait queue队列中,但是并不执行等待任务。
驱动程序:
dev_fops.poll = dev_poll ..............................................................................................................................................................................(2)
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
p->qproc(filp, wait_address, p)
和(1)对比发现 这里实际上执行的就是__pollwait
执行流程:
1.应用程序调用poll函数后,通过CALL(sys_poll)进入内核,调用sys_poll,然后 sys_poll -> do_sys_poll -> do_sys_poll
2.在do_sys_poll 中先通过poll_initwait 通过
pt->qproc = qproc 链接到__pollwait。
3.在do_poll中循环检测do_pollfd 函数 如果
返回为真则被唤醒,休眠终止,如果返回为假,则进入schedule_timeout进入休眠状态,直至
超时休眠终止。
4.在do_pollfd中实际执行的是
file->f_op->poll 也就是驱动中file_operations所定义的poll函数。
5.file_operations中定义的poll函数调用的是poll_wait,p->qproc对应的就是_pollwait函数。将poll_table的结构体竹子很p添加到task_list队列中。
6.当超时时休眠结束,当驱动发现有数据可以读写时,自动将poll_wait挂载到队列中的进程唤醒。