kernel网络协议栈初始化
来源:互联网 发布:扳机在淘宝叫什么 编辑:程序博客网 时间:2024/05/05 22:02
kernel的网络初始化顺序:
- core_initcall : sock_init
- fs_initcall : inet_init
- subsys_initcall : net_dev_init
- device_initcall : 设备驱动初始化
网络基础系统初始化
第一步,使用core_initcall初始化宏修饰sock_init函数,这个宏指定了sock_init函数放在级别为1的代码中,也就是说它的执行时最先进的一部分,此函数只是分配一些内存空间,以及创建了一个sock_fs_type的文件系统。在do_basic_setup中调用sock_init先于internet协议注册被调用,因此基本的socket初始化必须在每一个TCP/IP成员协议能注册到socket层之前完成。
static int __init sock_init(void){ /* * Initialize sock SLAB cache. */ sk_init(); /* * Initialize skbuff SLAB cache */ skb_init(); /* * Initialize the protocols module. */ init_inodecache(); register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type); /* The real protocol initialization is performed in later initcalls. */#ifdef CONFIG_NETFILTER netfilter_init();#endif#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING skb_timestamping_init();#endif return 0;}
sock_init函数看上去比较简单,其实里面完成了相当重要的工作。第一句调用sk_init(),其实不做什么实质性的事,只是对一些变量进行赋值。
void __init sk_init(void){ if (totalram_pages <= 4096) { sysctl_wmem_max = 32767; sysctl_rmem_max = 32767; sysctl_wmem_default = 32767; sysctl_rmem_default = 32767; } else if (totalram_pages >= 131072) { sysctl_wmem_max = 131071; sysctl_rmem_max = 131071; }}
网络内存管理内核
skb_init函数
void __init skb_init(void){ skbuff_head_cache = kmem_cache_create("skbuff_head_cache", sizeof(struct sk_buff), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", (2*sizeof(struct sk_buff)) + sizeof(atomic_t), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);}
函数的作用就是创建了两个缓存,skbuff_head_cache和skbuff_fclone_cache。协议中相关的数据都在这两个缓存中创建。
sk_buff结构
数据包在应用层成为data,在TCP层成为segment,在IP层成为packet,在数据链路层成为frame。linux内核中sk_buff{}结构来存放数组,在INET socket和它以下的层次中用来存放网络接收到或需要发送的数据,因此它需要设计的有足够扩展性。
sk_buff结构:
struct sk_buff { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev; ktime_t tstamp; struct sock *sk; struct net_device *dev; /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ char cb[48] __aligned(8); unsigned long _skb_refdst;#ifdef CONFIG_XFRM struct sec_path *sp;#endif unsigned int len, data_len; __u16 mac_len, hdr_len; union { __wsum csum; struct { __u16 csum_start; __u16 csum_offset; }; }; __u32 priority; kmemcheck_bitfield_begin(flags1); __u8 local_df:1, cloned:1, ip_summed:2, nohdr:1, nfctinfo:3; __u8 pkt_type:3, fclone:2, ipvs_property:1, peeked:1, nf_trace:1; kmemcheck_bitfield_end(flags1); __be16 protocol; void (*destructor)(struct sk_buff *skb);#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; struct sk_buff *nfct_reasm;#endif#ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info *nf_bridge;#endif int skb_iif;#ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */#ifdef CONFIG_NET_CLS_ACT __u16 tc_verd; /* traffic control verdict */#endif#endif __u32 rxhash; kmemcheck_bitfield_begin(flags2); __u16 queue_mapping:16;#ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2, deliver_no_wcard:1;#else __u8 deliver_no_wcard:1;#endif kmemcheck_bitfield_end(flags2); /* 0/14 bit hole */#ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie;#endif#ifdef CONFIG_NETWORK_SECMARK __u32 secmark;#endif union { __u32 mark; __u32 dropcount; }; __u16 vlan_tci; sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; /* These elements must be at the end, see alloc_skb() for details. */ sk_buff_data_t tail; sk_buff_data_t end; unsigned char *head, *data; unsigned int truesize; atomic_t users;};
- next和prev,这两个域是用来连接相关的skb的(例如如果有分片,将这些分片连接在一起可以)
- sk,指向报文所属的套接字指针
- stamp,记录接收或者传输报文的时间戳
- dev和input_dev,记录接收或者发送的设备
- union u,对于一个层次,例如tcp层,可能有很多不同的协议,他们的协议头不一样,那么这个联合体就是记录这些协议头的。
此处u就是代表传输层 - union nh,代表网络层头
- union mac,代表链路层头
- dst,指向des_entry结构,记录了到达目的地的路由信息,以及其他的一些网络特征信息。
- sp:安全路径,用于xfrm
- cb[],保存与协议相关的控制信息,每个协议可能独立使用这些信息。
- 重要的字段 len 和 data_len:
len代: 表整个数据区域的长度!这里要提前解释几个定义,skb的组成是有sk_buff控制 + 线性数据 + 非线性数据
(skb_shared_info) 组成!
后面会具体解释是什么意思!在sk_buff这个里面没有实际的数据,这里仅仅是控制信息,数据是通过后面的data指针指向其他内
存块的!那个内存块中是线性数据和
非线性数据!那么len就是length(线性数据) + length(非线性数据)!
data_len: 指的是length(非线性数据)!那么可以知道:length(线性数据) = skb->len - skb->data_len - mac_len,指的是mac头长度
- csum,某时刻协议的校验和
- priority,报文排队优先级,取决于ip中的tos域
- local_df,允许在本地分配
- cloned,保存当前的skb_buff是克隆的还是原始数据
- ip_summed,是否计算ip校验和
- nohdr,仅仅引用数据区域
- pkt_type,报文类型,例如广播,多播,回环,本机,传出…
- fclone,skb_buff克隆状态
- ipvs_property,skb_buff是否属于ipvs
- protocal,协议信息
- nfmark,用于钩子之间通信
- nfct_reasm,netfilter的跟踪连接重新组装指针
- nf_bridge,保存桥接信息
- tc_index: Traffic control index,tc_verd: traffic control verdict
- truesize,该缓冲区分配的所有总的内存,包括:skb_buff + 所有数据大小
- users,保存引用skb_buff的数量
- 重要数据字段:head,data,tail,end
head:指向分配给的线性数据内存首地址( 建立起一个观念:并不是分配这么多内存,就都能被使用作为数据存储,可能没这么多
数据也有可能!但是也不要认为分配这么多 就足够了,也不一定(非线性数据就是例子) )
data:指向保存数据内容的首地址!我们由head可以知道,head和data不一定就是指在同一个位置!
tail:指向数据的结尾!
end:指向分配的内存块的结尾! ( 由上面我们知道数据结尾 != 分配的内存块的结尾 )
sk_buff.pkt_type是指该数据包的类型,定义如下:
为了使用套接字缓冲区,内核创建了两个后备高速缓存lookaside cache,他们分别是skbuff_head_cache和skbuff_fclone_cache,协议栈中所使用到的所有sk_buff结构都是从这两个后备高速缓存中分配出来的。两者的区别在于skbuff_head_cache在创建时指定的单位内存区域的大小是sizeof(struct sk_buff),可以容纳任意数目的struct sk_buff,而skbuff_fclone_cache在创建时指定的单位内存区域大小是2*sizeof(struct sk_buff)+sizeof(atomic_t),它的最小区域单位是一对struct sk_buff和一个引用计数,这一对sk_buff是克隆的,即它们指向同一数据缓冲区,引用计数值是0,1,或2,表示这一对中有几个sk_buff已被调用。
创建一个套接字缓冲区,最常用的操作是alloc_skb,它在skbuff_head_cache中创建一个struct sk_buff,如果要在skbuff_fclone_cache中创建,可以调用__alloc_skb,通过特定参数进行。
再end后面还有一个结构体struct skb_shared_info:
struct skb_shared_info { atomic_t dataref; // 对象被引用次数 unsigned short nr_frags; // 分页段数目,即frags数组元素个数 unsigned short tso_size; unsigned short tso_segs; unsigned short ufo_size; unsigned int ip6_frag_id; struct sk_buff *frag_list; // 一般用于分段(还没有非常清楚的理解) skb_frag_t frags[MAX_SKB_FRAGS]; // 保存分页数据(skb->data_len=所有的数组数据长度之和) };
这个结构体存放分隔存储的数据片段,将数据分解为多个数据片段是为了使用分散/聚集I/O。
内存管理函数
在sk_buff{}中4个指针data、head、tail、end初始化的时候,data,head,tail都是指向申请到数据区的头部,end指向数据区的尾部。在以后的操作中,一般都是通过data和tail来获得在sk_buff中可用的数据区的开始和结尾。而head和end就表示sk_buff中存在的数据包最大可扩展的空间范围。
以下为网络协议栈的内存管理函数
- kernel网络协议栈初始化
- linux kernel 网络协议栈
- linux 网络协议栈初始化
- linux kernel 网络协议栈之GRO
- linux kernel 网络协议栈之GRO
- Linux网络协议栈之设备初始化
- VxWorks网络协议栈初始化流程
- ZigBee协议栈初始化网络启动流程
- 网络协议初始化2
- linux kernel 网络协议栈之GRO(Generic receive offload)
- linux kernel 网络协议栈之GRO(Generic receive offload)
- linux kernel 网络协议栈之xps特性详解
- linux网络协议栈分析——网络文件系统初始化
- 网络协议的初始化dev_add_pack
- 网络协议的初始化dev_add_pack
- 网络协议的初始化dev_add_pack
- 网络协议的初始化dev_add_pack
- 第1章 ZigBee协议栈初始化网络启动流程
- java
- 2016.11.7 html5+css(复习1)
- 自建网页项目-蚁族财富网-记录3
- Java学习之旅
- hive数据倾斜
- kernel网络协议栈初始化
- java和C++的区别
- 编程之美--1.9高效率的安排见面会(解法二python)
- 11.07,上课三程序,100内的素数,1000内的完数,输入一个数计算多少9
- 自定义ViewGroup时直接继承已知布局,节省代码
- HTML5全局属性
- 关于计时的相关知识点
- OpenCV学习——图像的缩放
- /contrib/contrib-global.mk: No such file or directory