《探寻linux协议栈》之二:源于skb的一场大联欢

写在前面

上一节,大概探讨了linux中协议栈的范围以及一个正常的封包在linux kernel中是如何传输的。当然实际代码层面的报文传输逻辑远远比上一章节中展示出来的几幅图复杂太多了,但无论如何复杂,基本的动向流程还是相当清晰。我觉得探寻协议栈一定要从大到小,从流程到细节。流程框架的东西搞清楚弄明白,基本上再从细微处看代码自己实践,就会非常清晰。本章节,我从帧的结构剖析,来看看一个封包是如何被linux kernel接收并处理的。

skb是什么

正如我写的标题一样,探寻linux协议栈,既然是协议栈,离不开最本质的东西:报文。

  • 报文是什么,站在用户的角度来讲,就是消息,是信息,是自己用手机、电脑等发出去的各种消息。站在linux kernek角度来看,报文是一个数据结构,一块data,一个buffer里存储的各种数据。站在驱动层来看,一个报文就是一段二进制数据。

  • 报文从哪里来?要到哪里去?

      报文从哪里来呢?毫无疑问,一台PC接在网络中,通过网线链接到本身的网卡中,每一秒都在接收来自网络中的各种报文。这些报文先经过网卡驱动处理之后然后才会被送到操作系统中处理。所以报文是接收而来的来自网络间的数据或者说信号,要被送给操作系统协议栈处理发送或者其它用途。

  • skb出场

      那么skb究竟是什么?在linux x86架构下的系统中,驱动(以e1000e为例),接收到一个消息后,这些消息是各种二进制组成的data,机器识别没问题,但是软件层不会认识,写软件的程序员更头疼。那么,x86体系下,为了管理这些数据,为了协议栈能正确接收处理这些报文,引入了一个结构体定义,linux 协议栈里,叫做skb.我们可以这么说,所谓的协议栈,就是处理一个个skb的过程。

  • 结论

    skb就是一个容器,驱动收到报文之后,就会把数据转化了skb结构,相当于给报文量身定做了一套衣服。之后报文在整个linux内部的传输,全部是以skb为单位用于转发,控制。

skb长什么样

skb既然是linux协议栈中最基本最重要的东西,本身到底是什么样的,我从以下两个方面来说明

  1. linux源码中的定义,参见 /include/linux/skbuff.h kernel version:3.3.8

    • 下图是内核中针对skbuff结构体定义的一些注释.其中一些比较常见的变量我做出了标示

linux

  • 代码中的定义见下:
struct sk_buff {
    /* These two members must be first. */
    struct sk_buff      *next;
    struct sk_buff      *prev;

    ktime_t         tstamp;

    struct sock     *sk;
    struct net_device   *dev; -----netdev的结构体,包含了设备信息比如:端口名,端口状态等等
        .
    .
    .
    unsigned int        len,
                data_len;
    __u16           mac_len,
                hdr_len;
    .
    .
    .
    __be16          protocol;


    .
    .
    .

    __u16           vlan_tci;

    sk_buff_data_t      transport_header;
    sk_buff_data_t      network_header;
    sk_buff_data_t      mac_header;
    /* These elements must be at the end, see alloc_skb() for details.  */
    sk_buff_data_t      tail;
    sk_buff_data_t      end;
    unsigned char       *head,
                *data;
    unsigned int        truesize;
    atomic_t        users;
}

以上就是skb结构在linux内核里的定义,当然我贴出来的有删减,只是列出了最常见最常用到的部分。

首先,上面的代码定义中,有几个指针要特别说明以下

  • head 这个指针指向一个skb整个数据结构的起始位置
  • data 这个指针指向一个skb中的data段开始位置
  • tail 这个指针指向一个skb中data段的结束位置
  • end 这个指针指向一个skb整个数据结构的结束位置

    它们看起来像是这样的:

skb

skb中和网络协议有关的指针

  • 主要是以下三个指针:

    • sk_buff_data_t transport_header;
    • sk_buff_data_t network_header;
    • sk_buff_data_t mac_header;
  • transport_header 主要指向传输层头部,比如udp/tcp协议

  • network_header 主要指向网络层头部比如IP层
  • mac_header 主要指向链路层头部ether

  • 还有其它比较重要的数据成员,这里仅作简单介绍

    • data_len:储存SKB的内存区并非都是线性连续的一整块区域,也有可能是不连续的两块内存区。这样的话,有可能一个skb的数据却存储在两个不连续的内存中,这就是skb分段。为了确定每一段大小,引入了这个变量,它显示的是本段内skb数据的大小。

    • len 是skb中真实数据的长度

    • protocol 协议,比如标识vlan的8021q协议
    • cb[48] 这个是私有数据,如果想保存一些信息希望在整个协议栈传输过程中一直可以读到,就可以存储在这里面

skb的储存

  • 线性区:即报文第一个buffer区域
  • 非线性区:报文除第一个buffer部分其余的区域

  • skb结构描述的内存区域包含几个部分:
    1)sk_buff结构体本身;
    2)线性buffer区域,由skb->head,data,tail,end等几个指针来描述,并且包含skb_shared_info结构;
    3)“paged data”:通过skb_shared_info结构管理的一组保存在 内存page 中的数据;
    4)skb_shared_info(skb)->frag_list队列包含分片的skb队列,队列成员是skb链表指针。

skb如何到linux内核协议栈中

  • 驱动收到报文初始化之后生成skb结构,然后接下来就要发送报文,发给linux协议栈,那么大概是个什么样的逻辑呢?

  • 先从驱动接收帧说起,驱动接收到一个帧到来后,要做三件事情:

    • 把帧拷贝到sk_buff结构中(使用DMA方式的处理要简单许多)
    • 初始化sk_buff中的一些网络相关参数,比如 skb->protocol
    • 更新驱动设备一些私有数据参数,产生中断告知cpu

    经过以上的处理,驱动就完成了一个帧到来的准备工作,那么接下来,就要把一个帧交给linux内核cpu来处理了。那么,驱动是如何通知linux内核cpu来处理报文呢?显然是触发一个中断给内核,通知cpu

  • 驱动通知内核机制简介

    • 老的接口netif_rx
    • NAPI

    该部分内容讲起来非常复杂,并不是本章节需要重点关注的内容。目前先简单来说,就是netif_rx采用中断去唤醒cpu来处理。而NAPI的网卡是采用中断+轮询的机制去通知内核cpu来处理。

内核接收驱动skb的入口函数:netif_receive_skb

  • 驱动完成一个skb的初始化之后,产生中断,中断处理函数最终调用的内核接收函数,就是netif_receive_skb函数。
    所以说,该函数是一扇大门,这扇大门的开启,标志着,一个报文,自驱动接收以后,万里长征预备工作完成。而该大门之后,就是linux内核世界里,充满凶险和刺激的协议栈旅程。一个skb的万里长征路,正式开启。

总结

一个报文的产生和发送,都需要硬件和软件的完美配合。硬件层面接收到报文之后,做一系列的初始化操作,之后驱动才开始把一个封包封装为skb。当然这是在x86架构下,如果是在cavium架构下,封包是wqe形式存在。不管是skb还是wqe,都仅仅是一种手段,一种达到完成报文传输所采用的一种解决方案,一种方法而已。

或许处理方案的具体实现细节差别万千,但是基本的原理,都是殊途同归,万变不离其宗。

skb的产生,让linux协议栈旅程的开启,具备了最基本的条件,接下来的协议栈之旅,才会更加精彩。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值