linux系统调用select

I/O复用技术是:把我们关注的描述符组成一个描述符表(通常不止一个描述符),调用I/O复用函数(select/poll/epoll),当描述符表中有可进行非阻塞I/O操作的描述符时,复用函数返回;否则阻塞复用函数,直到描述符表中有可进行非阻塞I/O操作的描述符出现时,才唤醒进程继续执行复用函数;当复用函数正常返回时,就可以知道哪些描述符可进行非阻塞I/O操作。
I/O复用的描述符通常包括:终端/伪终端,pipes,socket等

 

I/O复用函数主要过程:
1.遍历描述符表,判断该描述符表中是否有描述符可进行非阻塞I/O操作(读、写、异常等);
2.如果描述符表中有描述符可进行非阻塞I/O操作,I/O复用函数通知用户进程这些描述符;
3.如果描述符表中没有描述符可进行非阻塞I/O操作,那么I/O复用函数被阻塞,并将进程添加到描述符表中所有描述符的poll等待队列中
4.当有描述符可进行非阻塞I/O操作时,内核唤醒该描述符poll等待队列中的阻塞进程;进程唤醒后继续执行I/O复用函数,I/O复用函数将进程从描述符表中所有描述符的poll等待队列中移除;然后重新遍历描述符表

 

I.select
select是I/O复用函数之一,其原型为:

int select(int nfds, fd_set *restrict ,
              fd_set *restrict writefds, fd_set *restrict errorfds,
              struct timeval *restrict timeout);

输入参数:
1、readfds、writefds、errorfds:我们所关心的可进行非阻塞读、写、异常操作的描述符表
2、nfds:文件描述符表大小
3、timeout:超时时间(一直、指定时间、不等待),当超时后仍没有文件可进行非阻塞I/O操作时,复用函数返回。
输出参数:
1、readfds、writefds、errorfds:可进行非阻塞读、写、异常操作的描述符表
2、timeout:有超时设置时,超时剩余的时间;即输出timeout=输入timeout-select所花时间
返回值:
0:超时
-1:错误
>0:可进行非阻塞读、写、异常操作的描述符表大小,即readfds、writefds、errorfds中位置1的个数,如一个文件即可进行读也可进行写则会算成2

注:
  在输入参数readfds、writefds中有,但是在输入参数errorfds中没有的描述符,当这些描述符表示的文件有错误时,会在输出参数errorfds中包含该描述符,并且I/O复用函数立即返回。
  输出参数readfds是输入参数readfds的子集,输出参数writefds是输入参数writefds的子集,输出参数errorfds是输出参数readfds,writefds,errorfds的子集


II.数据结构

i.fd_set_bits
 80 /*
 81  * Scaleable version of the fd_set.
 82  */
 83 
 84 typedef struct {
 85         unsigned long *in, *out, *ex;
 86         unsigned long *res_in, *res_out, *res_ex;
 87 } fd_set_bits;
 88 
 89 /*
 90  * How many longwords for "nr" bits?
 91  */
 92 #define FDS_BITPERLONG  (8*sizeof(long))
 93 #define FDS_LONGS(nr)   (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
 94 #define FDS_BYTES(nr)   (FDS_LONGS(nr)*sizeof(long))

fd_set_bits:用于标识出可读、可写、异常描述符位图内核内存块起始位置(包括输入和输出);内存块的大小是FDS_BYTES(nfds),与select函数参数nfds的大小对应,FDS_BYTES会对nfds大小做long对齐(即32或64对齐)
fd_set_bits用法如下图:

ii.poll_wqueues

 33 typedef struct poll_table_struct {
 34         poll_queue_proc qproc;
 35         unsigned long key;
 36 } poll_table;
 
 50 struct poll_table_entry {
 51         struct file *filp;
 52         unsigned long key;
 53         wait_queue_t wait;
 54         wait_queue_head_t *wait_address;
 55 };
 56 
 57 /*
 58  * Structures and helpers for sys_poll/sys_poll
 59  */
 60 struct poll_wqueues {
 61         poll_table pt;
 62         struct poll_table_page *table;
 63         struct task_struct *polling_task;
 64         int triggered;
 65         int error;
 66         int inline_index;
 67         struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
 68 };

poll_table:对每个文件进行poll操作时,判断是否能够非阻塞的进行key值(poll事件组成)标识的I/O操作;如果不能,调用回调函数qproc将进程添加到文件的poll等待队列中
poll_table_entry:用于阻塞进程并将进程添加到文件的poll等待队列中,一个文件对应一个poll_table_entry
poll_wqueues:用于在select/poll时,如果需要阻塞进程,将进程添加到描述符表标识的所有文件的poll等待队列中,以便任意一个文件可进行非阻塞I/O操作时唤醒进程

iii.进程、打开文件、poll等待队列之间关系图


 

III.复用函数阻塞/唤醒
i.poll_wqueues的初始化

 44 static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
 45 {
 46         pt->qproc = qproc;
 47         pt->key   = ~0UL; /* all events enabled */
 48 }

116 void poll_initwait(struct poll_wqueues *pwq)
117 {
118         init_poll_funcptr(&pwq->pt, __pollwait);
119         pwq->polling_task = current;
120         pwq->triggered = 0;
121         pwq->error = 0;
122         pwq->table = NULL;
123         pwq->inline_index = 0;
124 }

1.将阻塞回调函数设置成__pollwait
2.将阻塞进程设置成当前进程


ii.文件poll阻塞

1.poll阻塞
当对单个文件执行poll操作时,如果文件不能非阻塞的进行key标识的I/O操作,会将当前进程添加到该文件的poll等待队列中
tcp阻塞f_op->poll:socket_file_ops->sock_poll->inet_stream_ops->tcp_poll->sock_poll_wait->poll_wait
pipe阻塞f_op->poll:write_pipefifo_fops->pipe_poll->poll_wait

 38 static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
 39 {
 40         if (p && wait_address)
 41                 p->qproc(filp, wait_address, p);
 42 }

216 static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
217                                 poll_table *p)
218 {
219         struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
220         struct poll_table_entry *entry = poll_get_entry(pwq);
221         if (!entry)
222                 return;
223         get_file(filp);
224         entry->filp = filp;
225         entry->wait_address = wait_address;
226         entry->key = p->key;
227         init_waitqueue_func_entry(&entry->wait, pollwake);
228         entry->wait.private = pwq;
229         add_wait_queue(wait_address, &entry->wait);
230 }

a.poll_wait中的qproc在poll_initwait时设置成__pollwait;如果poll_table与wait_address非NULL,则调用__poll_wait
b.将poll等待队列的waiter唤醒函数设置成pollwake
c.将poll_table_entry放入wait_address(socket为sock->sk_sleep,pipe为pipe_inode_info->wait)的等待队列中

 

3.poll_get_entry

 98 #define POLL_TABLE_FULL(table) \
 99         ((unsigned long)((table)->entry+1) > PAGE_SIZE + (unsigned long)(table))
 
155 static struct poll_table_entry *poll_get_entry(struct poll_wqueues *p)
156 {
157         struct poll_table_page *table = p->table;
158 
159         if (p->inline_index < N_INLINE_POLL_ENTRIES)
160                 return p->inline_entries + p->inline_index++;
161 
162         if (!table || POLL_TABLE_FULL(table)) {
163                 struct poll_table_page *new_table;
164 
165                 new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);
166                 if (!new_table) {
167                         p->error = -ENOMEM;
168                         return NULL;
169                 }
170                 new_table->entry = new_table->entries;
171                 new_table->next = table;
172                 p->table = new_table;
173                 table = new_table;
174         }
175 
176         return table->entry++;
177 }

a.poll_get_entry用于获取poll_wqueues中的poll_table_entry
b.如果poll_wqueues的INLINE空间有空闲entry,则从INLINE空间中分配entry
c.如果INLINE空间没有空闲entry,则分配新页帧作为poll_table_page;新poll_table_page插入链表头,以便下次分配只查看首结点就能知道是否有空闲entry

 

iii.文件poll唤醒
当复用函数被阻塞后,如果有异步事件出现而使文件能非阻塞的进行key标识的I/O操作时,会调用wake_up_interruptible_sync_poll唤醒被阻塞的复用函数
tcp数据接收事件唤醒复用函数:tcp_protocol->tcp_v4_rcv->tcp_v4_do_rcv->tcp_rcv_established->sk_data_ready->sock_def_readable->wake_up_interruptible_sync_poll
pipe写事件唤醒复用函数:pipe_write->wake_up_interruptible_sync_poll

164 #define wake_up_interruptible_sync(x)   __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)

5897 /*
 5898  * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just
 5899  * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve
 5900  * number) then we wake all the non-exclusive tasks and one exclusive task.
 5901  *
 5902  * There are circumstances in which we can try to wake a task which has already
 5903  * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns
 5904  * zero in this (rare) case, and we handle it by continuing to scan the queue.
 5905  */
 5906 static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
 5907                         int nr_exclusive, int wake_flags, void *key)
 5908 {
 5909         wait_queue_t *curr, *next;
 5910 
 5911         list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
 5912                 unsigned flags = curr->flags;
 5913 
 5914                 if (curr->func(curr, mode, wake_flags, key) &&
 5915                                 (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
 5916                         break;
 5917         }
 5918 }
 5954 /**
 5955  * __wake_up_sync_key - wake up threads blocked on a waitqueue.
 5956  * @q: the waitqueue
 5957  * @mode: which threads
 5958  * @nr_exclusive: how many wake-one or wake-many threads to wake up
 5959  * @key: opaque value to be passed to wakeup targets
 5960  *
 5961  * The sync wakeup differs that the waker knows that it will schedule
 5962  * away soon, so while the target thread will be woken up, it will not
 5963  * be migrated to another CPU - ie. the two threads are 'synchronized'
 5964  * with each other. This can prevent needless bouncing between CPUs.
 5965  *
 5966  * On UP it can prevent extra preemption.
 5967  *
 5968  * It may be assumed that this function implies a write memory barrier before
 5969  * changing the task state if and only if any tasks are woken up.
 5970  */
 5971 void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode,
 5972                         int nr_exclusive, void *key)
 5973 {
 5974         unsigned long flags;
 5975         int wake_flags = WF_SYNC;
 5976 
 5977         if (unlikely(!q))
 5978                 return;
 5979 
 5980         if (unlikely(!nr_exclusive))
 5981                 wake_flags = 0;
 5982 
 5983         spin_lock_irqsave(&q->lock, flags);
 5984         __wake_up_common(q, mode, nr_exclusive, wake_flags, key);
 5985         spin_unlock_irqrestore(&q->lock, flags);
 5986 }
 5987 EXPORT_SYMBOL_GPL(__wake_up_sync_key);
 5988 
 5989 /*
 5990  * __wake_up_sync - see __wake_up_sync_key()
 5991  */
 5992 void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive)
 5993 {
 5994         __wake_up_sync_key(q, mode, nr_exclusive, NULL);
 5995 }
 5996 EXPORT_SYMBOL_GPL(__wake_up_sync);      /* For internal use only */

wake_up_interruptible_sync同步唤醒一个等待进程
__wake_up_common中的func是由__pollwait设置成的pollwake

179 static int __pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)
180 {
181         struct poll_wqueues *pwq = wait->private;
182         DECLARE_WAITQUEUE(dummy_wait, pwq->polling_task);
183 
184         /*
185          * Although this function is called under waitqueue lock, LOCK
186          * doesn't imply write barrier and the users expect write
187          * barrier semantics on wakeup functions.  The following
188          * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up()
189          * and is paired with set_mb() in poll_schedule_timeout.
190          */
191         smp_wmb();
192         pwq->triggered = 1;
193 
194         /*
195          * Perform the default wake up operation using a dummy
196          * waitqueue.
197          *
198          * TODO: This is hacky but there currently is no interface to
199          * pass in @sync.  @sync is scheduled to be removed and once
200          * that happens, wake_up_process() can be used directly.
201          */
202         return default_wake_function(&dummy_wait, mode, sync, key);
203 }
204 
205 static int pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)
206 {
207         struct poll_table_entry *entry;
208 
209         entry = container_of(wait, struct poll_table_entry, wait);
210         if (key && !((unsigned long)key & entry->key))
211                 return 0;
212         return __pollwake(wait, mode, sync, key);
213 }

1.如果唤醒进程的事件不是复用函数所关心的事件,则不会去唤醒复用函数;如果是关心的事件,则调用__pollwake唤醒复用函数
2.将triggered置1 ;在遍历完复用函数所提供的文件描述符表后,如果没有满足的描述符时会阻塞进程;但是如果已经遍历的文件在遍历的过程中,有异步事件出现而使文件能非阻塞的进行key标识的I/O操作时,则不会去阻塞进程;阻塞进程时会检查triggered标识,如果是0才会去阻塞进程 ,否则不会去阻塞。
3.通过default_wake_function->try_to_wake_up唤醒复用函数的调用进程(当进程已经是TASK_RUNNING时,则直接返回)

 

iv.poll_wqueues释放

127 static void free_poll_entry(struct poll_table_entry *entry)
128 {
129         remove_wait_queue(entry->wait_address, &entry->wait);
130         fput(entry->filp);
131 }
132 
133 void poll_freewait(struct poll_wqueues *pwq)
134 {
135         struct poll_table_page * p = pwq->table;
136         int i;
137         for (i = 0; i < pwq->inline_index; i++)
138                 free_poll_entry(pwq->inline_entries + i);
139         while (p) {
140                 struct poll_table_entry * entry;
141                 struct poll_table_page *old;
142 
143                 entry = p->entry;
144                 do {
145                         entry--;
146                         free_poll_entry(entry);
147                 } while (entry > p->entries);
148                 old = p;
149                 p = p->next;
150                 free_page((unsigned long) old);
151         }
152 }

1.由于在遍历描述符表中文件的过程中,不知道未遍历到的文件能否非阻塞的进行key标识的I/O操作;所以当前文件不能非阻塞的进行key标识的I/O操作时,就会将进程添加到文件的poll等待队列中,以便后续文件不能非阻塞的进行I/O操作时不用再遍历描述符表去将进程添加到文件的poll等待队列中。
2.不管是阻塞被唤醒(进程添加到描述符表中所有文件的poll队列中)还是未阻塞(进程已经添加到描述符表中可进行非阻塞I/O操作文件之前的所有文件的poll队列中),在复用函数退出时,都会调用poll_freewait将poll_wqueues中所有的waiter从文件的等待队列中清空,及释放相应的文件及内存

 

IV.select实现
i.select
fs/select.c:

596 SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
597                 fd_set __user *, exp, struct timeval __user *, tvp)
598 {
599         struct timespec end_time, *to = NULL;
600         struct timeval tv;
601         int ret;
602 
603         if (tvp) {
604                 if (copy_from_user(&tv, tvp, sizeof(tv)))
605                         return -EFAULT;
606 
607                 to = &end_time;
608                 if (poll_select_set_timeout(to,
609                                 tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
610                                 (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
611                         return -EINVAL;
612         }
613 
614         ret = core_sys_select(n, inp, outp, exp, to);
615         ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
616 
617         return ret;
618 }

1.将超时参数timeout由用户空间复制到内核空间,并将其由timeval类型转换成timespec类型
2.调用core_sys_select
3.计算超时剩余时间

ii.core_sys_select

521 int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
522                            fd_set __user *exp, struct timespec *end_time)
523 {
524         fd_set_bits fds;
525         void *bits;
526         int ret, max_fds;
527         unsigned int size;
528         struct fdtable *fdt;
529         /* Allocate small arguments on the stack to save memory and be faster */
530         long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
531 
532         ret = -EINVAL;
533         if (n < 0)
534                 goto out_nofds;
535 
536         /* max_fds can increase, so grab it once to avoid race */
537         rcu_read_lock();
538         fdt = files_fdtable(current->files);
539         max_fds = fdt->max_fds;
540         rcu_read_unlock();
541         if (n > max_fds)
542                 n = max_fds;
543 
544         /*
545          * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
546          * since we used fdset we need to allocate memory in units of
547          * long-words. 
548          */
549         size = FDS_BYTES(n);
550         bits = stack_fds;
551         if (size > sizeof(stack_fds) / 6) {
552                 /* Not enough space in on-stack array; must use kmalloc */
553                 ret = -ENOMEM;
554                 bits = kmalloc(6 * size, GFP_KERNEL);
555                 if (!bits)
556                         goto out_nofds;
557         }
558         fds.in      = bits;
559         fds.out     = bits +   size;
560         fds.ex      = bits + 2*size;
561         fds.res_in  = bits + 3*size;
562         fds.res_out = bits + 4*size;
563         fds.res_ex  = bits + 5*size;
564 
565         if ((ret = get_fd_set(n, inp, fds.in)) ||
566             (ret = get_fd_set(n, outp, fds.out)) ||
567             (ret = get_fd_set(n, exp, fds.ex)))
568                 goto out;
569         zero_fd_set(n, fds.res_in);
570         zero_fd_set(n, fds.res_out);
571         zero_fd_set(n, fds.res_ex);
572 
573         ret = do_select(n, &fds, end_time);
575         if (ret < 0)
576                 goto out;
577         if (!ret) {
578                 ret = -ERESTARTNOHAND;
579                 if (signal_pending(current))
580                         goto out;
581                 ret = 0;
582         }
583 
584         if (set_fd_set(n, inp, fds.res_in) ||
585             set_fd_set(n, outp, fds.res_out) ||
586             set_fd_set(n, exp, fds.res_ex))
587                 ret = -EFAULT;
588 
589 out:
590         if (bits != stack_fds)
591                 kfree(bits);
592 out_nofds:
593         return ret;
594 }

1.输入参数n(描述符表大小)检查,如果大于进程打开的最大描述符,则以打开的最大文件描述符为准
2.将读、写、异常描述符表位图从用户空间复制到内核空间,并由fd_set_bits标识出位图超始位置;并将返回读、写、异常描述符表位图置0
3.调用do_select
4.当有信号产生时返回ERESTARTNOHAND,通知内核信号处理完成后自动重新调用select5.将返回文件描述符表位图由内核空间复制到用户空间

5.将返回文件描述符表位图由内核空间复制到用户空间

 

iii.do_select

396 int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
397 {
398         ktime_t expire, *to = NULL;
399         struct poll_wqueues table;
400         poll_table *wait;
401         int retval, i, timed_out = 0;
402         unsigned long slack = 0;
403 
404         rcu_read_lock();
405         retval = max_select_fd(n, fds);
406         rcu_read_unlock();
407 
408         if (retval < 0)
409                 return retval;
410         n = retval;
411 
412         poll_initwait(&table);
413         wait = &table.pt;
414         if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
415                 wait = NULL;
416                 timed_out = 1;
417         }
418 
419         if (end_time && !timed_out)
420                 slack = estimate_accuracy(end_time);
421 
422         retval = 0;
423         for (;;) {
424                 unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
425 
426                 inp = fds->in; outp = fds->out; exp = fds->ex;
427                 rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
428 
429                 for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
430                         unsigned long in, out, ex, all_bits, bit = 1, mask, j;
431                         unsigned long res_in = 0, res_out = 0, res_ex = 0;
432                         const struct file_operations *f_op = NULL;
433                         struct file *file = NULL;
434 
435                         in = *inp++; out = *outp++; ex = *exp++;
436                         all_bits = in | out | ex;
437                         if (all_bits == 0) {
438                                 i += __NFDBITS;
439                                 continue;
440                         }
441 
442                         for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
443                                 int fput_needed;
444                                 if (i >= n)
445                                         break;
446                                 if (!(bit & all_bits))
447                                         continue;
448                                 file = fget_light(i, &fput_needed);
449                                 if (file) {
450                                         f_op = file->f_op;
451                                         mask = DEFAULT_POLLMASK;
452                                         if (f_op && f_op->poll) {
453                                                 wait_key_set(wait, in, out, bit);
454                                                 mask = (*f_op->poll)(file, wait);
455                                         }
456                                         fput_light(file, fput_needed);
457                                         if ((mask & POLLIN_SET) && (in & bit)) {
458                                                 res_in |= bit;
459                                                 retval++;
460                                                 wait = NULL;
461                                         }
462                                         if ((mask & POLLOUT_SET) && (out & bit)) {
463                                                 res_out |= bit;
464                                                 retval++;
465                                                 wait = NULL;
466                                         }
467                                         if ((mask & POLLEX_SET) && (ex & bit)) {
468                                                 res_ex |= bit;
469                                                 retval++;
470                                                 wait = NULL;
471                                         }
472                                 }
473                         }
474                         if (res_in)
475                                 *rinp = res_in;
476                         if (res_out)
477                                 *routp = res_out;
478                         if (res_ex)
479                                 *rexp = res_ex;
480                         cond_resched();
481                 }
482                 wait = NULL;
483                 if (retval || timed_out || signal_pending(current))
484                         break;
485                 if (table.error) {
486                         retval = table.error;
487                         break;
488                 }
489 
490                 /*
491                  * If this is the first loop and we have a timeout
492                  * given, then we convert to ktime_t and set the to
493                  * pointer to the expiry value.
494                  */
495                 if (end_time && !to) {
496                         expire = timespec_to_ktime(*end_time);
497                         to = &expire;
498                 }
499 
500                 if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
501                                            to, slack))
502                         timed_out = 1;
503         }
504 
505         poll_freewait(&table);
506 
507         return retval;
508 }

1.取读、写、异常文件描述符表位图中最大文件描述符,并做合法性检查(检查位图中置位的文件是否打开,如果未打开则返回EBADF)
2.初始化poll_wqueues,包括poll_table及poll_table的回调函数等
3.遍历描述符表位图中的文件,并对每个文件进行poll操作
  a.如果文件不能非阻塞的进行key标识的I/O操作,则poll会自动将进程添加到文件的poll等待队列中(见poll阻塞);
  b.如果文件能非阻塞的进行key标识的I/O操作,则poll返回相应的poll事件,并记录到返回文件描述符位图中;将poll_table置成NULL,即不用再将进程添加到文件的poll等待队列中;即使该文件不能进行非阻塞的I/O操作也不用将进程添加到文件的等待队列中,因为复用函数只需要有一个文件可进行非阻塞I/O操作即可
4.遍历结束后
  A.如果描述符表中有文件能非阻塞的进行key标识的I/O操作,则走5
  B.如果描述符表中没有文件能非阻塞的进行key标识的I/O操作且未超时
    a.triggered=1(在遍历的过程中,异步收到数据等情况时,文件能非阻塞的进行key标识的I/O操作),跳转到步骤3重新遍历描述符表位图
    b.triggered=0,则会阻塞进程,被唤醒后跳转到步骤3重新遍历描述符表位图
  C.超时,则超时退出,跳转到步骤5释放poll_wqueues
5.释放poll_wqueues

iv.poll_schedule_timeout

232 int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
233                           ktime_t *expires, unsigned long slack)
234 {
235         int rc = -EINTR;
236 
237         set_current_state(state);
238         if (!pwq->triggered)
239                 rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
240         __set_current_state(TASK_RUNNING);
241 
242         /*
243          * Prepare for the next iteration.
244          *
245          * The following set_mb() serves two purposes.  First, it's
246          * the counterpart rmb of the wmb in pollwake() such that data
247          * written before wake up is always visible after wake up.
248          * Second, the full barrier guarantees that triggered clearing
249          * doesn't pass event check of the next iteration.  Note that
250          * this problem doesn't exist for the first iteration as
251          * add_wait_queue() has full barrier semantics.
252          */
253         set_mb(pwq->triggered, 0);
254 
255         return rc;
256 }

1.当pwq->triggered=0时,则阻塞进程并超时等待;
2.否则,表示已经有文件触发waiter,不用阻塞进程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值