linux_api专题:poll


在用户空间应用程序向驱动程序请求数据时,有以下几种方式:

    1、不断查询,条件不满足的情况下就是死循环,非常耗cpu

    2、休眠唤醒的方式,如果条件不满足,应用程序则一直睡眠下去

    3、poll机制,如果条件不满足,休眠指定的时间,休眠时间内条件满足唤醒,条件一直不满足时间到达自动唤醒

    4、异步通知,应用程序注册信号处理函数,驱动程序发信号。类似于QT的信号与槽机制。


应用程序: poll 支持同时查询多个打开的文件

  1. int main(int argc, char **argv)
  2. {
  3. int fd;
  4. unsigned char key_val = 0;
  5. int ret;
  6. struct pollfd fds[1];
  7. fd = open( "/dev/button", O_RDWR);
  8. if (fd < 0)
  9. {
  10. printf( "can't open!\n");
  11. }
  12. fds[ 0].fd = fd;
  13. fds[ 0].events = POLLIN;
  14. while ( 1)
  15. {
  16. ret = poll(fds, 1, 5000);
  17. if (ret == 0)
  18. {
  19. printf( "time out\n");
  20. }
  21. else
  22. {
  23. read(fd, &key_val, 1);
  24. printf( "key_val = 0x%x\n", key_val);
  25. }
  26. }
  27. return 0;
  28. }
内核空间:sys_poll
  1. asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,
  2. long timeout_msecs)
  3. {
  4. s64 timeout_jiffies;
  5. if (timeout_msecs > 0) {
  6. #if HZ > 1000
  7. /* We can only overflow if HZ > 1000 */
  8. if (timeout_msecs / 1000 > (s64) 0x7fffffffffffffffULL / (s64)HZ)
  9. timeout_jiffies = -1;
  10. else
  11. #endif
  12. timeout_jiffies = msecs_to_jiffies(timeout_msecs);
  13. } else {
  14. /* Infinite (< 0) or no (0) timeout */
  15. timeout_jiffies = timeout_msecs;
  16. }
  17. // 转换等待时间,do_sts_poll 是重点
  18. return do_sys_poll(ufds, nfds, &timeout_jiffies);
  19. }
  1. int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
  2. {
  3. struct poll_wqueues table;
  4. ...
  5. long stack_pps[POLL_STACK_ALLOC/ sizeof( long)];
  6. struct poll_list *stack_pp = NULL;
  7. //table->pt->qproc = __pollwait
  8. poll_initwait(&table);
  9. /*
  10. void poll_initwait(struct poll_wqueues *pwq)
  11. {
  12. init_poll_funcptr(&pwq->pt, __pollwait); // pt->qproc = qproc;
  13. pwq->error = 0;
  14. pwq->table = NULL;
  15. pwq->inline_index = 0;
  16. }
  17. */
  18. fdcount = do_poll(nfds, head, &table, timeout);
  19. ...
  20. poll_freewait(&table);
  21. return err;
  22. }
  1. typedef struct poll_table_struct {
  2. poll_queue_proc qproc;
  3. } poll_table;
  4. typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
  do_sys_poll 干了一件重要的事:table->pt->qproc =  __pollwait ,驱动程序中会调用这个函数
  1. static int do_poll(unsigned int nfds, struct poll_list *list,
  2. struct poll_wqueues *wait, s64 *timeout)
  3. {
  4. int count = 0;
  5. poll_table* pt = &wait->pt;
  6. for (;;) {
  7. struct poll_list *walk;
  8. long __timeout;
  9. // 设置当前进程为可中断的
  10. set_current_state(TASK_INTERRUPTIBLE);
  11. for (walk = list; walk != NULL; walk = walk->next) {
  12. struct pollfd * pfd, * pfd_end;
  13. pfd = walk->entries;
  14. pfd_end = pfd + walk->len;
  15. for (; pfd != pfd_end; pfd++) {
  16. // 调用驱动程序中的 poll,返回值>0 count++ ,直接返回,如果是0,休眠
  17. // mask = file->f_op->poll(file, pwait);
  18. // mask &= pollfd->events | POLLERR | POLLHUP;
  19. // pollfd->revents = mask;
  20. if (do_pollfd(pfd, pt)) {
  21. count++;
  22. pt = NULL;
  23. }
  24. }
  25. }
  26. // count非0表示 do_pollfd 至少有一个成功
  27. // 跳出条件:有成功的,时间到,有信号需要处理
  28. if (count || !*timeout || signal_pending(current))
  29. break;
  30. count = wait->error;
  31. if (count)
  32. break;
  33. ...
  34. // 进程的调度,休眠,要么时间到达,再去查询一次看是否满足,要么驱动程序中条件满足,驱动唤醒。
  35. __timeout = schedule_timeout(__timeout);
  36. if (*timeout >= 0)
  37. *timeout += __timeout;
  38. }
  39. // 这里设置进程状态是因为,如果第一次查询就成功了,前面讲进程状态改变了 需要更正回运行状态。
  40. __set_current_state(TASK_RUNNING);
  41. return count;
  42. }
  do_poll 函数中会调用驱动程序中的 poll 函数

驱动程序:

  1. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
  2. static unsigned poll_drv_poll(struct file *file, poll_table *wait)
  3. {
  4. unsigned int mask = 0;
  5. poll_wait(file, &button_waitq, wait); // 将当前进程挂载到休眠队列,但是不会立即休眠
  6. if (ev_press)
  7. mask |= POLLIN | POLLRDNORM; //返回给sys_poll 判断条件是否满足 跳出 or 休眠
  8. return mask;
  9. }
  1. 返回值,合法的事件如下:
  2. POLLIN
  3. 有数据可读。
  4. POLLRDNORM
  5. 有普通数据可读。
  6. POLLRDBAND
  7. 有优先数据可读。
  8. POLLPRI
  9. 有紧迫数据可读。
  10. POLLOUT
  11. 写数据不会导致阻塞。
  12. POLLWRNORM
  13. 写普通数据不会导致阻塞。
  14. POLLWRBAND
  15. 写优先数据不会导致阻塞。
  16. POLLMSG
  17. SIGPOLL 消息可用。
  1. static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
  2. {
  3. if (p && wait_address)
  4. p->qproc(filp, wait_address, p); // __pollwait(filp, wait_address, p)
  5. }
  6. static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
  7. poll_table *p)
  8. {
  9. struct poll_table_entry *entry = poll_get_entry(p);
  10. if (!entry)
  11. return;
  12. get_file(filp);
  13. entry->filp = filp;
  14. entry->wait_address = wait_address;
  15. init_waitqueue_entry(&entry->wait, current);
  16. add_wait_queue(wait_address, &entry->wait);
  17. }
 驱动程序中一开始定义了一个等待队列头,在__pollwait中初始化了等待队列头,并将其加入等待队列。接着就会有下面几种情况:

  1、do_poll 函数第一次查询就OK,直接跳出 for 循环,将进程状态改回running状态,继续执行

  2、do_poll 函数查询失败,进程调度,当前进程进入休眠状态,这个状态下又会产生以下几种情况

    1、在休眠过程中驱动程序条件得到满足,驱动程序唤醒休眠进程,万事大吉

    2、在休眠时间过程中一直不满足,休眠给定时间到达,自动唤醒,再次查询,查询ok 唤醒,查询失败继续休眠给定时间,一直循环下去。

在驱动程序中条件满足,唤醒进程

  1. static irqreturn_t buttons_irq(int irq, void *dev_id)
  2. {
  3. struct keys_desc * keys_desc = (struct keys_desc *)dev_id;
  4. unsigned int keyval;
  5. keyval = s3c2410_gpio_getpin(keys_desc->key_addr);
  6. if (keyval)
  7. {
  8. /* 松开 */
  9. key_val = keys_desc->key_value_up;
  10. }
  11. else
  12. {
  13. /* 按下 */
  14. key_val = keys_desc->key_value_down;
  15. }
  16. ev_press = 1; /* 表示中断发生了 */
  17. wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
  18. return IRQ_RETVAL(IRQ_HANDLED);
  19. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值