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

WPA_SUPPLICANT源码分析(1):EVENT LOOP的实现

2012-09-11 17:56 1641 查看
转:http://blog.chinaunix.net/uid-20273473-id-3128151.html

WPA_SUPPLICANT的程序的生命就是在运行一个EVENT LOOP, 等待各种Event的到来,然后做相应的处理。因此分析EVENT LOOP的实现能起到提纲挈领的作用。

1. 数据结构: 见下图





struct eloop_data结构体是一个统领全局的数据结构,只有一个实例,即Line 75:

点击(此处)折叠或打开

static struct eloop_data eloop;

要处理的Event有三大种类型:Socket事件,Timeout事件,Signal事件. 其中Socket事件又分为三种类型:

* @EVENT_TYPE_READ: Socket has data available for reading

* @EVENT_TYPE_WRITE: Socket has room for new data to be written

* @EVENT_TYPE_EXCEPTION: An exception has been reported

Socket事件:有readers, writers, exceptions三个eloop_sock_table结构体, 每个里面都有动态数组,数组的每一项都是一个具体的eloop_sock. 可以向各个Table里面添加、删除eloop_sock. 事件分发就是遍历sock_table, 依次运行里面的每个Handler.

Timeout事件: 每个struct eloop_timeout都被放在一个双向链表中, 链表头就是eloop_data中的”timeout”项。这些struct eloop_timeout按超时先后排序。

Signal事件:每个struct eloop_signal放在动态数组中

疑问: 为什么Socket和signal事件不使用链表,而是动态的扩大和缩小数组?

2. 基于Select的多Event处理

void eloop_run(void) 是个总函数,我们看它是怎么用select系统调用实现多路复用监听多个事件的。

while (!eloop.terminate &&

(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||

eloop.writers.count > 0 || eloop.exceptions.count > 0)) {

如果eloop.terminate变为非零值,就会退出循环。这是为了提供一种从外部结束循环的方法。

如果eloop.terminate为零,只要timeout链表或者任一个Socket不为空,都会继续循环。

Select系统调用的原型是:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

在循环体里面:

(1)找到timeout链表的第一项(因为是按超时先后排序的,所以第一项肯定是最先超时的),计算超时时间距现在还有多久, 并据此设置select的timeout参数。

(2)设置readfd, writefds和exceptfds三个fd_set: 方法是遍历各个eloop_sock_table,把每个sock描述符加入相应的fd_set里面。

(3)调用select。 可能阻塞在此处。

(4)eloop_process_pending_signals(); 处理Signal事件。后面会分析。

(5)判断是否有超时发生,如果是则调用其Handler, 并从timeout链表移除。然后继续下次循环。

(6)如果不是超时事件,则应该是readfds, writefds或者exceptfds事件, fd_set里面会被改变,存放发生事件的描述符。因此分别遍历三个sock_table, 如果其描述符在fd_set里面则调用其Handler.

(7)继续下次循环.
值得一提的是,这里对Signal的处理有点特别。在eloop_register_signal() 函数注册的signal的Handler并不是当Signal发生时就会自动执行的。当Signal发生时只会对该struct eloop_signal的 signaled变量加1,以表明Signal已收到并处于Pendning状态。在select()超时或者有Socket事件方式时才会顺便调用eloop_process_pending_signals(), 对每个处于Pending状态的struct
eloop_signal调用其Handler.

疑问:这样做有什么好处?如果select阻塞很久,岂不是Signal也要Pending很久才得到处理?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: