select的分析

来源:互联网 发布:python 消息队列 编辑:程序博客网 时间:2024/06/11 03:50

转自出处:http://blog.csdn.net/CodeJoker/article/details/5404395


Linux 2.6.25中的select系统调用主要有4个函数, 层层分工明确: 
sys_select:处理时间参数,调用core_sys_select。 
core_sys_select:处理三个fd_set参数,调用do_select。 
do_select:做select/poll的工作。在合适的时机把自己挂起等待,调用sock_poll。 
sock_poll:用函数指针分派到具体的协议层函数tcp_poll、udp_poll、datagram_poll。

 

[cpp] view plaincopy
  1. /*   
  2. sys_select(fs/select.c)   
  3. 处理了超时值(如果有),将struct timeval转换成了时钟周期数,调用core_sys_select,然后检查剩余时间,处理时间   
  4. */   
  5. asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp,    
  6.          fd_set __user *exp, struct timeval __user *tvp)    
  7. {    
  8.  s64 timeout = -1;    
  9.  struct timeval tv;    
  10.  int ret;    
  11.    
  12.  if (tvp) {/*如果有超时值*/   
  13.   if (copy_from_user(&tv, tvp, sizeof(tv)))    
  14.    return -EFAULT;    
  15.    
  16.   if (tv.tv_sec < 0 || tv.tv_usec < 0)/*时间无效*/   
  17.    return -EINVAL;    
  18.    
  19.   /* Cast to u64 to make GCC stop complaining */   
  20.   if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS)    
  21.    timeout = -1; /* 无限等待*/   
  22.   else {    
  23.    timeout = DIV_ROUND_UP(tv.tv_usec, USEC_PER_SEC/HZ);    
  24.    timeout += tv.tv_sec * HZ;/*计算出超时的相对时间,单位为时钟周期数*/   
  25.   }    
  26.  }    
  27.    
  28.  /*主要工作都在core_sys_select中做了*/   
  29.  ret = core_sys_select(n, inp, outp, exp, &timeout);    
  30.    
  31.  if (tvp) {/*如果有超时值*/   
  32.   struct timeval rtv;    
  33.    
  34.   if (current->personality & STICKY_TIMEOUTS)/*模拟bug的一个机制,不详细描述*/   
  35.    goto sticky;    
  36.   /*rtv中是剩余的时间*/   
  37.   rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ));    
  38.   rtv.tv_sec = timeout;    
  39.   if (timeval_compare(&rtv, &tv) >= 0)/*如果core_sys_select超时返回,更新时间*/   
  40.    rtv = tv;    
  41.   /*拷贝更新后的时间到用户空间*/   
  42.   if (copy_to_user(tvp, &rtv, sizeof(rtv))) {    
  43. sticky:    
  44.    /*   
  45.    * If an application puts its timeval in read-only   
  46.    * memory, we don't want the Linux-specific update to   
  47.    * the timeval to cause a fault after the select has   
  48.    * completed successfully. However, because we're not   
  49.    * updating the timeval, we can't restart the system   
  50.    * call.   
  51.    */   
  52.    if (ret == -ERESTARTNOHAND)/*ERESTARTNOHAND表明,被中断的系统调用*/   
  53.     ret = -EINTR;    
  54.   }    
  55.  }    
  56.    
  57.  return ret;    
  58. }    
  59.    
  60.    
  61.    
  62.    
  63.    
  64.    
  65. /*core_sys_select   
  66. 为do_select准备好了位图,然后调用do_select,将返回的结果集,返回到用户空间   
  67. */   
  68. static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,    
  69.          fd_set __user *exp, s64 *timeout)    
  70. {    
  71.  fd_set_bits fds;    
  72.  void *bits;    
  73.  int ret, max_fds;    
  74.  unsigned int size;    
  75.  struct fdtable *fdt;    
  76.  /* Allocate small arguments on the stack to save memory and be faster */   
  77.    
  78.  /*SELECT_STACK_ALLOC 定义为256*/   
  79.  long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];    
  80.    
  81.  ret = -EINVAL;    
  82.  if (n < 0)    
  83.   goto out_nofds;    
  84.    
  85.  /* max_fds can increase, so grab it once to avoid race */   
  86.  rcu_read_lock();    
  87.  fdt = files_fdtable(current->files);/*获取当前进程的文件描述符表*/   
  88.  max_fds = fdt->max_fds;    
  89.  rcu_read_unlock();    
  90.  if (n > max_fds)/*修正用户传入的第一个参数:fd_set中文件描述符的最大值*/   
  91.   n = max_fds;    
  92.    
  93.  /*   
  94.  * We need 6 bitmaps (in/out/ex for both incoming and outgoing),   
  95.  * since we used fdset we need to allocate memory in units of   
  96.  * long-words.    
  97.  */   
  98.    
  99.  /*   
  100.  如果stack_fds数组的大小不能容纳下所有的fd_set,就用kmalloc重新分配一个大数组。   
  101.  然后将位图平均分成份,并初始化fds结构   
  102.  */   
  103.  size = FDS_BYTES(n);    
  104.  bits = stack_fds;    
  105.  if (size > sizeof(stack_fds) / 6) {    
  106.   /* Not enough space in on-stack array; must use kmalloc */   
  107.   ret = -ENOMEM;    
  108.   bits = kmalloc(6 * size, GFP_KERNEL);    
  109.   if (!bits)    
  110.    goto out_nofds;    
  111.  }    
  112.  fds.in      = bits;    
  113.  fds.out     = bits +   size;    
  114.  fds.ex      = bits + 2*size;    
  115.  fds.res_in  = bits + 3*size;    
  116.  fds.res_out = bits + 4*size;    
  117.  fds.res_ex  = bits + 5*size;    
  118.    
  119.  /*get_fd_set仅仅调用copy_from_user从用户空间拷贝了fd_set*/   
  120.  if ((ret = get_fd_set(n, inp, fds.in)) ||    
  121.   (ret = get_fd_set(n, outp, fds.out)) ||    
  122.   (ret = get_fd_set(n, exp, fds.ex)))    
  123.   goto out;    
  124.    
  125.  zero_fd_set(n, fds.res_in);    
  126.  zero_fd_set(n, fds.res_out);    
  127.  zero_fd_set(n, fds.res_ex);    
  128.    
  129.    
  130.  /*   
  131.  接力棒传给了do_select   
  132.  */   
  133.  ret = do_select(n, &fds, timeout);    
  134.    
  135.  if (ret < 0)    
  136.   goto out;    
  137.    
  138.  /*do_select返回,是一种异常状态*/   
  139.  if (!ret) {    
  140.   /*记得上面的sys_select不?将ERESTARTNOHAND转换成了EINTR并返回。EINTR表明系统调用被中断*/   
  141.   ret = -ERESTARTNOHAND;    
  142.   if (signal_pending(current))/*当当前进程有信号要处理时,signal_pending返回真,这符合了EINTR的语义*/   
  143.    goto out;    
  144.   ret = 0;    
  145.  }    
  146.    
  147.  /*把结果集,拷贝回用户空间*/   
  148.  if (set_fd_set(n, inp, fds.res_in) ||    
  149.   set_fd_set(n, outp, fds.res_out) ||    
  150.   set_fd_set(n, exp, fds.res_ex))    
  151.   ret = -EFAULT;    
  152.    
  153. out:    
  154.  if (bits != stack_fds)    
  155.   kfree(bits);/*对应上面的kmalloc*/   
  156. out_nofds:    
  157.  return ret;    
  158. }    
  159.    
  160.    
  161.    
  162.    
  163.    
  164.    
  165.    
  166.    
  167. /*do_select   
  168. 真正的select在此,遍历了所有的fd,调用对应的xxx_poll函数   
  169. */   
  170. int do_select(int n, fd_set_bits *fds, s64 *timeout)    
  171. {    
  172.  struct poll_wqueues table;    
  173.  poll_table *wait;    
  174.  int retval, i;    
  175.    
  176.  rcu_read_lock();    
  177.  /*根据已经打开fd的位图检查用户打开的fd, 要求对应fd必须打开, 并且返回最大的fd*/   
  178.  retval = max_select_fd(n, fds);    
  179.  rcu_read_unlock();    
  180.    
  181.  if (retval < 0)    
  182.   return retval;    
  183.  n = retval;    
  184.    
  185.    
  186.  /*将当前进程放入自已的等待队列table, 并将该等待队列加入到该测试表wait*/   
  187.  poll_initwait(&table);    
  188.  wait = &table.pt;    
  189.    
  190.  if (!*timeout)    
  191.   wait = NULL;    
  192.  retval = 0;    
  193.    
  194.  for (;;) {/*死循环*/   
  195.   unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;    
  196.   long __timeout;    
  197.    
  198.   /*注意:可中断的睡眠状态*/   
  199.   set_current_state(TASK_INTERRUPTIBLE);    
  200.    
  201.   inp = fds->in; outp = fds->out; exp = fds->ex;    
  202.   rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;    
  203.    
  204.    
  205.   for (i = 0; i < n; ++rinp, ++routp, ++rexp) {/*遍历所有fd*/   
  206.    unsigned long in, out, ex, all_bits, bit = 1, mask, j;    
  207.    unsigned long res_in = 0, res_out = 0, res_ex = 0;    
  208.    const struct file_operations *f_op = NULL;    
  209.    struct file *file = NULL;    
  210.    
  211.    in = *inp++; out = *outp++; ex = *exp++;    
  212.    all_bits = in | out | ex;    
  213.    if (all_bits == 0) {    
  214.     /*   
  215.     __NFDBITS定义为(8 * sizeof(unsigned long)),即long的位数。   
  216.     因为一个long代表了__NFDBITS位,所以跳到下一个位图i要增加__NFDBITS   
  217.     */   
  218.     i += __NFDBITS;    
  219.     continue;    
  220.    }    
  221.    
  222.    for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {    
  223.     int fput_needed;    
  224.     if (i >= n)    
  225.      break;    
  226.    
  227.     /*测试每一位*/   
  228.     if (!(bit & all_bits))    
  229.      continue;    
  230.    
  231.     /*得到file结构指针,并增加引用计数字段f_count*/   
  232.     file = fget_light(i, &fput_needed);    
  233.     if (file) {    
  234.      f_op = file->f_op;    
  235.      mask = DEFAULT_POLLMASK;    
  236.    
  237.      /*对于socket描述符,f_op->poll对应的函数是sock_poll   
  238.      注意第三个参数是等待队列,在poll成功后会将本进程唤醒执行*/   
  239.      if (f_op && f_op->poll)    
  240.       mask = (*f_op->poll)(file, retval ? NULL : wait);    
  241.    
  242.      /*释放file结构指针,实际就是减小他的一个引用计数字段f_count*/   
  243.      fput_light(file, fput_needed);    
  244.    
  245.      /*根据poll的结果设置状态,要返回select出来的fd数目,所以retval++。   
  246.      注意:retval是in out ex三个集合的总和*/   
  247.      if ((mask & POLLIN_SET) && (in & bit)) {    
  248.       res_in |= bit;    
  249.       retval++;    
  250.      }    
  251.      if ((mask & POLLOUT_SET) && (out & bit)) {    
  252.       res_out |= bit;    
  253.       retval++;    
  254.      }    
  255.      if ((mask & POLLEX_SET) && (ex & bit)) {    
  256.       res_ex |= bit;    
  257.       retval++;    
  258.      }    
  259.     }    
  260.    
  261.     /*   
  262.     注意前面的set_current_state(TASK_INTERRUPTIBLE);   
  263.     因为已经进入TASK_INTERRUPTIBLE状态,所以cond_resched回调度其他进程来运行,   
  264.     这里的目的纯粹是为了增加一个抢占点。被抢占后,由等待队列机制唤醒。   
  265.   
  266.     在支持抢占式调度的内核中(定义了CONFIG_PREEMPT),cond_resched是空操作   
  267.     */     
  268.     cond_resched();    
  269.    }    
  270.    /*根据poll的结果写回到输出位图里*/   
  271.    if (res_in)    
  272.     *rinp = res_in;    
  273.    if (res_out)    
  274.     *routp = res_out;    
  275.    if (res_ex)    
  276.     *rexp = res_ex;    
  277.   }    
  278.   wait = NULL;    
  279.   if (retval || !*timeout || signal_pending(current))/*signal_pending前面说过了*/   
  280.    break;    
  281.   if(table.error) {    
  282.    retval = table.error;    
  283.    break;    
  284.   }    
  285.    
  286.   if (*timeout < 0) {    
  287.    /*无限等待*/   
  288.    __timeout = MAX_SCHEDULE_TIMEOUT;    
  289.   } else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT - 1)) {    
  290.    /* 时间超过MAX_SCHEDULE_TIMEOUT,即schedule_timeout允许的最大值,用一个循环来不断减少超时值*/   
  291.    __timeout = MAX_SCHEDULE_TIMEOUT - 1;    
  292.    *timeout -= __timeout;    
  293.   } else {    
  294.    /*等待一段时间*/   
  295.    __timeout = *timeout;    
  296.    *timeout = 0;    
  297.   }    
  298.    
  299.   /*TASK_INTERRUPTIBLE状态下,调用schedule_timeout的进程会在收到信号后重新得到调度的机会,   
  300.   即schedule_timeout返回,并返回剩余的时钟周期数   
  301.   */   
  302.   __timeout = schedule_timeout(__timeout);    
  303.   if (*timeout >= 0)    
  304.    *timeout += __timeout;    
  305.  }    
  306.    
  307.  /*设置为运行状态*/   
  308.  __set_current_state(TASK_RUNNING);    
  309.  /*清理等待队列*/   
  310.  poll_freewait(&table);    
  311.    
  312.  return retval;    
  313. }    
  314.    
  315.    
  316. static unsigned int sock_poll(struct file *file, poll_table *wait)    
  317. {    
  318.  struct socket *sock;    
  319.    
  320.  /*约定socket的file->private_data字段放着对应的socket结构指针*/   
  321.  sock = file->private_data;    
  322.    
  323.  /*对应了三个协议的函数tcp_poll,udp_poll,datagram_poll,其中udp_poll几乎直接调用了datagram_poll   
  324.  累了,先休息一下,这三个函数以后分析*/   
  325.  return sock->ops->poll(file, sock, wait);    
  326. }    

 

其他重要函数一览 
static int max_select_fd(unsigned long n, fd_set_bits *fds) 
返回在fd_set中已经打开的,并且小于用户指定最大值,的fd

static inline int get_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset) 
从用户空间拷贝fd_set到内核

static inline void zero_fd_set(unsigned long nr, unsigned long *fdset) 
把fd_set清零

static inline unsigned long __must_check set_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset) 
把fd_set拷贝回用户空间


static inline int signal_pending(struct task_struct *p) 
目前进程有信号需要处理

struct file *fget_light(unsigned int fd, int *fput_needed) 
由fd得到其对应的file结构指针,并增加其引用计数

static inline void fput_light(struct file *file, int fput_needed) 
释放由fget_light得到的file结构指针,减少其引用计数

set_current_state 
设置当前进程的状态

static inline int cond_resched(void) 
判断是否有进程需要抢占当前进程,如果是将立即发生调度。就是额外增加一个抢占点。

signed long __sched schedule_timeout(signed long timeout) 
当前进程睡眠timeout个jiffies

rcu_read_lock 
rcu_read_unlock 
Linux 2.6新加入的rcu锁。读锁的加锁、解锁函数 
参考http://www.ibm.com/developerworks/cn/linux/l-rcu


poll_freewait 
poll_initwait 
poll_wait 
... 
和文件IO,poll机制有关的几个函数,参考《Linux设备驱动(第三版)》6.3

tcp_poll 
udp_poll 
datagram_poll 
协议层的poll函数

0 0