Linux 网络栈剖析

姓名:穆培婷

学号:17101223414

专业:软件工程

本文转载自:https://www.ibm.com/developerworks/cn/linux/l-linux-networking-stack/

【嵌牛导读】:Linux操作系统的最大特性之一就是它的网络栈。它最初源于BSD的网络栈,具有一套非常干净的接口,组织得非常好。其接口范围从协议无关层(例如通用socket层接口或设备层)到各种网络协议的具体层。本文将从分层角度对Linux网络栈的接口进行探索,并介绍其中的一些主要结构。

【嵌牛鼻子】:Linux  网络  协议

【嵌牛提问】:如何理解Linux中的网络栈?

【嵌牛正文】:

协议简介

虽然对于网络的正式介绍一般都参考了 OSI(Open Systems

Interconnection)模型,但是本文对 Linux 中基本网络栈的介绍分为四层的 Internet 模型(如图 1 所示)。

图 1. 网络栈的 Internet 模型

这个栈的最底部是链路层。链路层是指提供对物理层访问的设备驱动程序,这可以是各种介质,例如串口链路或以太网设备。链路层上面是网络层,它负责将报文定向到目标位置。再上一层称为传输层,负责端到端的通信(例如,在一台主机内部)。尽管网络层负责管理主机之间的通信,但是传输层需要负责管理主机内部各端之间的通信。最后一层是应用层,它通常是一个语义层,能够理解要传输的数据。例如,超文本传输协议(HTTP)就负责传输服务器和客户机之间对 Web 内容的请求与响应。

实际来说,网络栈的各个层次有一些更为人所熟知的名字。在链路层上,可以找到以太网,这是最常用的一种高速介质。更早的链路层协议包括一些串口协议,例如

SLIP(Serial Line Internet Protocol)、CSLIP(Compressed

SLIP)和PPP(Point-to-Point Protocol)。最常见的网络层协议是 IP(Internet

Protocol),但是网络层中还存在一些满足其他需求的协议,例如 ICMP(Internet Control Message

Protocol)和ARP( Address Resolution Protocol)。在传输层上是 TCP(Transmission

Control Protocol)和 UDP(User Datagram

Protocol)。最后,应用层中包含很多大家都非常熟悉的协议,包括标准的 Web 协议 HTTP 和电子邮件协议 SMTP(Simple

Mail Transfer Protocol)。

核心网络架构

现在继续了解 Linux 网络栈的架构以及如何实现这种 Internet 模型。图 2 提供了 Linux 网络栈的高级视图。最上面是用户空间层,或称为应用层,其中定义了网络栈的用户。底部是物理设备,提供了对网络的连接能力(串口或诸如以太网之类的高速网络)。中间是内核空间,即网络子系统,也是本文介绍的重点。流经网络栈内部的是 socket 缓冲区(sk_buffs),它负责在源和汇点之间传递报文数据。您很快就将看到sk_buff的结构。

图 2. Linux 高级网络栈架构

首先,让我们来快速浏览一下

Linux 网络子系统的核心元素,后续章节中会更详细进行介绍。顶部(请参阅图

2)是系统调用接口。它简单地为用户空间的应用程序提供了一种访问内核网络子系统的方法。位于其下面的是一个协议无关层,它提供了一种通用方法来使用底层传输层协议。然后是实际协议,在

Linux 中包括内嵌的协议 TCP、UDP,当然还有

IP。然后是另外一个协议无关层,提供了与各个设备驱动程序通信的通用接口,最下面是设备驱动程序本身。

系统调用接口

系统调用接口可以从两个角度进行描述。用户发起网络调用时,通过系统调用接口进入内核的过程应该是多路的。最后调用 ./net/socket.c 中的sys_socketcall结束该过程,然后进一步将调用分路发送到指定目标。系统调用接口的另一种描述是使用普通文件操作作为网络 I/O。例如,典型的读写操作可以在网络 socket 上执行(socket 使用一个文件描述符表示,与一个普通文件一样)。因此,尽管有很多操作是网络专用的(使用socket调用创建一个 socket,使用connect调用连接一个收信方,等等),但是也有一些标准的文件操作可以应用于网络对象,就像操作普通文件一样。最后,系统调用接口提供了在用户空间应用程序和内核之间转移控制的方法。

协议无关接口

socket

层是一个协议无关接口,它提供了一组通用函数来支持各种不同协议。socket 层不但可以支持典型的 TCP 和 UDP 协议,而且还可以支持

IP、裸以太网和其他传输协议,例如 SCTP(Stream Control Transmission Protocol)。

通过网络栈进行的通信都需要对 socket 进行操作。Linux 中的 socket 结构是struct sock,这个结构是在 linux/include/net/sock.h 中定义的。这个巨大的结构中包含了特定 socket 所需要的所有状态信息,其中包括 socket 所使用的特定协议和在 socket 上可以执行的一些操作。

