Linux协议栈(6)——初始化及链路层实现

简介: 这篇主要学习链路层在内核协议栈的实现,包括初始化、注册以及接收发送,会涉及相关函数和代码所在位置。 我们知道以太网不仅可以传输IP分组,还可以传输其他协议的分组,接收系统必须能够区分不同的协议类型,以便将数据转发到正确的例程进一步处理。

这篇主要学习链路层在内核协议栈的实现,包括初始化、注册以及接收发送,会涉及相关函数和代码所在位置。

我们知道以太网不仅可以传输IP分组,还可以传输其他协议的分组,接收系统必须能够区分不同的协议类型,以便将数据转发到正确的例程进一步处理。因为分析数据并查明所用传输协议比较耗时,所以在以太网的帧首部包含了一个标识符,ip数据包的以太类型为0x0800,存在在以太网14字节报头中的前两个字节中。(定义在include/uapi/linux/if_ether.h#define ETH_P_IP        0x0800  /* Internet Protocol packet */。这些都是在链路层实现的。在链路层的帧处理由中断事件驱动。中断会将帧复制到sk_buff数据结构中。

            那么下面我们补补深入看下。

1.1.1.1  初始化

链路层比较底层,涉及的内容比较多,因为先后逻辑关系比较复杂,现在这些知识点放在一起,后续再逐个剥离之。

我们从start_kernel函数开始,该函数定义在init/main.c中。关于start_kernel的其他工作先不去讲解了,不然容易一环一环无法解套,我们直接将网络初始化相关的内容。

我们只需要知道该函数会调用和网络相关的初始化函数,如下:

init_IRQ();

init_timers();

softirq_init();

完成定时器、硬中断和软中断初始化,然后启动init进程。直接给出一个初始化逻辑流程图。

fdcc9e3572fdcad74da55378a58595cd548843cd

 

1.1.1.2  sock_init

从图中我们知道,start_kernel函数最终会调用do_initcalls函数,调用通过xxx_initcall注册的各种函数,sock_init就是其中之一。

sock_init(net/socket.c)函数放在级别为1的代码中

(core_initcall(sock_init)),用于初始化应用层网络协议。该函数调用skb_init函数,创建skbuff SLAB缓存区。调用init_inodecache函数初始化协议模块。并注册文件系统sock_fs_type

这个可以参考文章:

《linux网络驱动初始化module_init函数跟踪》

1.1.1.3  inet_init

inet_init函数(net/ipv4/af_inet.c)初始化internet 协议族的协议栈。定义如下:

static int __init inet_init(void)

也是__init类的函数, fs_initcall(inet_init);放在优先级为5的代码中。

inet_init函数初始化internet 协议族的协议栈。

1.1.1.4  proto_init

subsys_initcall(proto_init);也是放在优先级为6的代码段中。

            调用register_pernet_subsys函数来注册网络命名空间。

1.1.1.5  net_dev_init

函数net_dev_init同理放在优先级给6的代码中。

subsys_initcall(net_dev_init);

net_dev_init函数(定义在net/core/dev.c),在启动的时候调用。单线程执行不需要rtnl信号保护。用于在初始化设备。在开启时候会遍历设备列表,保证都是可用的设备都在线。

其会调用dev_proc_init函数, 它在/proc/net 下注册3个文件。/proc/net/softnet_stat输出netdevice设备的统计信息。例如如下,每行表示一个CPU数据:

06e3cd8a 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

其中第一列为该CPU所接收到的所有数据包。

netdev_kobject_init()函数,在/sys/class/下注册net类 它和设备模型有关.

register_pernet_subsys注册的所有的网络命名空间子系统都加入到 static struct list_head *first_device链表里.

注册软中断,如下:

open_softirq(NET_TX_SOFTIRQ, net_tx_action); 

open_softirq(NET_RX_SOFTIRQ, net_rx_action);

PS:网络设备命名:ethX:表示以太网适配器;pppX表示通过调制解调器简历的链接;isdnX表示ISDN卡;atmX表示异步传输模式,高速网卡的接口;lo环回设备。

1.1.1.6  接收

前面涉及了很多系统初始化本身的故事,虽然有线条没分支,但是也是涉及了不少点,因而将材料名字也做了相应修改。后续会将初始化的内容丰满之。

由于网络包的到达时间是不可预测的,所以所有现代设备驱动程序都使用中断来通知内核有包到达。现在所有的网卡都支持DMA模式,能自行将数据传输到物理内存。

            总体逻辑如下:

7e666b72779d584c3eccae5bb64f7bffa64255fa

            net_interrupt是由设备驱动程序设置的中断处理程序。如果是分组引发(排除报告错误),则将控制转移到net_rx。net_rx创建一个套接字缓冲区,将包从网卡传输到缓冲区(物理内存),然后分析首部数据,确定包所使用的网络层协议。

            然后调用netif_rx,该函数不特定于网络驱动程序。标志着控制从网卡代码转移到了网络层的通用接口部分。

netif_rx函数从设备驱动中接收一个包,将其排队给上层协议中处理。函数总是成功的。包的命运交于协议层处理,比如由于流程控制进行丢弃。将缓存投递到网络代码中。在结束之前将软中断NET_RX_SOFTIRQ(include/linux/interrupt.h)标记为即将执行,然后退出中断上下文。

  softnet_data数组管理进出分组的等待队列,每个CPU都会创建等待队列,支持并发处理。softnet_data结构定义在include/linux/netdevice.h.

            net_rx_action(net/core/dev.c)用作软中断的处理程序,net_rx_action调用设备的poll方法(默认为process_backlog),process_backlog函数循环处理所有分组。调用__skb_dequeue从等待队列移除一个套接字缓冲区。

调用__netif_receive_skb(net/core/dev.c)函数,分析分组类型、处理桥接,然后调用deliver_skb(net/core/dev.c),该函数调用packet_type->func使用特定于分组类型的处理程序,代码逻辑如下图所示:。

e46962411fd6e25fd1c03e10fff3ab7b97638980

            关于NAPI,为了防止中断过快导致出现中断风暴,NAPI采用了IRQ和轮询的组合。实现NAPI的条件是:1.设备能够保留多个接收(例如DMA),2.设备能够禁用用于分组接收的IRQ。

1.1.1.7  发送

发送时候除了特定协议需要完成的首部和校验和,以及由高层协议实例生成的数据之外,分组的路由是最重要的。在一个网卡系统下,内核也要区分发送到外部目标还是针对环回接口。

从网络层下来调用的链路层发送函数是dev_queue_xmit,直接调用__dev_queue_xmit函数,

            分组放置到等待队列上一定时间之后,分组将可以发出,通过网卡的定的函数 hard_start_xmit 来完成,在每个 net_device 结构中都以函数指针形式出现,由硬件设备驱动程序实现。
目录
相关文章
|
1月前
|
安全 Shell Linux
【Shell 命令集合 系统设置 】Linux 初始化系统设置setup命令 使用指南
【Shell 命令集合 系统设置 】Linux 初始化系统设置setup命令 使用指南
33 0
|
1月前
|
Shell Linux C语言
【Shell 命令集合 磁盘维护 】Linux 创建一个初始化内存盘 mkinitrd命令使用教程
【Shell 命令集合 磁盘维护 】Linux 创建一个初始化内存盘 mkinitrd命令使用教程
35 0
|
1月前
|
存储 Shell Linux
【Shell 命令集合 磁盘管理 】⭐⭐ Linux 显示当前shell会话中的目录栈 dirs命令使用教程
【Shell 命令集合 磁盘管理 】⭐⭐ Linux 显示当前shell会话中的目录栈 dirs命令使用教程
27 0
|
1月前
|
Shell Linux C语言
【Shell 命令集合 磁盘管理 】Linux 显示当前shell会话中的目录栈 dirs命令使用教程
【Shell 命令集合 磁盘管理 】Linux 显示当前shell会话中的目录栈 dirs命令使用教程
36 1
|
2月前
|
传感器 网络协议 物联网
在Linux中搭建Mosquitto MQTT协议消息服务端并结合内网穿透工具实现公网访问
Mosquitto是一个开源的消息代理,它实现了MQTT协议版本3.1和3.1.1。它可以在不同的平台上运行,包括Windows、Linux、macOS等。mosquitto可以用于物联网、传感器、移动应用程序等场景,提供了一种轻量级的、可靠的、基于发布/订阅模式的消息传递机制。
|
4月前
|
安全 关系型数据库 MySQL
Linux 实用小脚本系列(2)----mysql安全初始化脚本的免交互执行--mysql_secure_installation
Linux 实用小脚本系列(2)----mysql安全初始化脚本的免交互执行--mysql_secure_installation
55 0
|
17天前
|
网络协议 Linux SDN
虚拟网络设备与Linux网络协议栈
在现代计算环境中,虚拟网络设备在实现灵活的网络配置和隔离方面发挥了至关重要的作用🔧,特别是在容器化和虚拟化技术广泛应用的今天🌐。而Linux网络协议栈则是操作系统处理网络通信的核心💻,它支持广泛的协议和网络服务🌍,确保数据正确地在网络中传输。本文将深入分析虚拟网络设备与Linux网络协议栈的关联,揭示它们如何共同工作以支持复杂的网络需求。
|
1月前
|
Linux 网络安全
Linux NFS协议详解
Linux NFS协议详解
33 5
|
3月前
|
网络协议 Linux C++
Linux C/C++ websocket协议与服务器实现
Linux C/C++ websocket协议与服务器实现
69 0
|
3月前
|
存储 网络协议 Linux
Linux C/C++ 开发(学习笔记九 ):DNS协议与请求的实现
Linux C/C++ 开发(学习笔记九 ):DNS协议与请求的实现
55 0