Linux中网络初始化函数sock_init的实现
网络套接字初始化sock_init
void __init sock_init(void)
{int i;/** Initialize all address (protocol) families. */for (i = 0; i < NPROTO; i++) net_families[i] = NULL;/** Initialize sock SLAB cache.*/sk_init();#ifdef SLAB_SKB/** Initialize skbuff SLAB cache */skb_init();
#endif/** Initialize the protocols module. */init_inodecache();register_filesystem(&sock_fs_type);sock_mnt = kern_mount(&sock_fs_type);/* The real protocol initialization is performed when* do_initcalls is run. */#ifdef CONFIG_NETFILTERnetfilter_init();
#endif
}
函数功能概述
sock_init 函数是Linux网络子系统的核心初始化函数,负责建立套接字基础设施
void __init sock_init(void)
{int i;
函数声明:
void __init: 初始化函数,完成后内存可被释放sock_init(void): 网络套接字初始化主函数int i: 循环计数器,用于初始化协议家族数组
协议家族数组初始化
/** Initialize all address (protocol) families. */for (i = 0; i < NPROTO; i++) net_families[i] = NULL;
协议家族表初始化:
- 初始化所有地址(协议)家族
for (i = 0; i < NPROTO; i++): 遍历所有可能的协议家族net_families[i] = NULL: 将每个协议家族指针初始化为NULL- NPROTO: 最大协议数量常量
- net_families: 全局数组,存储所有注册的网络协议家族处理程序
套接字SLAB缓存初始化
/** Initialize sock SLAB cache.*/sk_init();
套接字对象缓存初始化:
- 初始化sock SLAB缓存
sk_init(): 初始化套接字对象的SLAB分配器- SLAB缓存: Linux内核的高效内存分配机制
- 作用: 为
struct sock结构体提供快速的内存分配和释放
SKB缓冲区的SLAB缓存初始化
#ifdef SLAB_SKB/** Initialize skbuff SLAB cache */skb_init();
#endif
条件编译的SKB缓存初始化:
#ifdef SLAB_SKB: 只在配置了SLAB_SKB时编译- 初始化
skbuff SLAB缓存 skb_init(): 初始化套接字缓冲区(sk_buff)的SLAB分配器sk_buff: 网络数据包在内核中的表示结构- 重要性:
sk_buff是网络栈中最频繁分配和释放的数据结构
套接字文件系统初始化
/** Initialize the protocols module. */init_inodecache();register_filesystem(&sock_fs_type);sock_mnt = kern_mount(&sock_fs_type);
套接字文件系统设置:
-
init_inodecache(): 初始化套接字文件系统的efs_inode_info结构体的slab缓存- 为
sockfs创建专用的SLAB缓存 - 提高
inode分配效率
- 为
-
register_filesystem(&sock_fs_type): 注册套接字文件系统类型sock_fs_type: 套接字文件系统类型结构体- 向VFS注册新的文件系统类型
-
sock_mnt = kern_mount(&sock_fs_type): 内核挂载套接字文件系统- 创建并挂载
sockfs的挂载点 sock_mnt: 全局变量存储挂载信息
- 创建并挂载
初始化调用说明
/* The real protocol initialization is performed when* do_initcalls is run. */
协议初始化时机说明:
- 真正的协议初始化在
do_initcalls运行时执行 - 这里只建立基础设施
网络过滤器初始化
#ifdef CONFIG_NETFILTERnetfilter_init();
#endif
}
Netfilter初始化:
#ifdef CONFIG_NETFILTER: 只在配置了Netfilter时编译netfilter_init(): 初始化网络过滤框架Netfilter: Linux的包过滤框架,iptables的基础- 函数结束
sock初始化sk_init
void __init sk_init(void)
{sk_cachep = kmem_cache_create("sock", sizeof(struct sock), 0,SLAB_HWCACHE_ALIGN, NULL, NULL);if (!sk_cachep)printk(KERN_CRIT "sk_init: Cannot create sock SLAB cache!");if (num_physpages <= 4096) {sysctl_wmem_max = 32767;sysctl_rmem_max = 32767;sysctl_wmem_default = 32767;sysctl_rmem_default = 32767;} else if (num_physpages >= 131072) {sysctl_wmem_max = 131071;sysctl_rmem_max = 131071;}
}
函数功能概述
这是一个socket子系统的初始化函数,主要完成两个任务:创建socket对象的内存缓存池,以及根据系统内存大小设置socket缓冲区的大小限制
代码逐段解析
函数定义
void __init sk_init(void)
{
void __init: 返回值为void,__init宏表示该函数在系统初始化后会被释放(节省内存)sk_init: 函数名,socket initialize的缩写
创建socket对象缓存池
sk_cachep = kmem_cache_create("sock", sizeof(struct sock), 0,SLAB_HWCACHE_ALIGN, NULL, NULL);
sk_cachep: 全局变量,指向socket对象的内存缓存池kmem_cache_create(): 内核内存缓存创建函数,参数如下:"sock": 缓存名称,用于调试和/proc/slabinfo显示sizeof(struct sock): 每个对象的大小,即struct sock结构体的大小0: 对齐偏移量,0表示使用默认值SLAB_HWCACHE_ALIGN: 标志位,要求硬件缓存行对齐,提高缓存性能NULL: 构造函数指针,这里为NULL表示不需要特殊的初始化NULL: 析构函数指针,这里为NULL表示不需要特殊的清理
if (!sk_cachep)printk(KERN_CRIT "sk_init: Cannot create sock SLAB cache!");
if (!sk_cachep): 检查缓存池是否创建成功printk(KERN_CRIT ...): 如果创建失败,输出关键错误信息到内核日志KERN_CRIT: 表示关键错误级别,通常会在控制台显示
根据系统内存设置socket缓冲区限制
if (num_physpages <= 4096) {sysctl_wmem_max = 32767;sysctl_rmem_max = 32767;sysctl_wmem_default = 32767;sysctl_rmem_default = 32767;
小内存系统配置(≤4096页):
num_physpages <= 4096: 判断物理内存页数是否小于等于4096页- 假设页大小为4KB,则4096页 = 16MB内存
sysctl_wmem_max = 32767: 设置发送缓冲区最大值为32767字节(约32KB)sysctl_rmem_max = 32767: 设置接收缓冲区最大值为32767字节sysctl_wmem_default = 32767: 设置发送缓冲区默认值为32767字节sysctl_rmem_default = 32767: 设置接收缓冲区默认值为32767字节
} else if (num_physpages >= 131072) {sysctl_wmem_max = 131071;sysctl_rmem_max = 131071;}
}
大内存系统配置(≥131072页):
num_physpages >= 131072: 判断物理内存页数是否大于等于131072页- 131072页 = 512MB内存(131072 × 4KB)
sysctl_wmem_max = 131071: 设置发送缓冲区最大值为131071字节(约128KB)sysctl_rmem_max = 131071: 设置接收缓冲区最大值为131071字节
详细功能说明
1. 内存缓存池创建
- 目的: 高效管理socket对象的内存分配
- 优势:
- 避免频繁的
kmalloc/kfree操作 - 减少内存碎片
- 硬件缓存对齐提高性能
- 避免频繁的
- 重要性: 如果创建失败,整个socket子系统将无法正常工作
2. 缓冲区大小调优
- 设计理念: 根据系统内存容量自适应调整
- 三个级别:
- 小内存系统(≤16MB): 保守配置32KB限制,避免内存耗尽
- 中等内存系统: 使用内核编译时的默认值
- 大内存系统(≥512MB): 宽松配置128KB限制,提高网络性能
netfilter初始化netfilter_init
void __init netfilter_init(void)
{int i, h;for (i = 0; i < NPROTO; i++) {for (h = 0; h < NF_MAX_HOOKS; h++)INIT_LIST_HEAD(&nf_hooks[i][h]);}
}
函数功能概述
这是一个netfilter子系统的初始化函数,用于初始化网络协议钩子(hook)的双向链表结构,为后续的网络包过滤、NAT等功能的钩子函数注册做好准备
代码逐段解析
函数定义
void __init netfilter_init(void)
{
void __init: 返回值为void,__init宏表示该函数在系统初始化后会被释放(节省内存)netfilter_init: 函数名,netfilter initialize的缩写
变量声明
int i, h;
int i: 外层循环计数器,用于遍历网络协议int h: 内层循环计数器,用于遍历钩子点
嵌套循环初始化
for (i = 0; i < NPROTO; i++) {for (h = 0; h < NF_MAX_HOOKS; h++)INIT_LIST_HEAD(&nf_hooks[i][h]);}
}
外层循环
for (i = 0; i < NPROTO; i++)
i = 0: 从第一个协议开始i < NPROTO: 循环条件,NPROTO是网络协议的数量常量- 遍历所有支持的网络协议(如IPv4、IPv6、ARP等)
内层循环
for (h = 0; h < NF_MAX_HOOKS; h++)
h = 0: 从第一个钩子点开始h < NF_MAX_HOOKS: 循环条件,NF_MAX_HOOKS是每个协议支持的最大钩子点数- 遍历每个协议的所有可能钩子点
链表头初始化
INIT_LIST_HEAD(&nf_hooks[i][h]);
INIT_LIST_HEAD(): Linux内核双向链表初始化宏&nf_hooks[i][h]: 取第i个协议第h个钩子点的链表头地址nf_hooks: 全局的二维数组,结构为:struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]
数据结构详细说明
nf_hooks二维数组结构
nf_hooks[NPROTO][NF_MAX_HOOKS]
│
├── [0] (IPv4协议)
│ ├── [0] NF_IP_PRE_ROUTING 钩子链表
│ ├── [1] NF_IP_LOCAL_IN 钩子链表
│ ├── [2] NF_IP_FORWARD 钩子链表
│ ├── [3] NF_IP_LOCAL_OUT 钩子链表
│ └── [4] NF_IP_POST_ROUTING 钩子链表
│
├── [1] (IPv6协议)
│ ├── [0] NF_IP6_PRE_ROUTING 钩子链表
│ └── ...
│
├── [2] (ARP协议)
│ └── ...
│
└── ... (其他协议)
INIT_LIST_HEAD宏的作用
#define INIT_LIST_HEAD(ptr) do { \(ptr)->next = (ptr); \(ptr)->prev = (ptr); \
} while (0)
- 将链表头的
next和prev指针都指向自己 - 创建一个空的双向循环链表
