poll调用深入解析

poll调用深入解析

2012年10月23日 10:50:11

  • 12006

poll调用和select调用实现的功能一样,都是网络IO利用的一种机制。先看一下poll的调用形式

一,poll调用

 

[cpp] view plain copy

  1. #include <poll.h>  
  2. int poll(struct pollfd fds[], nfds_t nfds, int timeout);  

struct pollfd结构如下:【在源码文件poll.h文件中】

 

 

[cpp] view plain copy

  1. struct pollfd {  
  2.     int fd;  
  3.     short events;  
  4.     short revents;  
  5. };  

 

这个结构中fd表示文件描述符,events表示请求检测的事件,revents表示检测之后返回的事件,如果当某个文件描述符有状态变化时,revents的值就不为空。

二,参数说明

 

  1. fds:存放需要被检测状态的Socket描述符;与select不同(select函数在调用之后,会清空检测socket描述符的数组),每当调用这个函数之后,系统不会清空这个数组,而是将有状态变化的描述符结构的revents变量状态变化,操作起来比较方便;
  2. nfds:用于标记数组fds中的struct pollfd结构元素的总数量;
  3. timeout:poll函数调用阻塞的时间,单位是MS(毫秒)

三,返回值

 

 

  1. 大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的socket描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的socket描述符,然后判断这个里面有哪些事件以读取数据。
  2. 等于0:表示没有socket描述符有状态变化,并且调用超时。
  3. 小于0:此时表示有错误发生,此时全局变量errno保存错误码。

四,内核实现

      poll系统调用的内核实现是sys_poll,其代码如下:

[cpp] view plain copy

  1. asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,  
  2.             long timeout_msecs)  
  3. {  
  4.     s64 timeout_jiffies;  
  5.     int ret;  
  6.   
  7.     if (timeout_msecs > 0) {  
  8. #if HZ > 1000  
  9.         /* We can only overflow if HZ > 1000 */  
  10.         if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ)  
  11.             timeout_jiffies = -1;  
  12.         else  
  13. #endif  
  14.             timeout_jiffies = msecs_to_jiffies(timeout_msecs);  
  15.     } else {  
  16.         /* Infinite (< 0) or no (0) timeout */  
  17.         timeout_jiffies = timeout_msecs;  
  18.     }  
  19.   
  20.     ret = do_sys_poll(ufds, nfds, &timeout_jiffies);  
  21.     if (ret == -EINTR) {  
  22.         struct restart_block *restart_block;  
  23.         restart_block = &current_thread_info()->restart_block;  
  24.         restart_block->fn = do_restart_poll;  
  25.         restart_block->arg0 = (unsigned long)ufds;  
  26.         restart_block->arg1 = nfds;  
  27.         restart_block->arg2 = timeout_jiffies & 0xFFFFFFFF;  
  28.         restart_block->arg3 = (u64)timeout_jiffies >> 32;  
  29.         ret = -ERESTART_RESTARTBLOCK;  
  30.     }  
  31.     return ret;  
  32. }  

这个函数还是比较容易理解,包括三个部分的工作:

  1. 函数调用超时阻塞时间转换,根据内核的软时钟设置频率将超时时间设置为jiffies标准时间。
  2. 调用do_sys_poll,这里完成主要的工作。
  3. 如果当前进程有待处理的信号,则先处理信号,这是根据do_sys_poll返回来决定的,事实上在这个调用中会检查当前的进程是否有未处理信号,如果有,就会返回EINTR以处理信号,然后返回-ERESTART_RESTARTBLOCK,这会导致重新调用。

进入到do_sys_poll函数中

