【Linux内核剖析】深入分析inet_init的处理机制

inet_init 是 Linux 内核中用于初始化 TCP/IP 协议栈的函数。它在内核启动时被调用,完成各种协议和数据结构的注册和初始化。
主要功能:

  • 注册 TCP、UDP、ICMP 等协议。
  • 初始化 ARP、IP 和其他网络协议模块。
  • 设置 socket 操作和协议处理。

前后调用关系链:

start_kernel()└── rest_init()└── kernel_init()└── do_basic_setup()└── do_initcalls()└── inet_init()├── proto_register(&tcp_prot, 1)├── proto_register(&udp_prot, 1)├── proto_register(&raw_prot, 1)├── proto_register(&ping_prot, 1)├── sock_register(&inet_family_ops)├── inet_add_protocol(&icmp_protocol, IPPROTO_ICMP)├── inet_add_protocol(&udp_protocol, IPPROTO_UDP)├── inet_add_protocol(&tcp_protocol, IPPROTO_TCP)├── arp_init()├── ip_init()├── tcp_v4_init()├── udp_init()├── ping_init()├── icmp_init()└── init_ipv4_mibs()


再来看源代码:

static int __init inet_init(void)
{struct sk_buff *dummy_skb;struct inet_protosw *q;struct list_head *r;int rc = -EINVAL;BUILD_BUG_ON(sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb));sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);if (!sysctl_local_reserved_ports)goto out;rc = proto_register(&tcp_prot, 1);if (rc)goto out_free_reserved_ports;rc = proto_register(&udp_prot, 1);if (rc)goto out_unregister_tcp_proto;rc = proto_register(&raw_prot, 1);if (rc)goto out_unregister_udp_proto;rc = proto_register(&ping_prot, 1);if (rc)goto out_unregister_raw_proto;/**	Tell SOCKET that we are alive...*/(void)sock_register(&inet_family_ops);#ifdef CONFIG_SYSCTLip_static_sysctl_init();
#endif/**	Add all the base protocols.*/if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICASTif (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
#endif/* Register the socket-side information for inet_create. */for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)INIT_LIST_HEAD(r);for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)inet_register_protosw(q);/**	Set the ARP module up*/arp_init();/**	Set the IP module up*/ip_init();tcp_v4_init();/* Setup TCP slab cache for open requests. */tcp_init();/* Setup UDP memory threshold */udp_init();/* Add UDP-Lite (RFC 3828) */udplite4_register();ping_init();/**	Set the ICMP layer up*/if (icmp_init() < 0)panic("Failed to create the ICMP control socket.\n");/**	Initialise the multicast router*/
#if defined(CONFIG_IP_MROUTE)if (ip_mr_init())printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n");
#endif/**	Initialise per-cpu ipv4 mibs*/if (init_ipv4_mibs())printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n");ipv4_proc_init();ipfrag_init();dev_add_pack(&ip_packet_type);rc = 0;
out:return rc;
out_unregister_raw_proto:proto_unregister(&raw_prot);
out_unregister_udp_proto:proto_unregister(&udp_prot);
out_unregister_tcp_proto:proto_unregister(&tcp_prot);
out_free_reserved_ports:kfree(sysctl_local_reserved_ports);goto out;
}fs_initcall(inet_init);

调用路径

  • start_kernel():内核的入口函数,位于 init/main.c 中,完成内核的基本初始化工作。
  • rest_init():在 start_kernel() 中被调用,创建内核初始化线程 kernel_init。
  • kernel_init():内核初始化线程的主函数,负责内核的后续初始化工作。
  • do_basic_setup():在 kernel_init() 中被调用,执行所有的初始化调用(initcall)。
  • do_initcalls():在 do_basic_setup() 中被调用,遍历所有的 initcall 函数,并依次执行它们。
  • inet_init():作为一个 __initcall 函数被调用,用于初始化 TCP/IP 协议栈。

proto_register():用于注册不同的协议(TCP、UDP、RAW、PING),将它们添加到协议列表中,以便后续处理。
sock_register():注册 socket 操作,包括创建和管理 socket 的方法。
inet_add_protocol():将各个传输层协议(如 ICMP、UDP、TCP)添加到网络层,以便接收和处理数据包。
模块初始化函数(如 arp_init(), ip_init(), tcp_v4_init(), 等):这些函数负责初始化各个网络模块,为后续的数据传输做好准备。



在看最后一行的处理函数

/***	dev_add_pack - add packet handler*	@pt: packet type declaration**	Add a protocol handler to the networking stack. The passed &packet_type*	is linked into kernel lists and may not be freed until it has been*	removed from the kernel lists.**	This call does not sleep therefore it can not*	guarantee all CPU's that are in middle of receiving packets*	will see the new packet type (until the next received packet).*/void dev_add_pack(struct packet_type *pt)
{struct list_head *head = ptype_head(pt);spin_lock(&ptype_lock);list_add_rcu(&pt->list, head);spin_unlock(&ptype_lock);
}
EXPORT_SYMBOL(dev_add_pack);

dev_add_pack 是 Linux 内核中用于添加数据包处理程序的函数。它将一个协议处理程序(packet handler)注册到网络栈中,以便在接收到特定类型的数据包时能够正确处理这些数据包。

具体来说:

  • 添加协议处理程序:将传入的 packet_type 结构体链接到内核的链表中,允许内核在接收到相应类型的数据包时调用相应的处理程序。
    struct packet_type {__be16			type;	/* This is really htons(ether_type). */struct net_device	*dev;	/* NULL is wildcarded here	     */int			(*func) (struct sk_buff *,struct net_device *,struct packet_type *,struct net_device *);struct sk_buff		*(*gso_segment)(struct sk_buff *skb,u32 features);int			(*gso_send_check)(struct sk_buff *skb);struct sk_buff		**(*gro_receive)(struct sk_buff **head,struct sk_buff *skb);int			(*gro_complete)(struct sk_buff *skb);void			*af_packet_priv;struct list_head	list;
    };
  • 非阻塞操作:该函数在执行过程中不会导致线程睡眠,这意味着它可以在任何上下文中被调用,包括中断上下文。

这么做有什么好处呢?

  • 网络协议处理:当网络设备接收到数据包时,内核会检查数据包类型,并调用相应的处理程序。通过 dev_add_pack() 注册的数据包处理程序会在接收到匹配的数据包时被触发。
  • 动态协议支持:可以在运行时动态地添加新的协议处理程序,而不需要重启内核或修改内核代码。这使得内核能够灵活地支持多种网络协议和功能。

那么,它如何如何根据数据包的类型来找到对应的链表头呢?

/*******************************************************************************Protocol management and registration routines*******************************************************************************//**	Add a protocol ID to the list. Now that the input handler is*	smarter we can dispense with all the messy stuff that used to be*	here.**	BEWARE!!! Protocol handlers, mangling input packets,*	MUST BE last in hash buckets and checking protocol handlers*	MUST start from promiscuous ptype_all chain in net_bh.*	It is true now, do not change it.*	Explanation follows: if protocol handler, mangling packet, will*	be the first on list, it is not able to sense, that packet*	is cloned and should be copied-on-write, so that it will*	change it and subsequent readers will get broken packet.*							--ANK (980803)*/static inline struct list_head *ptype_head(const struct packet_type *pt)
{if (pt->type == htons(ETH_P_ALL))return &ptype_all;elsereturn &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/473337.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV5人脸目标检测

【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV5人脸目标检测 文章目录 【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV5人脸目标检测前言YOLOV5模型运行环境搭建YOLOV5模型运行数据集准备YOLOV5运行模型训练模型验证模型推理 总结 前言 Ultralytics YOLO 是一…

使用Axios函数库进行网络请求的使用指南

目录 前言1. 什么是Axios2. Axios的引入方式2.1 通过CDN直接引入2.2 在模块化项目中引入 3. 使用Axios发送请求3.1 GET请求3.2 POST请求 4. Axios请求方式别名5. 使用Axios创建实例5.1 创建Axios实例5.2 使用实例发送请求 6. 使用async/await简化异步请求6.1 获取所有文章数据6…

windows工具 -- 使用rustdesk和云服务器自建远程桌面服务, 手机, PC, Mac, Linux远程桌面 (简洁明了)

目的 向日葵最先放弃了, todesk某些功能需要收费, 不想用了想要 自己搭建远程桌面 自己使用希望可以电脑 控制手机分辨率高一些 原理理解 ubuntu云服务器配置 够买好自己的云服务器, 安装 Ubuntu操作系统 点击下载 hbbr 和 hbbs 两个 deb文件: https://github.com/rustdesk/…

MySQL-关联查询和子查询

目录 一、笛卡尔积 二、表连接 1、内部连接 1.1 等值连接 1.2 非等值连接 2、外部链接 2.1 左外连接-LEFT JOIN 2.2 右外连接-RIGHT JOIN 2.3 全关联-FULL JOIN/UNION 三、子查询 1、嵌套子查询 2、相关子查询 3、insert和select语句添加数据 4、update和select语…

AWTK-WIDGET-WEB-VIEW 实现笔记 (1) - 难点

webview 提供了一个跨平台的 webview 库&#xff0c;其接口简单&#xff0c;提供的例子也直观易懂。但是把它集成到 AWTK 里&#xff0c;还是遇到一些难题&#xff0c;这里记录一下&#xff0c;供有需要的朋友参考。 1. 作为 AWTK 控件 webview 提供的例子都是独立的程序&…

类与对象;

目录 一、认识类&#xff1b; 1、类的引入&#xff1b; 2、类的定义&#xff1b; 类的两种定义方式&#xff1a; 3、类的访问限定符及封装&#xff1b; 4、类的作用域&#xff1b; 5、类的实例化&#xff1b; 6、类对象模型&#xff1b; 计算类对象的大小&#xff1b; …

Ubuntu22.04LTS 部署前后端分离项目

一、安装mysql8.0 1. 安装mysql8.0 # 更新安装包管理工具 sudo apt-get update # 安装 mysql数据库&#xff0c;过程中的选项选择 y sudo apt-get install mysql-server # 启动mysql命令如下 &#xff08;停止mysql的命令为&#xff1a;sudo service mysql stop&#xff0…

使用 Ant Design Vue 自定渲染函数customRender实现单元格合并功能rowSpan

使用 Ant Design Vue 自定渲染函数customRender实现单元格合并功能rowSpan 背景 在使用Ant Design Vue 开发数据表格时&#xff0c;我们常常会遇到需要合并单元格的需求。 比如&#xff0c;某些字段的值可能会在多行中重复出现&#xff0c;而我们希望将这些重复的单元格合并为…

27.<Spring博客系统③(实现用户退出登录接口+发布博客+删除/编辑博客)>

PS&#xff1a;关于打印日志 1.建议在关键节点打印日志。 ①请求入口。 ②结果响应 2.在可能发生错误的节点打印日志 3.日志不是越多越好。因为打日志也会消耗性能。 日志也可以配置去除重复日志。 一、用户退出功能 判断用户退出。我们只需要在前端将token删掉就可以了。 由于…

[前端面试]javascript

js数据类型 简单数据类型 null undefined string number boolean bigint 任意精度的大整数 symbol 创建唯一且不变的值&#xff0c;常用来表示对象属性的唯一标识 复杂数据类型 object&#xff0c;数组&#xff0c;函数,正则,日期等 区别 存储区别 简单数据类型因为其大小固定…

uniapp自动注册机制:easycom

传统 Vue 项目中&#xff0c;我们需要注册、导入组件之后才能使用组件。 uniapp 框架提供了一种组件自动注册机制&#xff0c;只要你在 components 文件夹下新建的组件满足 /components/组件名/组件名.vue 的命名规范&#xff0c;就能直接使用。 注意&#xff1a;组件的文件夹…

人工智能与SEO优化中的关键词策略解析

内容概要 在当今数字化快速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;与搜索引擎优化&#xff08;SEO&#xff09;的结合正变得愈发重要。关键词策略是SEO优化的一项基础工作&#xff0c;它直接影响到网站的可见性和流量。通过运用智能算法&#xff0c;企业能…

【异常解决】Linux shell报错:-bash: [: ==: 期待一元表达式 解决方法

博主介绍&#xff1a;✌全网粉丝21W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…

卷径计算(基于卷径变化微分方程计算实时卷径)

这里本质是积分法计算实时卷径,PLC里如何实现数值积分器算法请参考下面文章链接: 博途PLC数值积分器(矩形积分和梯形积分法自由切换) 博途PLC数值积分器(矩形梯形积分自由切换)_博图 积分计算-CSDN博客文章浏览阅读505次。本文详细介绍了博途PLC的数值积分器功能,涵盖了矩…

【Mysql】Mysql的多表查询---多表联合查询(上)

1、介绍 多表查询就是同时查询两个或者两个以上的表&#xff0c;因为有的时候&#xff0c;用户在查看数据的时候&#xff0c;需要显示的数据来自多张表&#xff0c;多表查询有以下分类&#xff1a; &#xff08;1&#xff09;交叉连接查询&#xff08;产生笛卡尔积&#xff0…

Shell基础(4)

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…

跨平台WPF框架Avalonia教程 十五

ListBox 列表框 列表框从元素源集合中显示多行元素&#xff0c;并允许选择单个或多个。 列表中的元素可以组合、绑定和模板化。 列表的高度会扩展以适应所有元素&#xff0c;除非特别设置&#xff08;使用高度属性&#xff09;&#xff0c;或由容器控件设置&#xff0c;例如…

有序数组的平方(leetcode 977)

一个数组&#xff0c;返回一个所有元素的平方之后依然是一个有序数组。&#xff08;数组中含负数&#xff09; 解法一&#xff1a;暴力解法 所有元素平方后再使用快速排序法重新排序&#xff0c;时间复杂度为O(nlogn)。 class Solution { public:vector<int> sortedSqu…

使用 Go 实现将任何网页转化为 PDF

在许多应用场景中&#xff0c;可能需要将网页内容转化为 PDF 格式&#xff0c;比如保存网页内容、生成报告、或者创建网站截图。使用 Go 编程语言&#xff0c;结合一些现有的库&#xff0c;可以非常方便地实现这一功能。本文将带你一步一步地介绍如何使用 Go 语言将任何网页转换…

ASP.NET 部署到IIS,访问其它服务器的共享文件 密码设定

asp.net 修改上面的 IIS需要在 配置文件 添加如下内容 》》》web.config <system.web><!--<identity impersonate"true"/>--><identity impersonate"true" userName"您的账号" password"您的密码" /><co…