Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2159694
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-11-20 00:45:23

一.总体说明

二.代码分析
2.1 在fs/select.c中 sys_select
  1. int sys_select( unsigned long *buffer )
  2. {
  3. /* Perform the select(nd, in, out, ex, tv) system call. */
  4.     int i;
  5.     fd_set res_in, in = 0, *inp;
  6.     fd_set res_out, out = 0, *outp;
  7.     fd_set res_ex, ex = 0, *exp;
  8.     fd_set mask;
  9.     struct timeval *tvp;
  10.     unsigned long timeout;

  11.     mask = ~((~0) << get_fs_long(buffer++));
  12.     inp = (fd_set *) get_fs_long(buffer++);
  13.     outp = (fd_set *) get_fs_long(buffer++);
  14.     exp = (fd_set *) get_fs_long(buffer++);
  15.     tvp = (struct timeval *) get_fs_long(buffer);

  16.     if (inp)
  17.         in = mask & get_fs_long(inp);
  18.     if (outp)
  19.         out = mask & get_fs_long(outp);
  20.     if (exp)
  21.         ex = mask & get_fs_long(exp);
  22. //1.当用户设置的timeout转为jiffies放在全局的current->timeout中
  23.     timeout = 0xffffffff;                        -->timeout设为-1
  24.     if (tvp) {
  25.         timeout = get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ);   //看清楚这儿是等号
  26.         timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;           //将用户设置的timeout在这儿转为jiffies
  27.         timeout += jiffies;                                                   //加上当前的jiffies就是超时时的jiffies 
  28.     }
  29.     current->timeout = timeout;                                               //将这个超时值放在全局的current->timeout中
  30.     cli();
  31. //2.在do_select中使用
  32.     i = do_select(in, out, ex, &res_in, &res_out, &res_ex);                   //在do_select中会用到这个current->timeout
  33.     if (current->timeout > jiffies)
  34.         timeout = current->timeout - jiffies;
  35.     else
  36.         timeout = 0;
  37.     sti();
  38.     current->timeout = 0;
  39.     if (i < 0)
  40.         return i;
  41.     if (inp) {
  42.         verify_area(inp, 4);
  43.         put_fs_long(res_in,inp);
  44.     }
  45.     if (outp) {
  46.         verify_area(outp,4);
  47.         put_fs_long(res_out,outp);
  48.     }
  49.     if (exp) {
  50.         verify_area(exp,4);
  51.         put_fs_long(res_ex,exp);
  52.     }
  53.     if (tvp) {
  54.         verify_area(tvp, sizeof(*tvp));
  55.         put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
  56.         timeout %= HZ;
  57.         timeout *= (1000000/HZ);
  58.         put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
  59.     }
  60.     if (!i && (current->signal & ~current->blocked))
  61.         return -EINTR;
  62.     return i;
  63. }
