居家办公快要两个月了,这段时间都没有怎么学习,感到十分懊悔
今天开始收拾心情,重新出发,重新学习

现在我意识到之前的学习路线有些问题,对很多一知半解的东西没有深挖
接受了师傅的建议,再结合自身情况,决定先把supplicant看完,然后入手driver
这个专栏会尽可能地详细解读wpa_supplicant

话不多说,开始吧

wpa_supplicant代码目录

咱是个务实的人,干啥都从代码开始搞起, talk is cheap, show me the code

这是 wpa_supplicant_8 源码目录,目录下包含四个子文件夹(下段文字是摘抄自wifi学习知识点整理):

  • hostapd :hostapd能够使得无线网卡切换为master模式,模拟AP(通常可以认为是路由器)功能,也就是我们说的软AP(Soft AP)。Hostapd的功能就是作为AP的认证服务器,负责控制管理stations(通常可以认为带无线网卡的PC)的接入和认证。
  • hs20 :Hotspot 2.0(HS 2.0)由WiFi联盟和无线宽带联盟开发,旨在实现移动网络和WiFi网络之间的无缝流量切换,它不需要额外的用户注册和验证。其实Hotspot 2.0只是802.11u标准中的一个子项功能——即利用802.11u提供无缝的自动WiFi身份识别和切换,这一个新兴的协议将可帮用户省去了连接到WiFi网络和服务的麻烦和乏味的过程。Hotspot 2.0的出现,意味着我们将可以自动由3G、4G切换到WiFi网络,用户将不用手动发现热点、输入身份验证来获得WiFi。而且从一个网络切换到另一个网络的时候都不会间断, 用户甚至感觉不到。这种无缝认证和交接以及空中传送的安全性,将使得WiFi有如蜂窝网络那般易于使用,并提供一致的用户体验与新增加的WiFi网络速度上的优势,你根本感受不到自己某刻正在连接的是WiFi还是3G网络。
  • wpa_supplicant:是Android用户空间中无线网络部分的核心模块, 所有Framework层中和Wi-Fi相关的操作最终都将借由wpa_supplicant来完成。
  • src :包含了一些通用的数据结构和处理方法.其他三个目录均有可能使用到

注意,hostapd/src和wpa_supplicant/src子目录均链接到此src目录(所以你修改了src路径下的文件后会发现hostapd和wpa_supplicant下的跟着改了)

wpa_supplicant简介

  • wpa_supplicant是一个开源项目,已经被移植到Linux,Windows以及很多嵌入式系统上。它是WPA的应用层认证客户端,负责完成认证相关的登录、加密等工作。
  • wpa_supplicant是一个独立运行的守护进程,其核心是一个事件驱动模块,在各种事件到来后处理WPA状态机、控制命令、驱动事件、配置信息等。
  • 经过编译后 的 wpa_supplicant源程序可以看到两个主要的可执行工具:wpa_supplicant 和 wpa_cli。wpa_supplicant是核心程序,它和wpa_cli的关系就是服务和客户端的关系:后台运行wpa_supplicant,使用wpa_cli来搜索、设置、和连接网络。(这是很早的说法,现在已经不是这样了,具体是怎样,我还不会表述,QAQ)

上手event loop

wpa_supplicant所有工作都围绕 事件 展开。即,它是基于事件驱动的。事件驱动和消息驱动类似,主线程运行一个 event loop,等待事件的发生并处理它们。WPAS没有使用多线程编程,所有事件处理都在主线程中完成。

根据之前半知半解时期的学习经验来看,最先要弄懂的就是这个 eloop 机制

下面部分内容摘自链接: wpa_supplicant之eloop_run分析
阅读以下内容前请先(至少)看完 eloop.c 前面的主要结构体

以下是eloop_data具体内容

这个变量会处理三大类型的Event事件:Socket事件,Timeout事件,Signal事件
根据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 类型的指针链接起来

eloop_run

先看eloop_run源码

void eloop_run(void)
{fd_set *rfds, *wfds, *efds;struct timeval _tv;int res;struct os_reltime tv, now;rfds = os_malloc(sizeof(*rfds));wfds = os_malloc(sizeof(*wfds));efds = os_malloc(sizeof(*efds));if (rfds == NULL || wfds == NULL || efds == NULL)goto out;while (!eloop.terminate &&(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||eloop.writers.count > 0 || eloop.exceptions.count > 0)) {struct eloop_timeout *timeout;if (eloop.pending_terminate) {/** This may happen in some corner cases where a signal* is received during a blocking operation. We need to* process the pending signals and exit if requested to* avoid hitting the SIGALRM limit if the blocking* operation took more than two seconds.*/eloop_process_pending_signals();if (eloop.terminate)break;}timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,list);if (timeout) {os_get_reltime(&now);if (os_reltime_before(&now, &timeout->time))os_reltime_sub(&timeout->time, &now, &tv);elsetv.sec = tv.usec = 0;_tv.tv_sec = tv.sec;_tv.tv_usec = tv.usec;}eloop_sock_table_set_fds(&eloop.readers, rfds);eloop_sock_table_set_fds(&eloop.writers, wfds);eloop_sock_table_set_fds(&eloop.exceptions, efds);res = select(eloop.max_sock + 1, rfds, wfds, efds,timeout ? &_tv : NULL);if (res < 0 && errno != EINTR && errno != 0) {wpa_printf(MSG_ERROR, "eloop: %s: %s","select", strerror(errno));goto out;}eloop.readers.changed = 0;eloop.writers.changed = 0;eloop.exceptions.changed = 0;eloop_process_pending_signals();/* check if some registered timeouts have occurred */timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,list);if (timeout) {os_get_reltime(&now);if (!os_reltime_before(&now, &timeout->time)) {void *eloop_data = timeout->eloop_data;void *user_data = timeout->user_data;eloop_timeout_handler handler =timeout->handler;eloop_remove_timeout(timeout);handler(eloop_data, user_data);}}if (res <= 0)continue;if (eloop.readers.changed ||eloop.writers.changed ||eloop.exceptions.changed) {/** Sockets may have been closed and reopened with the* same FD in the signal or timeout handlers, so we* must skip the previous results and check again* whether any of the currently registered sockets have* events.*/continue;}eloop_sock_table_dispatch(&eloop.readers, rfds);eloop_sock_table_dispatch(&eloop.writers, wfds);eloop_sock_table_dispatch(&eloop.exceptions, efds);}eloop.terminate = 0;
out:os_free(rfds);os_free(wfds);os_free(efds);return;
}

eloop_run方法中的“死循环”:

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

如果eloop.terminate变为非零值,就会退出循环。这是为了提供一种从外部结束循环的方法。例如 void eloop_terminate(void) 方法。
如果eloop.terminate为零,只要timeout链表或者任一个Socket不为空,都会继续循环。

select系统调用的原型是:

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

关于select()函数的相关介绍,可见 select()函数用法
简单来讲:select函数可以同时监视多个文件描述符,并且可以监视三种事件。一旦某个文件描述符所指的对象发生了相应事件,就可以进行相应的处理。

在循环体里面:

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

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

(3)调用select。可能阻塞在此处。最大等待时间取决于“最快会发生的一次”timeout时间 。

(4)eloop_process_pending_signals(); 处理Signal事件。

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

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

(7)继续下次循环。

我按循环中处理事件类型的先后顺序来分析

signal事件
 int eloop_register_signal(int sig, eloop_signal_handler handler,void *user_data){struct eloop_signal *tmp;tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,sizeof(struct eloop_signal));if (tmp == NULL)return -1;tmp[eloop.signal_count].sig = sig;tmp[eloop.signal_count].user_data = user_data;tmp[eloop.signal_count].handler = handler;tmp[eloop.signal_count].signaled = 0;eloop.signal_count++;eloop.signals = tmp;signal(sig, eloop_handle_signal);return 0;
}

别处调用 eloop_register_signal() 函数注册 signal 时,对应的 eloop_handle_signal 不会立即执行(初注册时将其 signaled置0)
只有当 signal 发生时才会将此 signal 的 signaled加1,表示此类 signal 已收到并处于 pending 状态
(如何做到的呢?见 signal(sig, eloop_handle_signal); eloop_handle_signal 会将此 signal 的 signaled加1且eloop_data的signaled加1)
signal()函数用法见 signal()用法

在select()超时或者有Socket事件方式时才会顺便调用eloop_process_pending_signals(), 对每个处于Pending状态的struct eloop_signal调用其handler。

socket事件

在循环期间,外部调用 eloop_sock_table_add_sock() 函数往 eloop_sock_table 中添加 eloop_sock
eloop_sock_table_set_fds() 函数将 rfds/wfds/efds 对应 sock位用 FD_SET 置1
而后调用 select() 函数

select()返回值:发生错误时返回-1,超时时返回0。如果发生监视的事件,返回相应的文件描述符。
如果 select() 返回结果大于0,说明有socket事件发生,则调用 eloop_sock_table_dispatch() 依次处理三个 table(遍历三个table中的eloop_sock,调用其handler)

timeout事件

eloop_date中维护一个 timeout 链表,按超时先后排序,依次处理

读者可自行把 eloop.c 和 eloop.h 中的一些 tool function读完

【wpa_supplicant】入门 eloop 机制相关推荐

  1. 【Java】绘图入门和机制,绘图方法演示(绘制坦克)

    目录 1.绘图入门(绘制一个圆) 2.绘图方法 1.//画直线 2.//画矩形边框 3.//画椭圆边框 4.//填充矩形 (设置画笔的颜色) 5.//填充椭圆 6.//画图片 7.//画字符串 3.绘 ...

  2. Java入门-核心机制

    学习Java基础的视频J2SE,用Java开发项目,对Java的基础知识必须有一个清楚的了解.首先我们应该了解Java的的机制,Java有两种核心机制. 1.Java虚拟机(Java Virtual ...

  3. Linux内核入门——attribute机制

    本文来自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E7%9F%A5%E8%AF%86/20151019/335027_3.html G ...

  4. Datawhale-零基础入门NLP-新闻文本分类Task05

    该任务是用Word2Vec进行预处理,然后用TextCNN和TextRNN进行分类.TextCNN是利用卷积神经网络进行文本文类,TextCNN是用循环神经网络进行文本分类. 1.Word2Vec 文 ...

  5. 第3讲 | 浅说区块链共识机制

    ----文章出自极客时间<深入浅出区块链>   作者:元界 CTO 陈浩 我在第2讲"区块链到底是怎么运行的"一文中,提到了"打包Transaction&qu ...

  6. 区块链的核心:共识机制

    我在上一篇"区块链到底是怎么运行的"一文中,提到了"打包交易"和"广播交易"这两个概念,其实,以上谈到的两个内容正是区块链最核心的技术内容之 ...

  7. UEFI的一点点概识

    最近看了一篇Blog讲的是关于PC安全的,其中很多的地方还是有一定相似之处.其中这个UEFI引起了我兴趣,以前安装系统的时候听说过这个名词.这里于是便来认识一下什么是UEFI. 前言 大多数人接触UE ...

  8. 入选MIT TR 35的王刚是谁 他给阿里带来什么

    一周之前,MIT 编辑部致电阿里巴巴人工智能实验室首席科学家王刚,向他传达了获奖讯息,第一个蹦入王刚脑海的反应是-- I'm lucky .  王刚向雷锋网(公众号:雷锋网)表示,在身边优秀的行业前沿 ...

  9. RocketMQ 安装详细说明

    目录 查看全文 http://www.taodudu.cc/news/show-3380830.html 相关文章: 试着模仿LeetCode做一个在线OJ系统(超级阉割版)(附项目测试) JS知识点 ...

  10. 人工智能趋势与深度学习算法

    人工智能趋势与深度学习算法 1 前沿技术 1.1 Transformer模型: 1.2 BERT模型:基于Transformer Encoder构建的预测模型 1.3 自监督学习(Self-super ...

最新文章

  1. Java Socket实战之五:使用加密协议传输对象
  2. 开发检测MySQL主从同步插件
  3. mysql 中遍历查询_mysql中循环查询
  4. spring使用注解时配置文件的写法
  5. Coolite 常用客户端校验
  6. 限制排列与棋盘多项式
  7. 如何让自己的写的程序在阿里云一直运行
  8. HDU 1217 Arbitrage (Floyd + SPFA判环)
  9. Redis面试常问-- Redis常用数据类型
  10. [转]携程大数据实践:高并发应用架构及推荐系统案例
  11. 如何教女朋友学 Python?
  12. xcode 4 制作静态库(转)
  13. 我们玩游戏,还是游戏玩我们……
  14. Python打印九九乘法表
  15. 【Word】word2010中如何去掉标题前面的小黑点
  16. 深入浅出DockerPDF
  17. 【NOIP2018提高组D2T2】填数游戏
  18. 企业微信 ios 识别二维码 无法直接跳转 (安卓正常)
  19. Java:如何选择一个好的Java外包合作伙伴?
  20. 小小英雄怎么修改服务器,英雄联盟自走棋小小英雄怎么换 LOL英雄战棋小小英雄皮肤更改方法...

热门文章

  1. 蓝牙模块 hc06 linux,arduino教程——蓝牙模块hc05和hc06的相互连接
  2. vm虚拟机安装win7出现 Error loading image cdmenu.ezb
  3. 软件工程专业英语翻译【考研复试】
  4. vue 项目 创建表格
  5. 【吐血整理】超全golang面试题合集+golang学习指南+golang知识图谱+成长路线 一份涵盖大部分golang程序员所需要掌握的核心知识。
  6. 嵌入式单片机网络链路测试
  7. CSS不规则卡片-纯CSS制作优惠券样式-CSS实现锯齿样式
  8. CSS3技巧:利用css3径向渐变做一张优惠券
  9. WordPress优化教程让WordPress打开速度更快
  10. mysql跨库查询 效率_教你用一条SQL搞定跨数据库查询难题