网络子系统可以通过一个定义了自己功能的特殊结构来了解可用协议。每个协议都维护了一个名为proto的结构(可以在 linux/include/net/sock.h 中找到)。这个结构定义了可以在从 socket 层到传输层中执行特定的 socket 操作(例如,如何创建一个 socket,如何使用 socket 建立一个连接,如何关闭一个 socket 等等)。

网络协议

网络协议这一节对一些可用的特定网络协议作出了定义(例如 TCP、UDP 等)。它们都是在 linux/net/ipv4/af_inet.c 文件中一个名为inet_init的函数中进行初始化的(因为 TCP 和 UDP 都是inet簇协议的一部分)。inet_init函数使用proto_register函数来注册每个内嵌协议。这个函数是在 linux/net/core/sock.c 中定义的,除了可以将这个协议添加到活动协议列表中之外,如果需要,该函数还可以选择分配一到多个 slab 缓存。

通过 linux/net/ipv4/ 目录中 udp.c 和 raw.c 文件中的proto接口,您可以了解各个协议是如何标识自己的。这些协议接口每个都按照类型和协议映射到inetsw_array,该数组将内嵌协议与操作映射到一起。inetsw_array结构及其关系如图 3 所示。最初,会调用inet_init中的inet_register_protosw将这个数组中的每个协议都初始化为inetsw。函数inet_init也会对各个inet模块进行初始化,例如 ARP、ICMP 和 IP 模块,以及 TCP 和 UDP 模块。

图 3. Internet 协议数组结构

Socket 协议的相互关系

回想以下在创建 socket 时,需要指定类型和协议,例如my_sock = socket( AF_INET, SOCK_STREAM, 0 )。AF_INET表示一个 Internet 地址簇,它使用的是一个流 socket,定义为SOCK_STREAM(如此处的inetsw_array所示)。

注意在图 3中,proto结构定义了传输特有的方法,而proto_ops结构则定义了通用的 socket 方法。可以通过调用inet_register_protosw将其他协议加入到inetsw协议中。例如,SCTP 就是通过调用 linux/net/sctp/protocol.c 中的sctp_init加入其中的。有关 SCTP 的更多信息,请参阅参考资料一节的内容。

socket 中的数据移动是使用一个所谓的 socket 缓冲区(sk_buff)的核心结构实现的。sk_buff中包含了报文数据,以及涉及协议栈中多个层次的状态数据。所发送或接收的每个报文都是使用一个sk_buff表示的。sk_buff结构是在 linux/include/linux/skbuff.h 中定义的,如图 4 所示。

图 4. Socket 缓冲区及其与其他结构的关系

如图所示,多个sk_buff可以针对某个给定连接链接在一起。每个sk_buff都在设备结构(net_device)中标识报文发送的目的地,或者接收报文的来源地。由于每个报文都是使用一个sk_buff表示的,因此报文头都可以通过一组指针(th、iph和mac[用于 Media        Access Control 或者 MAC 头])方便地进行定位。由于sk_buff是 socket 数据管理的中心,因此创建了很多支持函数来对它们进行管理。其中有些函数用于创建和销毁sk_buff结构,或对它进行克隆或排队管理。

针对给定的 socket,Socket 缓冲区可以链接在一起,这样可以包含众多信息,包括到协议头的链接、时间戳(报文是何时发送或接收的),以及与这个报文相关的设备。

设备无关接口

协议层下面是另外一个无关接口层,它将协议与具有很多各种不同功能的硬件设备连接在一起。这一层提供了一组通用函数供底层网络设备驱动程序使用,让它们可以对高层协议栈进行操作。

首先,设备驱动程序可能会通过调用register_netdevice或unregister_netdevice在内核中进行注册或注销。调用者首先填写net_device结构,然后传递这个结构进行注册。内核调用它的init函数(如果定义了这种函数),然后执行一组健全性检查,并创建一个sysfs条目,然后将新设备添加到设备列表中(内核中的活动设备链表)。在  linux/include/linux/netdevice.h 中可以找到这个net_device结构。这些函数都是在 linux/net/core/dev.c 中实现的。

要从协议层向设备中发送sk_buff,就需要使用dev_queue_xmit函数。这个函数可以对sk_buff进行排队,从而由底层设备驱动程序进行最终传输(使用sk_buff中引用的net_device或sk_buff->dev所定义的网络设备)。dev结构中包含了一个名为hard_start_xmit的方法,其中保存有发起sk_buff传输所使用的驱动程序函数。

报文的接收通常是使用netif_rx执行的。当底层设备驱动程序接收一个报文(包含在所分配的sk_buff中)时,就会通过调用netif_rx将sk_buff上传至网络层。然后,这个函数通过netif_rx_schedule将sk_buff在上层协议队列中进行排队,供以后进行处理。可以在 linux/net/core/dev.c 中找到dev_queue_xmit和netif_rx函数。

