课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
图示中的虚线内展示的是内核空间的情况。用户空间的程序运行在最上层,以及硬件相关的设备则在最下面。图示中左边为以太网设备,右边为 WiFi 设备。
正如图中看到的一样,存在着两种 WiFi 设备,具体是哪一类要看 IEEE802.11 标准的 MLME 如何实现。
如果直接通过硬件实现,那么设备就是硬 MAC (full MAC)设备;如果通过软件的方式实现,那么设备就是软 MAC (soft MAC)设备。现阶段大部分无线设备都是软件实现的软 MAC 设备。
通常我们把 Linux 内核无线子系统看成两大块: cfg80211 和 mac80211 ,它们连通内核其他模块和用户空间的应用程序。
特别指出, cfg80211 在内核空间提供配置管理服务,内核与应用层通过 nl80211 实现配置管理接口。需要记住的是,
硬 MAC 设备和软 MAC 设备都需要 cfg80211 才能工作。而 mac80211 只是一个驱动 API ,它只支持软件实现的软 MAC 设备。
接下来,我们主要关注软 MAC 设备。
Linux 内核无线子系统统一各种 WiFi 设备,并处理 OSI 模型中最底层的 MAC 、 PHY 两层。
若进一步划分, MAC 层可以分为 MAC 高层和 MAC 底层。前者负责管理 MAC 层无线网络的探测发现、身份认证、关联等;
后者实现 MAC 层如 ACK 等紧急操作。大部分情况下,硬件(如无线适配器)处理大部分的 PHY 层以及 MAC 底层操作。Linux 子系统实现大部分的 MAC 高层回调函数。
2 模块间接口
从图一中我们可以看出,各个模块之间分界线很清晰,并且模块间相互透明不可见。模块之间一般不会相互影响。
举个例子,我们在 WiFi 设备驱动做修改(如,打补丁、添加新的 WiFi 驱动等),这些变更并不会影响到 mac80211 模块,
所以我们根本不用改动 mac80211 的代码。再如,添加一个新的网络协议理论上是不用修改套接字层以及设备无关层代码。一般情况下,内核通过一系列的函数指针实现各层之间相互透明。
如下代码展示 rtl73usb 无线网卡驱动与 mac80211 的联系。
static const struct ieee80211_ops rt73usb_mac80211_ops = {
.tx = rt2x00mac_tx,
.start = rt2x00mac_start,
.stop = rt2x00mac_stop,
.add_interface = rt2x00mac_add_interface,
.remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim,
.set_key = rt2x00mac_set_key,
.sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt73usb_conf_tx,
.get_tsf = rt73usb_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
.flush = rt2x00mac_flush,
.set_antenna = rt2x00mac_set_antenna,
.get_antenna = rt2x00mac_get_antenna,
.get_ringparam = rt2x00mac_get_ringparam,
.tx_frames_pending = rt2x00mac_tx_frames_pending,
};
左侧是 mac80211 为 WiFi 驱动模块实现的 ieee80211_ops 结构体形式的回调接口,回调函数的具体内容由驱动层实现。
显然,不同设备相应驱动的实现不同。结构体 ieee80211_ops 负责将不同设备驱动实现的回调函数与 mac80211 提供的 API 映射绑定起来。
当驱动模块插入注册时,这些回调函数就被注册到 mac80211 里面(通过 ieee80211_alloc_hw 实现),接着 mac80211 就绑定了相应的回调函数,根本不用知道具体的名字,以及实现细节等。
完整定义的 ieee80211_ops 结构包含很多成员,但不是所有都必须要驱动层实现。一般而言,实现的前七个成员函数就足够了。但是,要想正确实现其他功能,某些相关的成员函数就需要被实现,就像上面的例子一样。
3 数据路径与管理路径
图一所示中,存在两条主要路径:数据路径和管理路径。数据路径对应 IEEE802.11 数据帧,而管理路径对应着控制帧。
在 IEEE802.11 的控制帧中,大部分用于如 ACK 这类时间紧急的操作,并且一般直接由硬件实现。一个例外可能就是 PS-Poll 帧(用于 Power Save 控制),它也可以由 mac80211 实现。
数据和管理路径在 mac80211 里面是分开实现的。
4 数据包是如何被发送?
接下来,我们集中探讨下数据的发送过程。
首先,数据包起源于用户空间的应用程序,应用程序首先创建一个套接字,然后绑定一个接口(如,以太网接口、 WiFi 接口)。
接下来将数据写入到套接字缓冲区,最后再将缓冲区的数据发送出去。在套接字创建时,我们需要指明将要使用的协议族,这将在内核中起作用。
刚才这些发生在图一中的 Data Application 模块中,最终应用程序陷入系统调用,随后在内核空间进行接下来的工作。
数据的传输首先经过套接字层,这个过程中一个最重要的数据结构就是 sk_buff ,一般称为 skb 。一个 skb 结构中的成员包含着缓冲区的地址以及数据长度。
它还为内核中不同层对数据的操纵提供了良好的支持;实现了众多的接口,如,不同网络层首部的插入与去除等。整个数据的发送/接收过程均会用到这个结构。
我们跳过网络协议模块,对于网络协议我没有太多想说的,因为一旦涉及网络协议,简直说不尽道不完。在这里协议并不是我们主要关心的。
不过我们需要知道的是,数据传输使用的协议在套接字创建的时候就与指定的协议绑定了,然后相关的协议便会负责相关层的数据传输。
接下来,数据由网络层落到了设备无关层。这一层透明的连接着各种各样的硬件设备(如以太网设备、 WiFi 设备等)。
设备无关层一个重要的结构是: net_device 。我们回去看图一,再看接下来的代码就能解释内核是如何与以太网设备驱动通信的。
具体接口通过 net_device_ops 结构实现,该结构对应了 net_device 的很多操作。