免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2651 | 回复: 9

select没有把进程放入wait queue吗? [复制链接]

论坛徽章:
0
发表于 2010-07-22 13:17 |显示全部楼层
本帖最后由 kgn28 于 2010-07-22 13:20 编辑

记得看过0.12的select实现:
1,检查文件描述符,调用fd->ops->poll,测试文件描述符是否准备好。
2,如果没有,则把自己放到一个wait queue中,然后schedule。
也就是让自己去睡眠,等待被唤醒,但是看2.6的代码时,有些不理解:
http://lxr.linux.no/linux+v2.6.33/fs/select.c
  1. 396int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
  2. 397{
  3. 398        ktime_t expire, *to = NULL;
  4. 399        struct poll_wqueues table;
  5. 400        poll_table *wait;
  6. 401        int retval, i, timed_out = 0;
  7. 402        unsigned long slack = 0;
  8. 403
  9. 404        rcu_read_lock();
  10. 405        retval = max_select_fd(n, fds);
  11. 406        rcu_read_unlock();
  12. 407
  13. 408        if (retval < 0)
  14. 409                return retval;
  15. 410        n = retval;
  16. 411
  17. 412        poll_initwait(&table);
  18. 413        wait = &table.pt;
  19. 414        if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
  20. 415                wait = NULL;
  21. 416                timed_out = 1;
  22. 417        }
  23. 418
  24. 419        if (end_time && !timed_out)
  25. 420                slack = estimate_accuracy(end_time);
  26. 421
  27. 422        retval = 0;
  28. 423        for (;;) {
  29. 424                unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
  30. 425
  31. 426                inp = fds->in; outp = fds->out; exp = fds->ex;
  32. 427                rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
  33. 428
  34. 429                for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
  35. 430                        unsigned long in, out, ex, all_bits, bit = 1, mask, j;
  36. 431                        unsigned long res_in = 0, res_out = 0, res_ex = 0;
  37. 432                        const struct file_operations *f_op = NULL;
  38. 433                        struct file *file = NULL;
  39. 434
  40. 435                        in = *inp++; out = *outp++; ex = *exp++;
  41. 436                        all_bits = in | out | ex;
  42. 437                        if (all_bits == 0) {
  43. 438                                i += __NFDBITS;
  44. 439                                continue;
  45. 440                        }
  46. 441
  47. 442                        for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
  48. 443                                int fput_needed;
  49. 444                                if (i >= n)
  50. 445                                        break;
  51. 446                                if (!(bit & all_bits))
  52. 447                                        continue;
  53. 448                                file = fget_light(i, &fput_needed);
  54. 449                                if (file) {
  55. 450                                        f_op = file->f_op;
  56. 451                                        mask = DEFAULT_POLLMASK;
  57. 452                                        if (f_op && f_op->poll) {
  58. 453                                                wait_key_set(wait, in, out, bit);
  59. 454                                                mask = (*f_op->poll)(file, wait);
  60. 455                                        }
  61. 456                                        fput_light(file, fput_needed);
  62. 457                                        if ((mask & POLLIN_SET) && (in & bit)) {
  63. 458                                                res_in |= bit;
  64. 459                                                retval++;
  65. 460                                                wait = NULL;
  66. 461                                        }
  67. 462                                        if ((mask & POLLOUT_SET) && (out & bit)) {
  68. 463                                                res_out |= bit;
  69. 464                                                retval++;
  70. 465                                                wait = NULL;
  71. 466                                        }
  72. 467                                        if ((mask & POLLEX_SET) && (ex & bit)) {
  73. 468                                                res_ex |= bit;
  74. 469                                                retval++;
  75. 470                                                wait = NULL;
  76. 471                                        }
  77. 472                                }
  78. 473                        }
  79. 474                        if (res_in)
  80. 475                                *rinp = res_in;
  81. 476                        if (res_out)
  82. 477                                *routp = res_out;
  83. 478                        if (res_ex)
  84. 479                                *rexp = res_ex;
  85. 480                        cond_resched();
  86. 481                }
  87. 482                wait = NULL;
  88. 483                if (retval || timed_out || signal_pending(current))
  89. 484                        break;
  90. 485                if (table.error) {
  91. 486                        retval = table.error;
  92. 487                        break;
  93. 488                }
  94. 489
  95. 490                /*
  96. 491                 * If this is the first loop and we have a timeout
  97. 492                 * given, then we convert to ktime_t and set the to
  98. 493                 * pointer to the expiry value.
  99. 494                 */
  100. 495                if (end_time && !to) {
  101. 496                        expire = timespec_to_ktime(*end_time);
  102. 497                        to = &expire;
  103. 498                }
  104. 499
  105. 500                if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
  106. 501                                           to, slack))
  107. 502                        timed_out = 1;
  108. 503        }
  109. 504
  110. 505        poll_freewait(&table);
  111. 506
  112. 507        return retval;
  113. 508}
复制代码
看情形,在for( ; ; )之间,不停的调用:
1,测试
2,cond_resched()
那分明是:没有把当前进程放入到wait queue,然后叫其他进程唤醒,而是不停的执行,直到被抢占或者文件描述符准备好。不知道我哪里理解错了???如果是这样cpu周期不是浪费了吗???
也就是说调用select的进程一直是出于running状态的了。。。

论坛徽章:
0
发表于 2010-07-22 13:53 |显示全部楼层
在fd->ops->poll里面,如果对应fd没有需要的事件,就会把current加入等待队列。
也就是说,current会被加入所有的fd的等待队列(如果它们都没有需要的事件的话)。 加入等待队列的这些waiter管理在table结构里面,退回do_select之前会清理。

在完成对所有fd的poll之后,如果没有发现需要的事件,最后会跑到poll_schedule_timeout,current在这里进入睡眠。