[cpp] view plain copy

  1. int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)  
  2. {  
  3.     struct poll_wqueues table;  
  4.     int err = -EFAULT, fdcount, len, size;  
  5.     /* Allocate small arguments on the stack to save memory and be 
  6.        faster - use long to make sure the buffer is aligned properly 
  7.        on 64 bit archs to avoid unaligned access */  
  8.     long stack_pps[POLL_STACK_ALLOC/sizeof(long)];  
  9.     struct poll_list *const head = (struct poll_list *)stack_pps;  
  10.     struct poll_list *walk = head;  
  11.     unsigned long todo = nfds;  
  12.   
  13.     if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)  
  14.         return -EINVAL;  
  15.   
  16.     len = min_t(unsigned int, nfds, N_STACK_PPS);  
  17.     for (;;) {  
  18.         walk->next = NULL;  
  19.         walk->len = len;  
  20.         if (!len)  
  21.             break;  
  22.   
  23.         if (copy_from_user(walk->entries, ufds + nfds-todo,  
  24.                     sizeof(struct pollfd) * walk->len))  
  25.             goto out_fds;  
  26.   
  27.         todo -= walk->len;  
  28.         if (!todo)  
  29.             break;  
  30.   
  31.         len = min(todo, POLLFD_PER_PAGE);  
  32.         size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;  
  33.         walk = walk->next = kmalloc(size, GFP_KERNEL);  
  34.         if (!walk) {  
  35.             err = -ENOMEM;  
  36.             goto out_fds;  
  37.         }  
  38.     }  
  39. pollfd  
  40.     poll_initwait(&table);  
  41.     fdcount = do_poll(nfds, head, &table, timeout);  
  42.     poll_freewait(&table);  
  43.   
  44.     for (walk = head; walk; walk = walk->next) {  
  45.         struct pollfd *fds = walk->entries;  
  46.         int j;  
  47.   
  48.         for (j = 0; j < walk->len; j++, ufds++)  
  49.             if (__put_user(fds[j].revents, &ufds->revents))  
  50.                 goto out_fds;  
  51.     }  
  52.   
  53.     err = fdcount;  
  54. out_fds:  
  55.     walk = head->next;  
  56.     while (walk) {  
  57.         struct poll_list *pos = walk;  
  58.         walk = walk->next;  
  59.         kfree(pos);  
  60.     }  
  61.   
  62.     return err;  
  63. }  

为了加快处理速度和提高系统性能,这里优先使用已经定好的一个栈空间,其大小为POLL_STACK_ALLOC,在我系统上,其值为256,大小为256个字节的栈空间转换为struct poll_list结构,以存储需要被检测的socket描述符,struct poll_list的结构如下:

[cpp] view plain copy

  1. struct poll_list {  
  2.     struct poll_list *next;  
  3.     int len;  
  4.     struct pollfd entries[0];  
  5. };  

上面可以看到该结构的entries为一个数组,结构为struct pollfd,这个有点眼熟,没错,它就是存储poll调用中需要被检测的socket描述符。那么前面分配的栈空间能存储多少个struct pollfd呢?这计算如下:

[cpp] view plain copy

  1. len = min_t(unsigned int, nfds, N_STACK_PPS);  

式中的N_STACK_PPS就是计算前面默认的固定栈大小能够存储多少个struct pollfd的

[cpp] view plain copy

  1. #define N_STACK_PPS ((sizeof(stack_pps) - sizeof(struct poll_list))  / \  
  2.             sizeof(struct pollfd))  

然后就复制len个struct pollfd至内核空间,这里有细心的用户就会发现:如果nfds比N_STACK_PPS大的话,怎么办呢?注意上面的函数,是一个循环,如果nfds比N_STACK_PPS大(事实上,一般都会比这里大),那么会再请求内存,然后接着复制,就是这个代码片段:

[cpp] view plain copy

  1.               len = min(todo, POLLFD_PER_PAGE);  
  2. size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;  
  3. walk = walk->next = kmalloc(size, GFP_KERNEL);  
  4. if (!walk) {  
  5.     err = -ENOMEM;  
  6.     goto out_fds;  
  7. }  

POLLFD_PER_PAGE表示一页的内存能够存储多少个struct pollfd,可以计算一下,一页是4K,而struct pollfd的内存占用8个字节,就是一页的内存可以将近存储512个socket描述符。如果在分配一页的内存之后,还不够nfds来用,没关系,循环不会退出的,会再分配一个页,并且所有分配的块都被struct poll_list链接起来,上面可以看到,这个结构有一个next域,就是专门做这个的。

在这之后,就会形成一个以stack_pps存储空间为头,然后一页一页分配的内存为接点的链表,这个链表上就存储了poll调用时传入的所有的socket描述符。


接下来调用一个很重要的部分

[cpp] view plain copy

  1. poll_initwait(&table);  
  2. fdcount = do_poll(nfds, head, &table, timeout);  
  3. poll_freewait(&table);  

这是最重要的部分,因为接下来的部分比较容易理解,在这之后,做两件事:

  1. 将链表上的所有struct pollfd中的revents的状态写入到用户空间(记得之前也从用户空间写入过内核空间,这是因为内核态地址,用户空间应用不能访问),所以需要写入到用户空间中去。
  2. 之前调用kmalloc分配了很多内存,现在要释放了,所以要从stack_pps地址处的head开始,顺着next不断的释放内存。

再回到最重要的部分,先看poll_initwait调用,下面是主要相关的数据结构

[cpp] view plain copy

  1. struct poll_wqueues {  
  2.     poll_table pt;  
  3.     struct poll_table_page * table;  
  4.     int error;  
  5.     int inline_index;  
  6.     struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];  
  7. };  
  8. typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);  
  9. typedef struct poll_table_struct {  
  10.     poll_queue_proc qproc;  
  11. } poll_table;  

poll_initwait函数如下:

[cpp] view plain copy

  1. void poll_initwait(struct poll_wqueues *pwq)  
  2. {  
  3.     init_poll_funcptr(&pwq->pt, __pollwait);//设置poll_table结构中的qproc函数指针为__pollwait函数,就是pwq->pt->qproc=__pollwait。这个函数是一个回调函数,基本上这种机制的实现,就是依靠回调函数了。  
  4.     pwq->error = 0;  
  5.     pwq->table = NULL;  
  6.     pwq->inline_index = 0;  
  7. }  

所以poll_initwait就是初始化了poll_wqueues table,主要是将其结构中的函数指针设置为__pollwait函数。那么这个函数是做什么的呢?我们先看poll_initwait之后调用的函数,就是do_poll函数,其实现如下:

注意下面函数在调用时的参数,参数有这么几个nfds, head, &table, timeout,参数就容易理解了:nfds表示poll调用时传入的数组中struct pollfd的个数,head其实是表示将poll调用时传入的数组,因为全部都表示为struct poll_list链表了(前面分析的,还记得吧),table是刚刚初始化的一个,里面暂时就只是包含一个回调函数的指针,就是__pollwait函数。timeout表示超时时间。

[cpp] view plain copy

  1. static int do_poll(unsigned int nfds,  struct poll_list *list,  
  2.            struct poll_wqueues *wait, s64 *timeout)  
  3. {  
  4.     int count = 0;  
  5.     poll_table* pt = &wait->pt;  
  6.   
  7.     /* Optimise the no-wait case */  
  8.     if (!(*timeout))  
  9.         pt = NULL;  
  10.   
  11.     for (;;) {  
  12.         struct poll_list *walk;  
  13.         long __timeout;  
  14.   
  15.         set_current_state(TASK_INTERRUPTIBLE);  
  16.         for (walk = list; walk != NULL; walk = walk->next) {  
  17.             struct pollfd * pfd, * pfd_end;  
  18.   
  19.             pfd = walk->entries;  
  20.             pfd_end = pfd + walk->len;  
  21.             for (; pfd != pfd_end; pfd++) {  
  22.                 /* 
  23.                  * Fish for events. If we found one, record it 
  24.                  * and kill the poll_table, so we don't 
  25.                  * needlessly register any other waiters after 
  26.                  * this. They'll get immediately deregistered 
  27.                  * when we break out and return. 
  28.                  */  
  29.                 if (do_pollfd(pfd, pt)) {  
  30.                     count++;  
  31.                     pt = NULL;  
  32.                 }  
  33.             }  
  34.         }  
  35.         /* 
  36.          * All waiters have already been registered, so don't provide 
  37.          * a poll_table to them on the next loop iteration. 
  38.          */  
  39.         pt = NULL;  
  40.         if (!count) {  
  41.             count = wait->error;  
  42.             if (signal_pending(current))  
  43.                 count = -EINTR;  
  44.         }  
  45.         if (count || !*timeout)  
  46.             break;  
  47.   
  48.         if (*timeout < 0) {  
  49.             /* Wait indefinitely */  
  50.             __timeout = MAX_SCHEDULE_TIMEOUT;  
  51.         } else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-1)) {  
  52.             /* 
  53.              * Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in 
  54.              * a loop 
  55.              */  
  56.             __timeout = MAX_SCHEDULE_TIMEOUT - 1;  
  57.             *timeout -= __timeout;  
  58.         } else {  
  59.             __timeout = *timeout;  
  60.             *timeout = 0;  
  61.         }  
  62.   
  63.         __timeout = schedule_timeout(__timeout);  
  64.         if (*timeout >= 0)  
  65.             *timeout += __timeout;  
  66.     }  
  67.     __set_current_state(TASK_RUNNING);  
  68.     return count;  
  69. }  

