部分内容转自http://blog.chinaunix.net/uid-20273473-id-3128151.html

重要结构体!!!

struct eloop_sock {
int sock;
void *eloop_data;
void *user_data;
eloop_sock_handler handler; //该handler是一个方法,后续socket有变化,就会调用相应的socket所在的结构体中的handler方法来处理
WPA_TRACE_REF(eloop);
WPA_TRACE_REF(user);
WPA_TRACE_INFO
}; struct eloop_timeout {
struct dl_list list; //双向链表
struct os_time time;
void *eloop_data;
void *user_data;
eloop_timeout_handler handler;
WPA_TRACE_REF(eloop);
WPA_TRACE_REF(user);
WPA_TRACE_INFO
}; struct eloop_signal {
int sig;
void *user_data;
eloop_signal_handler handler;
int signaled;
}; struct eloop_sock_table {
int count;
struct eloop_sock *table; //eloop_sock类型的变量
int changed;
}; struct eloop_data {
int max_sock; int count; /* sum of all table counts */
#ifdef CONFIG_ELOOP_POLL
int max_pollfd_map; /* number of pollfds_map currently allocated */
int max_poll_fds; /* number of pollfds currently allocated */
struct pollfd *pollfds;
struct pollfd **pollfds_map;
#endif /* CONFIG_ELOOP_POLL */
struct eloop_sock_table readers;
struct eloop_sock_table writers;
struct eloop_sock_table exceptions; struct dl_list timeout; int signal_count;
struct eloop_signal *signals;
int signaled;
int pending_terminate; int terminate;
int reader_table_changed;
}; //创建了一个静态全局变量,类型为eloop_data
static struct eloop_data eloop;

ps:这里贴出结构体关系图,应该是wpa_supplicant_6代码相关的

进入正题:

1.关键点:一个成员变量: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

根据eloop_data结构体分析事件的处理:

Socket事件:有readers,writers,exceptions三个eloop_sock_table结构体,每个里面都有一个eloop_sock类型的指针table,这里可以将该指针变量理解成动态数组。可以向各个table里面添加、删除eloop_sock。事件分发就是遍历eloop_sock_table,依次运行里面的每个handler。

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

Signal事件:每个struct eloop_signal都通过eloop_signal类型的指针链接起来。

2.基于select方法实现多事件监听

eloop_run方法中的“死循环”:

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

如果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)设置rfds,wfds和efds三个fd_set:方法是遍历各个eloop_sock_table,把每个sock描述符加入相应的fd_set里面。

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

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

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

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

(7)继续下次循环。

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

-----------------------------------------------------------------------------------------------------------------

下面分析handler方法的由来。

1.监听rfds集合中socket的变化。若发生变化,就会调用eloop.readers->table[i]->handler方法来处理。该handler方法的来源需要分析下。

XX_init
-->eloop_register_read_sock
-->eloop_register_sock
-->eloop_sock_table_add_sock
-->......
-->eloop_run的while循环中等待上层发送命令过来

注册过程:在一些初始化的方法(XX_init)中,会调用eloop_register_read_sock方法,如wpa_supplicant_ctrl_iface_init方法。然后将socket以及作为handler的方法名以参数形式传递过去(即在这里完成了回调方法的注册)。

回调过程:eloop_run方法通过select方法在循环监听rfds的变化,一旦rfds发生变化,就会调用相应socket的handler方法来处理。

我也只是涉及wpa_supplicant与HAL等通过socket发送/接收消息的流程,所以这里只分析这个通信的流程。这一个知识点主要是监听rfds集合中socket的变化,即是否有可读消息,所以其实说白了就是监听上层发送给wpa_supplicant的命令。

在wpa_supplicant_ctrl_iface_init方法[ctrl_iface_unix.c]中,有如下语句:

eloop_register_read_sock(priv->sock,wpa_supplicant_ctrl_iface_receive, wpa_s, priv);

所以这里注册的handler方法就是wpa_supplicant_ctrl_iface_receive方法。该方法主要就是接收ctrl_conn发送过来的命令,处理后反馈reponse命令。

该方法在./wpa_supplicant/ctrl_iface_unix.c文件中,分析之。

主要三个方法:recvfrom,wpa_supplicant_ctrl_iface_process,sendto

(1)recvfrom方法从文件描述符中获取message,保存对方的ip等信息,然后进行比较处理。

(2)wpa_supplicant_ctrl_iface_process方法也属于比较处理的部分。在我看来只是内容比较多,就单独写了方法来操作了。该方法在./wpa_supplicant/ctrl_iface.c文件中。该方法处理完后,会返回reply值。

