近期发现python的web开发要进入比较高级的阶段,基本都会要对于事件编程有一定的掌握,此文逐渐介绍一些时间编程的知识
首先介绍一下linux的select.poll等机制
http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
while (1) { 157 158 /*每次调用select前都要重新设置文件描述符和时间,因为事件发生后,文件描述符和时间都被内核修改啦*/ 159 /*添加监听套接字*/ 160 FD_ZERO(readfds); 161 FD_SET(srvfd, readfds); 162 s_srv_ctx->maxfd = srvfd; 163 164 tv.tv_sec = 30; 165 tv.tv_usec = 0; 166 167 /*添加客户端套接字*/ 168 for (i = 0; i < s_srv_ctx->cli_cnt; i++) { 169 clifd = s_srv_ctx->clifds[i]; 170 FD_SET(clifd, readfds); 171 s_srv_ctx->maxfd = (clifd > s_srv_ctx->maxfd ? clifd : s_srv_ctx->maxfd); 172 } 173 174 retval = select(s_srv_ctx->maxfd + 1, readfds, NULL, NULL, &tv);
建立了一个select来监听一些read的事件。
(1)使用copy_from_user从用户空间拷贝fd_set到内核空间
(2)注册回调函数__pollwait
(3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)
(4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数。
(5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了。
(6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值。
(7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是current)进入睡眠。当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(schedule_timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd。
(8)把fd_set从内核空间拷贝到用户空间。