这个函数有以下几个要注意的点:

  1. 信号处理保障。在这个函数中先将当前进程设置为可以被信号中断,就是set_current_state(TASK_INTERRUPTIBLE)这一行,后面还会检查是否有需要处理的信号signal_pending(current)。这里的意思是就算是poll调用进入到sys_poll系统调用之后,也可以接收外部信号,从而退出当前系统调用(因为我们知道一般的系统调用都不会被中断的,所以系统调用一般都尽量很快的返回)。
  2. 外部大循环退出的条件,外部大循环退出的条件只有if (count || !*timeout) break;后面的条件容易理解,就是超时,前面的count是什么意思?它在每次调用do_pollfd函数之后,都有可能会加1,其实调用do_pollfd就是检查socket描述符状态的变化,如果有变化,就会使count加1,所以在结束内部遍历之后,count保存了所有的有状态变化的socket描述符数量。
  3. 这个函数会对之前以head为头结点的链表进行遍历,然后链表上每个结点中都包含很多很多的struct pollfd进行遍历(这些struct pollfd都被存储在struct poll_list结构的数组字段struct pollfd entries里面。
  4. 然后对每个struct pollfd调用do_pollfd(这会调用很多次,根据你传入多少个socket描述符而定),这个函数需要两个参数,一个是struct pollfd,这没得说的,另一个是刚刚初始化的table,就是那个暂时只是包含__pollwait回调指针的结构,还记得吧。

我们再进入do_pollfd,了解这个函数是做什么的?

[cpp] view plain copy

  1. static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)  
  2. {  
  3.     unsigned int mask;  
  4.     int fd;  
  5.   
  6.     mask = 0;  
  7.     fd = pollfd->fd;  
  8.     if (fd >= 0) {  
  9.         int fput_needed;  
  10.         struct file * file;  
  11.   
  12.         file = fget_light(fd, &fput_needed);  
  13.         mask = POLLNVAL;  
  14.         if (file != NULL) {  
  15.             mask = DEFAULT_POLLMASK;  
  16.             if (file->f_op && file->f_op->poll)  
  17.                 mask = file->f_op->poll(file, pwait);  
  18.             /* Mask out unneeded events. */  
  19.             mask &= pollfd->events | POLLERR | POLLHUP;  
  20.             fput_light(file, fput_needed);  
  21.         }  
  22.     }  
  23.     pollfd->revents = mask;  
  24.   
  25.     return mask;  
  26. }  

这个函数很简单,先根据socket描述符或者是文件句柄找到进程对应的struct file *file结构,然后调用file->f_op->poll(file,pwait),这是这个函数的核心调用,这其实也是linux的VFS的一部分,这会根据当前的文件是什么类型的文件来选择调用的入口,如file是socket网络文件,此时调用的就是由网络驱动设备来实现的poll,如果file是ext3等文件系统上打开的一个文件,那就会调用由该文件系统来实现的poll函数,我们以tcp_poll为例来了解一般poll完成什么工作;

注意下面的参数,file和wait是由file->f_op->poll调用传入的参数,而struct socket为socket连接的进程方面表示。

[cpp] view plain copy

  1. unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)  
  2. {  
  3.     unsigned int mask;  
  4.     struct sock *sk = sock->sk;  
  5.     struct tcp_sock *tp = tcp_sk(sk);  
  6.   
  7.     poll_wait(file, sk->sk_sleep, wait);  
  8.     if (sk->sk_state == TCP_LISTEN)  
  9.         return inet_csk_listen_poll(sk);  
  10.   
  11.     /* Socket is not locked. We are protected from async events 
  12.        by poll logic and correct handling of state changes 
  13.        made by another threads is impossible in any case. 
  14.      */  
  15.   
  16.     mask = 0;  
  17.     if (sk->sk_err)  
  18.         mask = POLLERR;  
  19.   
  20.     /* 
  21.      * POLLHUP is certainly not done right. But poll() doesn't 
  22.      * have a notion of HUP in just one direction, and for a 
  23.      * socket the read side is more interesting. 
  24.      * 
  25.      * Some poll() documentation says that POLLHUP is incompatible 
  26.      * with the POLLOUT/POLLWR flags, so somebody should check this 
  27.      * all. But careful, it tends to be safer to return too many 
  28.      * bits than too few, and you can easily break real applications 
  29.      * if you don't tell them that something has hung up! 
  30.      * 
  31.      * Check-me. 
  32.      * 
  33.      * Check number 1. POLLHUP is _UNMASKABLE_ event (see UNIX98 and 
  34.      * our fs/select.c). It means that after we received EOF, 
  35.      * poll always returns immediately, making impossible poll() on write() 
  36.      * in state CLOSE_WAIT. One solution is evident --- to set POLLHUP 
  37.      * if and only if shutdown has been made in both directions. 
  38.      * Actually, it is interesting to look how Solaris and DUX 
  39.      * solve this dilemma. I would prefer, if PULLHUP were maskable, 
  40.      * then we could set it on SND_SHUTDOWN. BTW examples given 
  41.      * in Stevens' books assume exactly this behaviour, it explains 
  42.      * why PULLHUP is incompatible with POLLOUT.    --ANK 
  43.      * 
  44.      * NOTE. Check for TCP_CLOSE is added. The goal is to prevent 
  45.      * blocking on fresh not-connected or disconnected socket. --ANK 
  46.      */  
  47.     if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE)  
  48.         mask |= POLLHUP;  
  49.     if (sk->sk_shutdown & RCV_SHUTDOWN)  
  50.         mask |= POLLIN | POLLRDNORM | POLLRDHUP;  
  51.   
  52.     /* Connected? */  
  53.     if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {  
  54.         /* Potential race condition. If read of tp below will 
  55.          * escape above sk->sk_state, we can be illegally awaken 
  56.          * in SYN_* states. */  
  57.         if ((tp->rcv_nxt != tp->copied_seq) &&  
  58.             (tp->urg_seq != tp->copied_seq ||  
  59.              tp->rcv_nxt != tp->copied_seq + 1 ||  
  60.              sock_flag(sk, SOCK_URGINLINE) || !tp->urg_data))  
  61.             mask |= POLLIN | POLLRDNORM;  
  62.   
  63.         if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {  
  64.             if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {  
  65.                 mask |= POLLOUT | POLLWRNORM;  
  66.             } else {  /* send SIGIO later */  
  67.                 set_bit(SOCK_ASYNC_NOSPACE,  
  68.                     &sk->sk_socket->flags);  
  69.                 set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);  
  70.   
  71.                 /* Race breaker. If space is freed after 
  72.                  * wspace test but before the flags are set, 
  73.                  * IO signal will be lost. 
  74.                  */  
  75.                 if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))  
  76.                     mask |= POLLOUT | POLLWRNORM;  
  77.             }  
  78.         }  
  79.   
  80.         if (tp->urg_data & TCP_URG_VALID)  
  81.             mask |= POLLPRI;  
  82.     }  
  83.     return mask;  
  84. }  

上面的tcp_poll看上去很长,但核心的的调用是:

[cpp] view plain copy

  1. poll_wait(file, sk->sk_sleep, wait);  

这个函数的file和wait是我们在poll调用过程中传入的参数,sk->sk_sleep是什么呢?这里解释一下

sk的值是

[cpp] view plain copy

  1. struct sock *sk = sock->sk;  

struct sock是socket连接的内核表示,sk->sk_sleep是struct wait_queue_head_t结构类型,这表示的是socket的等待队列,每一个socket都有自己的一个等待队列,由内核结构struct sock来维护。

其实大多数驱动实现的时候,此时都调用这个函数,这个函数也很简单,实现如下:

[cpp] view plain copy

  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. }  

现在一个转折点出现了,前面我们说过初始化table的函数指针为__pollwait,那么此时调用的就是__pollwait(filp,wait_address,p),这里的参数分别表示为进程表示文件结构struct file,socket或设备的等待队列wait_queue_head_t,和poll_table。

再回顾一下,到此为止,从我们调用poll函数开始,然后复制数据至内核、将struct pollfd表示为内核的struct poll_list链表、初始化poll_table变量、然后调用do_pollfd函数等过程,其实都是为了检查poll传递的每个struct pollfd是否有状态变化,也就是调用VFS的file->f_op->poll函数,这就到了__pollwait函数这里来了,这个函数会往等待队列上添加一个新的结点。

__pollwait的实现

[cpp] view plain copy

  1. static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,  
  2.                 poll_table *p)  
  3. {  
  4.     struct poll_table_entry *entry = poll_get_entry(p);  
  5.     if (!entry)  
  6.         return;  
  7.     get_file(filp);  
  8.     entry->filp = filp;  
  9.     entry->wait_address = wait_address;  
  10.     init_waitqueue_entry(&entry->wait, current);  
  11.     add_wait_queue(wait_address, &entry->wait);  
  12. }  

我们现在来分析一下,__pollwait调用完成之后,内核做了什么?先看一下poll_get_entry(p);

[cpp] view plain copy

  1. static struct poll_table_entry *poll_get_entry(poll_table *_p)  
  2. {  
  3.     struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);  
  4.     struct poll_table_page *table = p->table;  
  5.   
  6.     if (p->inline_index < N_INLINE_POLL_ENTRIES)  
  7.         return p->inline_entries + p->inline_index++;  
  8.   
  9.     if (!table || POLL_TABLE_FULL(table)) {  
  10.         struct poll_table_page *new_table;  
  11.   
  12.         new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);  
  13.         if (!new_table) {  
  14.             p->error = -ENOMEM;  
  15.             __set_current_state(TASK_RUNNING);  
  16.             return NULL;  
  17.         }  
  18.         new_table->entry = new_table->entries;  
  19.         new_table->next = table;  
  20.         p->table = new_table;  
  21.         table = new_table;  
  22.     }  
  23.   
  24.     return table->entry++;  
  25. }  

这个函数会根据情况创建struct poll_table_page结构,因为__pollwait在系统中是会被多次调用的,所以可能会有多个struct poll_table_page结构,这个结构是对struct poll_table_entry的一个封装,其结构如下所示:

[cpp] view plain copy

  1. struct poll_table_page {  
  2.     struct poll_table_page * next;  
  3.     struct poll_table_entry * entry;  
  4.     struct poll_table_entry entries[0];  
  5. };  
  6. struct poll_table_entry {  
  7.     struct file * filp;  
  8.     wait_queue_t wait;  
  9.     wait_queue_head_t * wait_address;  
  10. };  

所以在调用poll_get_entry之后,会返回一个新的poll_table_entry,这也是每次调用__pollwait都会产生的。接下来调用init_waitqueue_entry函数将这个新建的struct poll_table_entry和当前的进程绑定起来,再将struct poll_table_entry加入到socket的等待队列。这样就将当前进程和socket的等待队列联系,说白了,就是把current挂到等待队列上。

因为一旦有数据就绪,就会叫醒等待队列上的进程。可以看代码

[cpp] view plain copy

  1. static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)  
  2. {  
  3.     q->flags = 0;  
  4.     q->private = p;  
  5.     q->func = default_wake_function;  
  6. }  

这里同时,注册了一个数据就绪时的叫醒函数

[cpp] view plain copy

  1. int default_wake_function(wait_queue_t *curr, unsigned mode, int sync,  
  2.               void *key)  
  3. {  
  4.     return try_to_wake_up(curr->private, mode, sync);  
  5. }  

这就完成了调用。再来所有回顾一下

  1. 调用poll函数。
  2. 进入sys_poll等系列内核调用。
  3. 准备数据:,注册__pollwait(这是通过初始化poll_wqueues来完成的),复制数据至内核,重新组织成struct poll_list等等。
  4. 对所有的struct pollfd循环,以调用do_pollfd函数。
  5. do_pollfd调用file->f_op->poll函数。
  6. 然后调用__pollwait创建一个struct poll_table_entry,并将其与当前进程绑定。
  7. 将当前进程挂在socket的等待队列上。
  8. 有数据就绪时唤醒进程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值