(3)sendto方法将reply发送给之前保存的对方端。

“ctrl_iface_unix.c”实现wpa_supplicant的Unix domain socket通信机制中server结点,完成对client结点的响应。

其中最主要的两个函数为:

/* 接收并解析client发送request命令,然后根据不同的命令调用底层不同的处理函数;
* 然后将获得response结果回馈到 client 结点。
*/
static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx) /* 向注册的monitor interfaces 主动发送event事件,该方法在下一个知识点进行分析 */
static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
int level, const char *buf,
size_t len)

-----------------------------------------------------------------------------------------------------------------

2.监听wfds集合中socket的变化。

因为我分析的主要是wpa_supplicant与上层或driver层通过socket通信的流程。所以这里就把wfds理解成是监听driver发送过来的消息。监听到socket有变化,eloop也会调用其结构体中相应的table中的handler方法处理driver发过来的消息,然后再发送消息给上层应用。

(1)首先分析driver和wpa_supplicant间的通信交互

即,wfds集合中相关联socket的handler方法是怎样注册进来的?

主要还是通过socket来处理,可以参考wpa_supplicant下行接口浅析 这篇的分析。

(2)wpa_supplicant和上层(HAL等)间的交互

简单来说,就是通过wpa_msg方法调用回调方法,将wpa_supplicant的事件发送给monitor interfaces。

下面分析调用wpa_msg方法会执行的流程。

注意以下三个文件,

./wpa_supplicant/ctrl_iface_unix.c   // TCP/IP
./wpa_supplicant/ctrl_iface_udp.c // UDP
./wpa_supplicant/ctrl_iface_named_pipe.c // PIPE

这三个文件中流程都是一样的,同样,因目前所学有限,只分析ctrl_iface_unix.c相关的流程。

(2.1)注册回调方法

首先在wpa_supplicant_ctrl_iface_init方法中,会注册一个回调方法,

wpa_supplicant_ctrl_iface_init---->wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);

//wpa_msg_register_cb源码
void wpa_msg_register_cb(wpa_msg_cb_func func)
{
wpa_msg_cb = func; //将回调方法保存下来
}

1. wpa_msg_register_cb方法作用:register callback function for wpa_msg() messages.

2. 这里wpa_supplicant_ctrl_iface_msg_cb方法就是callback方法。

(2.2)wpa_msg方法

//wpa_msg方法源码
void wpa_msg(void *ctx, int level, const char *fmt, ...)
{
va_list ap;
char *buf;
const int buflen = ;
int len;
char prefix[]; buf = os_malloc(buflen);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
"buffer");
return;
}
va_start(ap, fmt);
prefix[] = '\0';
if (wpa_msg_ifname_cb) {
const char *ifname = wpa_msg_ifname_cb(ctx);
if (ifname) {
int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
ifname);
if (res < || res >= (int) sizeof(prefix))
prefix[] = '\0';
}
}
len = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
wpa_printf(level, "%s%s", prefix, buf);
if (wpa_msg_cb)
wpa_msg_cb(ctx, level, buf, len); //这里就是回调方法起作用的地方了
os_free(buf);
}

wpa_msg方法:类似于wpa_printf,但同时它也会把消息发送给所有已经attach到wpa_supplicant的monitors。

因此,一旦调用wpa_msg方法,就会回调wpa_supplicant_ctrl_iface_msg_cb方法。

--->wpa_msg
--->wpa_supplicant_ctrl_iface_msg_cb(即wpa_msg_cb被赋予的方法)
--->wpa_supplicant_ctrl_iface_send
--->sendmsg(ctrl_iface_unix.c中是sendmsg方法)发送到monitors(当然包括monitor_conn)

wpa_supplicant之eloop_run分析的更多相关文章

  1. 局域网Linux机器中病毒简单处理 .aliyun.sh 挖矿病毒 ---不彻底

    1. 昨天晚上同事打电话给我说自己的服务器上面的redis无故被清空了,并且查看aof 日志有很多 wget和write指令 一想就是大事不好.局域网中病毒了.. 2. 今天早上到公司忙完一阵简单看了 ...

  2. wpa_supplicant软件架构分析

    wpa_supplicant软件架构分析 1. 启动命令 wpa supplicant 在启动时,启动命令可以带有很多参数,目前我们的启动命令如下: wpa_supplicant /system/bi ...

  3. 上层应用与wpa_supplicant,wpa_supplicant与kernel 相关socket创建交互分析

    单独拿出来,分析以下上层应用与wpa_supplicant   wpa_supplicant与kernel 的socket交互. 关联上层应用与wpa_supplicant的socket的创建.连接流 ...

  4. linux 代码分析工具 gprof - 以wpa_supplicant为例

        当我们遇到一个新的程序的时候,经常会无从下手,需要debug一个功能的时候,我们不知道函数的运行流程是怎么样的,这就需要借助工具来帮助我们加快流程了.这里以分析wpa_supplicant为例 ...

  5. Android WiFi 扫描流程分析(wpa_supplicant选择网络)

    扫描流程 1.如果之前就已经有相关记录,优化扫描,扫描记录部分的频率信道. 2.如果1中的扫描没有结果,清除黑名单中的进行选择. 3.如果2中没有结果,进行所有频率的信道进行扫描 相关log参考: h ...

  6. Wpa_supplicant 调试故障原因分析

    背景 在使用Wpa_supplicant 工具调试Linux的wifi的时候,发现有一些问题.特此记录一下.有些问题是遇到的并已经有了解决方法,一些问题比较发杂,只能作为思路. 问题以及解决办法 1. ...

  7. Android wpa_supplicant 四次握手 流程分析

    记录wpa_supplicant四次握手的过程. 相关log:https://www.cnblogs.com/helloworldtoyou/p/9633603.html 接收到第一次握手,会设置一个 ...

  8. Android WiFi 扫描流程分析(wpa_supplicant)

    void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { int res; if (wpa_s-& ...

  9. wpa_supplicant 使用

    (1)通过adb命令行,可以直接打开supplicant,从而运行wpa_cli,可以解决客户没有显示屏而无法操作WIFI的问题,还可以避免UI的问题带到driver.进一步来说,可以用在很多没有键盘 ...

随机推荐

  1. 【模板】缩点(tarjan,DAG上DP)

    题目背景 缩点+DP 题目描述 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只 ...

  2. 哈希查找解决地址冲突的两种最常见方法(线性探测再散列,链地址法)C++实现

    #include<iostream>#include<iomanip>using namespace std; typedef struct Node{ int data; s ...

  3. Java开发小技巧(五):HttpClient工具类

    前言 大多数Java应用程序都会通过HTTP协议来调用接口访问各种网络资源,JDK也提供了相应的HTTP工具包,但是使用起来不够方便灵活,所以我们可以利用Apache的HttpClient来封装一个具 ...

  4. 我的名字叫hadoop

      第一回 新入环境 我的名字是hadoop,我一出生我的爸爸雅虎就给我取了这样一个名字:hadoop,我也不知道为什么叫这个名字,刚出生没多久,雅虎爸爸就把我领进一个黑暗的屋子里面,屋里堆满了黑色的 ...

  5. QP-nano结构分析

    QP-nano是QP的一个裁剪版本,是一个通用的.可移植的.超轻量级的事件驱动型框架.适用于像8051.PIC.AVR.MSP430.68HC01/11/12.R8C/Tiny等资源受限的8位和16位 ...

  6. 基于原生JS封装数组原型上的sort方法

    基于原生JS封装数组原型上的sort方法 最近学习了数组的原型上内置方法的封装,加强了用原生JS封装方法的能力,也进一步理解数组方法封装的过程,实现的功能.虽然没有深入底层,了解源码.以下解法都是基于 ...

  7. SpaceVim 语言模块 erlang

    原文连接: https://spacevim.org/cn/layers/lang/erlang/ 模块简介 功能特性 启用模块 快捷键 语言专属快捷键 交互式编程 模块简介 这一模块为 SpaceV ...

  8. Prism for WPF 搭建一个简单的模块化开发框架(六)隐藏菜单、导航

    原文:Prism for WPF 搭建一个简单的模块化开发框架(六)隐藏菜单.导航 这个实际上是在聊天之前做的,一起写了,也不分先后了 看一下效果图,上面是模块主导航,左侧是模块内菜单,现在加一下隐藏 ...

  9. JDK核心源码(2)

    Java的基础知识有很多,但是我认为最基础的知识应该要属jdk的基础代码, jdk的基础代码里面,有分了很多基础模块,其中又属jdk包下面的lang包最为基础. 我们下面将总结和分析一下lang包下面 ...

  10. Unicode编码相关概念

    1.Unicode是一种字符映射方案,这种映射并不是编码(即还没有到二进制机器码层面),而是像一个电话本一样,把全世界所有语言使用的字符,都映射成一个"u+"开头的数字(在JAVA ...