最近,内核中引入了一种新的应用程序编程接口(NAPI),该接口允许驱动程序与设备无关层(dev)进行交互。有些驱动程序使用的是 NAPI,但是大多数驱动程序仍然在使用老式的帧接收接口(比例大约是 6 比 1)。NAPI 在高负载的情况下可以产生更好的性能,它避免了为每个传入的帧都产生中断。

设备驱动程序

网络栈底部是负责管理物理网络设备的设备驱动程序。例如,包串口使用的 SLIP 驱动程序以及以太网设备使用的以太网驱动程序都是这一层的设备。

在进行初始化时,设备驱动程序会分配一个net_device结构,然后使用必须的程序对其进行初始化。这些程序中有一个是dev->hard_start_xmit,它定义了上层应该如何对sk_buff排队进行传输。这个程序的参数为sk_buff。这个函数的操作取决于底层硬件,但是通常sk_buff所描述的报文都会被移动到硬件环或队列中。就像是设备无关层中所描述的一样,对于 NAPI 兼容的网络驱动程序来说,帧的接收使用了netif_rx和netif_receive_skb接口。NAPI 驱动程序会对底层硬件的能力进行一些限制。有关更详细的信息,请参阅参考资料一节的内容。

设备驱动程序在dev结构中配置好自己的接口之后,调用register_netdevice便可以使用该配置。在 linux/drivers/net 中可以找出网络设备专用的驱动程序。

展望

Linux

源代码是学习有关大多数设备类型的设备驱动程序设计最佳方法,包括网络设备驱动程序。在这里可以找到的是各种设计的变化以及对可用内核 API

的使用,但是所学到的每一点都会非常有用,都可以作为新设备驱动程序的起点。除非您需要一种新协议,否则网络栈中的其余代码都是通用的,都会非常有用。即使现在,TCP(用于流协议)或

UDP(用于基于消息的协议)的实现都可以作为开始新开发有用模块使用。

相关主题

您可以参阅本文在 developerWorks 全球站点上的英文原文

请查阅 www.linuxjunkies.org 上的 “Introduction to the Internet Protocols” ,获得有关 TCP/IP、UDP 和 ICMP 的简要介绍。

使用 Linux 系统调用的内核命令”(developerWorks,2007 年 3 月)介绍了 Linux 系统调用接口,它是 Linux 内核中很重要的一层,GNU C Library(glbic)提供了对用户空间支持,这样便可以在用户空间和内核之间进行函数调用。

使用 /proc 文件系统来访问 Linux 内核的内容”(developerWorks,2006 年 3 月)介绍了 /proc 文件系统,它是一个虚拟文件系统,为用户空间的应用程序与内核进行通信提供了一种创新的方法。这篇文章展示 /proc 的应用,并演示可加载的内核模块。

如果对网络协议感兴趣,Linux 是一个非常不错的操作系统,这一点与 BSD 类似。“使用 SCTP 优化网络”(developerWorks,2006 年 2 月)介绍了最有趣的网络协议之一 SCTP,它的操作方式和 TCP 类似,但是却增加了一些有用的特性,例如消息、多宿主和多流特性。

Linux slab 分配器详解”(developerWorks,2007 年 5 月)介绍了 Linux 中内存管理的最有趣特性之一:slab 分配器。这种机制源自于 SunOS,但是它在 Linux 内核中找到了一个友好的家园。

NAPI 驱动程序与使用老式报文处理框架的驱动程序相比,具有很多优点,从终端管理到报文处理都要好很多。在 OSDL 上的NAPI 接口和设计中可以了解更多内容。

有关 Linux 用户空间编程的更多信息,请参考 Tim 撰写的GNU/Linux Application Programming一书。

在 Tim 撰写的BSD Sockets

Programming from a Multi-Language Perspective一书中,可以学习有关使用 BSD socket API 进行 socket 编程的知识。

Free Software FoundationWeb 站点上学习更多有关自由软件的内容。由于 Linux 是一个自由软件,因此任何希望从事相关工作的人都可以装配和发行自己的发行版。

developerWorks Linux 专区中可找到适合 Linux 开发人员的更多资源,包括Linux 教程

利用可直接从 developerWorks 下载的IBM 试用软件在 Linux 上构建您的下一个开发项目。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • 1三个相关数据结构. 关于socket的创建,首先需要分析socket这个结构体,这是整个的核心。 104 str...
    ice_camel阅读 2,806评论 1 8
  • 首先来一张图说明GNU/Linux的基本的体系结构: 用户模式和内核模式 执行进程的模式有两种:用户模式和内核模式...
    Joe_HUST阅读 2,092评论 0 2
  • 1、关键数据结构 1.1 嵌套字缓冲区 一个数据封包就存储在这里,所有网络分层都会使用这个结构来存储其报头、有关的...
    shaarawy18阅读 3,650评论 0 5
  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    数据革命阅读 12,146评论 2 34
  • 你喜欢雨天,空气带着湿润的青草气息,灰色屋檐的有细微的水花溅起,一本书一杯茶,可以很安静也可以和三两闺蜜就这么漫无...
    桐馨阅读 1,133评论 7 8