2.2 在fs/select.c中 sys_select-->do_select
  1. int do_select(fd_set in, fd_set out, fd_set ex,
  2.     fd_set *inp, fd_set *outp, fd_set *exp)
  3. {
  4.     int count;
  5.     select_table wait_table;
  6.     int i;
  7.     fd_set mask;

  8.     mask = in | out | ex;
  9.     for (i = 0 ; i < NR_OPEN ; i++,mask >>= 1) {
  10.         if (!(mask & 1))
  11.             continue;
  12.         if (!current->filp[i])
  13.             return -EBADF;
  14.         if (!current->filp[i]->f_inode)
  15.             return -EBADF;
  16.         if (current->filp[i]->f_inode->i_pipe)
  17.             continue;
  18.         if (S_ISCHR(current->filp[i]->f_inode->i_mode))
  19.             continue;
  20.         if (S_ISFIFO(current->filp[i]->f_inode->i_mode))
  21.             continue;
  22.         return -EBADF;
  23.     }
  24. repeat:
  25.     wait_table.nr = 0;
  26.     *inp = *outp = *exp = 0;
  27.     count = 0;
  28.     mask = 1;
  29.     for (i = 0 ; i < NR_OPEN ; i++, mask += mask) {
  30.         if (mask & in)
  31.             if (check_in(&wait_table,current->filp[i]->f_inode)) {
  32.                 *inp |= mask;
  33.                 count++;
  34.             }
  35.         if (mask & out)
  36.             if (check_out(&wait_table,current->filp[i]->f_inode)) {
  37.                 *outp |= mask;
  38.                 count++;
  39.             }
  40.         if (mask & ex)
  41.             if (check_ex(&wait_table,current->filp[i]->f_inode)) {
  42.                 *exp |= mask;
  43.                 count++;
  44.             }
  45.     }
  46. //下面的if判断有好几个,这儿只关心current->timout
  47. //如果current->timeout!=0则条件成立进入schedule(),然后goto repeat,即do_select并不结束
  48. //如果current->timeout==0,则条件不成立,free_wait之后就return了
  49.     if (!(current->signal & ~current->blocked) & (wait_table.nr || current->timeout) && !count) {
  50.         current->state = TASK_INTERRUPTIBLE;
  51.         schedule();                          -->这个函数里面有问题
  52.         free_wait(&wait_table);
  53.         goto repeat;
  54.     }
  55.     free_wait(&wait_table);
  56.     return count;
  57. }
a. 如果用户设置了timeout,那么这儿的current->timeout!=0,就会不断的goto repeat,这个do_select就会永远执行下去,
   那么这个timeout就起不到相应的作用了。
b. 肯定是有个地方会在超时结束时改变这个current->timeout,才会使得超时后跳出do_select,这个地方是哪儿呢?
2.3 在kernel/sched.c中
  1. void schedule(void)
  2. {
  3.     for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
  4.         if (*p) {
  5. //如果current->timeouttimeout设为0
  6.             if ((*p)->timeout && (*p)->timeout < jiffies) {
  7.                 (*p)->timeout = 0;
  8.                 if ((*p)->state == TASK_INTERRUPTIBLE)
  9.                     (*p)->state = TASK_RUNNING;
  10.             }
  11.             ...
  12.         }
  13. ...
  14.     switch_to(next);
  15. }
2.4 稍微总结一下
a.如果用户在调用select时设置了timeout,则在sys_select中会把这个timeout转为jiffies,并加上当前的jiffies,构成超时时的jiffies
b. sys_select调用do_select,如果current->timeout不为0,则一直goto repeat进行循环,并调用schedule
c. schedule中进行判断:
   c.1 如果current->timeout>=jiffies说明还没有到超时,不改变,do_select中goto repeate循环
   c.2 如果current->timeout说明超时时间到了,就把current->timeout=0,则do_select循环结束,到了sys_select中了。

2.5沿着这条路再返回去
a. kernel/sched.c中发现current->timeouttimeout=0
b. kernel/select.c中do_select里面if(current->timeout) 条件不成立,就return了
c. kernel/select.c中sys_select里面
  1. int sys_select( unsigned long *buffer )
  2. {
  3.     i = do_select(in, out, ex, &res_in, &res_out, &res_ex);    //这儿返回了
  4.     if (current->timeout > jiffies)             //如果current->timeout>jiffies说明没有超时
  5.         timeout = current->timeout - jiffies;   //没有超时(超时之前)就有描述符可读/写/异常,记录超时时间
  6.     else
  7.         timeout = 0;                            //如果是因为超时返回,就把tiemout设为0
  8.     sti();
  9.     current->timeout = 0
  10.     ...
  11.     if (tvp) {
  12.         verify_area(tvp, sizeof(*tvp));         //把这个时间报给用户层
  13.         put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
  14.         timeout %= HZ;
  15.         timeout *= (1000000/HZ);
  16.         put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
  17.     }

  18. }



阅读(2598) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~