[原创]WPA_SUPPLICANT源码分析(1):EVENT LOOP的实现

2372阅读 0评论2012-04-03 土豆和地瓜
分类:

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

1. 数据结构: 见下图



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

点击(此处)折叠或打开

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

  1.  * @EVENT_TYPE_READ: Socket has data available for reading
  2.  * @EVENT_TYPE_WRITE: Socket has room for new data to be written
  3.  * @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放在动态数组中

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

2. 基于Select的多Event处理

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

  1. while (!eloop.terminate &&
  2.      (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
  3.         eloop.writers.count > 0 || eloop.exceptions.count > 0)) {

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

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

Select系统调用的原型是:

  1. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
在循环体里面:

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

(2)设置readfd, writefdsexceptfds三个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() 函数注册的signalHandler并不是当Signal发生时就会自动执行的。当Signal发生时只会对该struct eloop_signal signaled变量加1,以表明Signal已收到并处于Pendning状态。在select()超时或者有Socket事件方式时才会顺便调用eloop_process_pending_signals() 对每个处于Pending状态的struct eloop_signal调用其Handler.

疑问:这样做有什么好处?如果select阻塞很久,岂不是Signal也要Pending很久才得到处理?

上一篇:浅析firmware完整生存和使用流程
下一篇:源码_添加wifi电源管理接口供jni调用