淘先锋技术网

首页 1 2 3 4 5 6 7

我们知道,linux系统是一个庞大而复杂的系统,代码行数可想而知,虽然linux内核的代码是公开的代码,但是起初,我对阅读linux内核的代码是畏惧的,畏惧它的庞大,畏惧它的复杂,阅读起来会有很多的困难,但是我这个人是想挑战高难度的,所以,我下定决心试试,阅读以下linux内核的源代码。

Linux内核是由很多的子系统组成的,包括进程管理、内存管理、文件系统、网络系统等,所以需要一个一个的学习,那么从此刻起,我将和大家分享我学习网络子系统的总结,带着大家一起走进linux内核网络系统的世界。

随着互联网的高速发展,我们人人一个手机,通过手机观看世界上的每一个美好的瞬间,那么,人们能够在互联网上互相交流,看电影、玩游戏等,事实上是网络协议的发明起了关键性的作用。

1. 网络体系的介绍

1.1 网络协议的简介

对于开发的人员,在设计代码时,会经常考虑扩展性、模块化等特点,那么网络协议的设计,也是遵循了这样的思想,一些制定标准的人,将网络设计成了分层的模型,分层的模型带来的好处,就是每一层只考虑本层的业务,这样就保证了扩展性。

Linux所采取的分层模型是OSI模型,将网络分成了四层,如下所示:

54bc67e60a63485b44b4ccfa7e708e09.png

网络模型

链路层提供了设备驱动程序,也就是网卡的驱动相关的程序,设备驱动用来支持物理层的设备。

物理层就是传输数据的介质,一般用的最多的就是以太网。

网络层是向上面传输层提供数据,或者从传输层接收数据的一个模块,网络层最主要处理的报文就是IP报文。

传输层大家应该非常不陌生了,它主要为应用层提供数据,以及从应用层接收数据的模块,它主要处理 TCP 和 UDP 报文,这层的业务比较复杂,可以说是整个协议栈最复杂的模块之一。

应用层负责处理应用层数据,例如:HTTP、FTP、SMTP、HTTPS等。

1.2 Linux 网络协议栈的架构简介

下图显示了linux 网络协议栈的架构,我们可以看出linux是遵循OSI模型进行的架构设计,最上面的为应用层,工作在用户空间,中间的部分就是我们即将要讲的工作在内核空间的网络协议栈,网络协议栈中,是靠套接口缓存区进行的数据传递,也就是我们常说的skb,有关skb的结构,我会下篇文章详细述说,网络协议栈的顶部是系统调用的实现,它是用户态与内核态传输数据的一个模块,系统调用的下面是套接口层,我们管它叫协议无关层,它提供了一种通用的方法来使用传输层,这种通用的设计方法我们可以学一学,这在我们以后的项目中会用到,传输层就是具体的协议的实现了,包括TCP和UDP,邻居子系统主要的作用就是获取下一跳的mac地址,主要的协议是arp。

695f47206186ba37a79eb7b64be31376.png

linux协议栈网络分层模型

1.3 Linux 网络子系统的系统调用的介绍

在linux系统中,用户态切换到内核态的方式有好几种,其中最常见的就是系统调用,用户态进入内核态的方法和cpu的架构有关,对于i386架构的cpu,当进程调用一个系统调用时,系统会产生int 0x80的软中断,通过产生的软中断用户态进入内核态,用户态进程将系统调用号传入内核,在内核中,有一个系统调用的总的入口,我们成为system_call函数,system_call函数会根据系统调用号作为索引,调用sys_call_table中的函数,这样最终的系统调用的函数就被执行了。

下图是套接口层,网络层,传输层的关系图:

b076677e0d3e4416c542f7260c50bc4c.png

linux套接口系统调用模型

通过上图,我们可以看到,linux套接口实际上是协议无关层,只是判断proto,根据proto的值,来调用不同的函数,例如,proto是tcp的话,就会调用tcp_sendmesg函数,来发送数据包,这种抽象的设计是值得我们去学习的。

2. Linux网络子系统之SKB

Linux 网络子系统在处理数据包时,会将数据存在某个结构中,然后再对数据进行处理,而这个结构就是套接口缓存,也就是skb,linux内核对缓存有特殊的设计要求:

1) 由于网络中的报文的长度变化多样,所以skb要有绝对的能力处理可变长的报文。

2) Skb 要具备在头尾能够对数据进行添加和移除,因为skb要在不同的网络层次之间进行数据的传递。

在不同的网络层次之间进行数据的传递时,要尽量的避免数据的copy,因为内存的copy也是比较消耗cpu资源的。

2.1 套接口缓存skb

Linux内核的skb的设计正是遵循了上述的设计要求而设计的,skb用于在网络层协议层之间进行数据传递的一个数据结构,可以用在不同的网络协议层,例如:数据链路层、网络层(IP层)、传输层(TCP/UDP),skb在不同的层次之间进行传递时,有些变量会进行修改,例如,四层向三层传递数据前,会添加四层的首部,同理,三层向二层传递数据前,会添加三层的首部,添加首部要比在不同的层次之间复制skb的效率高,但是添加首部的操作比较复杂,因此linux内核提供一个函数接口skb_reserve()来完成这个操作,每当协议栈中的上一层在往下一层传递数据之前,都会调用该函数来添加首部。

如果是下层向上层传递skb,那么下层的首部相关的信息对于上层来说就没有用了,此时,linux内核并没有将下层的数据删除,而是将指针指向上层的头部,这种方式极大的提高了处理数据的能力。

2.2 套接口缓存skb

skb主要有两部分组成,一部分是skb描述符(即skb结构体本身),另一部分是数据缓存区,这部分是现申请的,这样的设计保证了skb可以处理可变长的数据报文,如下图所示,当然skb不止一个data成员指向数据缓存区,还有其他的成员,当然后续会讲到。

bc9ba18d021cf54b0e30223ce91ad52f.png

linux 套接口结构示意图

skb的成员变量的介绍:

struct sk_buff*next;

struct sk_buff*prev;

这两个成员使得skb结构构成了双向链表,头结点是skb_buff_head,这个就比较简单了,双向链表构成如下图:

5abe91f761b00a7e573fc86d0514af50.png

linux 套接口链表连接方式

下一篇,接着介绍linux内核网络子系统的相关知识,谢谢。