论坛徽章:
0
发表于 2010-07-22 13:55 |显示全部楼层
你可以选择用pause()函数来实现

论坛徽章:
0
发表于 2010-07-22 13:59 |显示全部楼层
pause():

     pause函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR,所以pause只有出错的返回值

   参考《apue2》

论坛徽章:
0
发表于 2010-07-22 14:03 |显示全部楼层
也就是说,current会被加入所有的fd的等待队列(如果它们都没有需要的事件的话)。 加入等待队列的这些waiter管理在table结构里面,退回do_select之前会清理。
回复 2# kouu
这个有代码为证吗?我看了字符终端的poll,貌似没有啊。。。

论坛徽章:
0
发表于 2010-07-22 14:29 |显示全部楼层
先看do_select里面,poll_initwait(&table);

  1. void poll_initwait(struct poll_wqueues *pwq)
  2. {
  3.         init_poll_funcptr(&pwq->pt, __pollwait);
  4.         pwq->polling_task = current;
  5.         pwq->error = 0;
  6.         pwq->table = NULL;
  7.         pwq->inline_index = 0;
  8. }
  9. static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
  10. {
  11.         pt->qproc = qproc;
  12. }
复制代码
这里把 table.pt.qproc 设为 __pollwait, 把polling_task设为current。

再看一下 pipe.c 里面管道的(读端的)poll函数,pipe_poll:

  1. static unsigned int
  2. pipe_poll(struct file *filp, poll_table *wait)
  3. {
  4.         unsigned int mask;
  5.         struct inode *inode = filp->f_path.dentry->d_inode;
  6.         struct pipe_inode_info *pipe = inode->i_pipe;
  7.         int nrbufs;

  8.         poll_wait(filp, &pipe->wait, wait);

  9.         /* Reading only -- no need for acquiring the semaphore.  */
  10.         nrbufs = pipe->nrbufs;
  11.         mask = 0;
  12.         if (filp->f_mode & FMODE_READ) {
  13.                 mask = (nrbufs > 0) ? POLLIN | POLLRDNORM : 0;
  14.                 if (!pipe->writers && filp->f_version != pipe->w_counter)
  15.                         mask |= POLLHUP;
  16.         }

  17.         if (filp->f_mode & FMODE_WRITE) {
  18.                 mask |= (nrbufs < PIPE_BUFFERS) ? POLLOUT | POLLWRNORM : 0;
  19.                 /*
  20.                  * Most Unices do not set POLLERR for FIFOs but on Linux they
  21.                  * behave exactly like pipes for poll().
  22.                  */
  23.                 if (!pipe->readers)
  24.                         mask |= POLLERR;
  25.         }

  26.         return mask;
  27. }

复制代码
一开始就会调用: poll_wait(filp, &pipe->wait, wait);

  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);
  5. }
复制代码
这里实际上就是调用之前初始化的 __pollwait。 再看__pollwait,这里就是加入等待队列

  1. static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
  2.                                 poll_table *p)
  3. {
  4.         struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
  5.         struct poll_table_entry *entry = poll_get_entry(pwq);
  6.         if (!entry)
  7.                 return;
  8.         get_file(filp);
  9.         entry->filp = filp;
  10.         entry->wait_address = wait_address;
  11.         init_waitqueue_func_entry(&entry->wait, pollwake);
  12.         entry->wait.private = pwq;
  13.         add_wait_queue(wait_address, &entry->wait);
  14. }
复制代码

论坛徽章:
0
发表于 2010-07-22 14:50 |显示全部楼层
本帖最后由 kgn28 于 2010-07-22 14:52 编辑

回复 2# kouu


   
poll_wait(filp, &pipe->wait, wait);


这里面__pollwait只是把当前进程加入队列,找了半天没找到,把当前进程状态改变并且schedule将当前进程从运行队列里面删除的代码?
我的意思是类似这样的:
  1. set_current_state(TASK_INTERRUPTIBLE);
  2. schedule();
复制代码
只有cond_resched();,这个不会起到修改进程状态的作用吧???

论坛徽章:
0
发表于 2010-07-22 14:56 |显示全部楼层
在完成对所有fd的poll之后,如果没有发现需要的事件,最后会跑到poll_schedule_timeout,current在这里进入睡眠。
kouu 发表于 2010-07-22 13:53


在 do_select 里面,大for的最后,poll_schedule_timeout

  1. int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
  2.                           ktime_t *expires, unsigned long slack)
  3. {
  4.         int rc = -EINTR;

  5.         set_current_state(state);
  6.         if (!pwq->triggered)
  7.                 rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
  8.         __set_current_state(TASK_RUNNING);

  9.         /*
  10.          * Prepare for the next iteration.
  11.          *
  12.          * The following set_mb() serves two purposes.  First, it's
  13.          * the counterpart rmb of the wmb in pollwake() such that data
  14.          * written before wake up is always visible after wake up.
  15.          * Second, the full barrier guarantees that triggered clearing
  16.          * doesn't pass event check of the next iteration.  Note that
  17.          * this problem doesn't exist for the first iteration as
  18.          * add_wait_queue() has full barrier semantics.
  19.          */
  20.         set_mb(pwq->triggered, 0);

  21.         return rc;
  22. }

复制代码

论坛徽章:
0
发表于 2010-07-22 14:57 |显示全部楼层
cond_resched(); 是在内层的for里面,每poll一个fd就会调一次。 而睡眠的事情,则应当是poll完所有fd之后的

论坛徽章:
0
发表于 2010-07-22 14:59 |显示全部楼层
回复 8# kouu


    {:3_185:} 终于理解了,谢谢。。。。。。。。。。。。。。。。。。。。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP