您的位置:首页 > 运维架构 > Linux

linux系统调用select

2013-04-20 15:42 549 查看
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,不用阻塞进程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: