概述
内核版本:linux-2.6.8.1
linux目录解析
网络内核布局
套接字
struct sk_buff, struct sk_buff_head
skbuff.h
sk_buff是套接字缓冲区类型,用来管理网络数据包,为发/收的网络数据提供存储区域和操作方法。
sk_buff_head管理一个双向链表,来组织sk_buff。
sk_buff与数据存储
1 | {//struct sk_buff |
sk_buff用union定义的三个协议头部指针:h, nh, mac。每个union有一个用于初始化的raw指针。采用tcp协议的数据包从第三层递交到第四层时指针的变化情况。
sk_buff->end指针指向数据报分片信息,数据包分片由struct skb_shared_info管理。
alloc_skb()
skbuff.c
创建套接字缓冲区并初始化,建立了sk_buff和struct skb_shared_info的关系。
1 | {//alloc_skb |
skb_put(), skb_trim(), skb_push(), skb_pull(), skb_reserve()
skbuff.h
skb_put()用来在数据区后加某协议尾部;调整tail,增加len。
skb_trim()用来去掉数据包的协议尾部;调整tail, 减少len。
skb_push()用来在数据区前加某协议头部;调整data,增加len。
skb_pull()用来去掉数据包的协议头部;调整data,减少len。
skb_reserve()用来在数据区创建存储协议头部的空间。应用:方便skb_push添加头部;调整数据区大小。
skb_queue_head(), skb_queue_tail()
skbuff.h
skb_queue_head() –> _skb_queue_head(),在套接字缓冲区链表头部添加一个缓冲区。
skb_dequeue() –> _skb_dequeue(),把排在头部的缓冲区从套接字缓冲区链表中移走,返回该缓冲区。
skb_queue_tail() –> _skb_queue_tail(),在套接字缓冲区链表尾部添加一个缓冲区。
skb_dequeue_tail() –> _skb_dequeue_tail(),从套接字缓冲区链表尾部移走一个缓冲区。
其中,套接字缓冲区链表即struct sk_buff_head *list,涉及增加操作时参数有待增加缓冲区struct sk_buff *newsk。
网络设备
struct net_device
管理网络设备的数据结构。含:通用字段、硬件配置字段、网络层数据字段、物理层数据字段、设备驱动程序中的函数。
include/linux/netdevice.h
通用字段:设备名称、next指针指向下一个net_device、设备状态state、网络设备索引值ifindex(用来标志网络设备以便快速定位,设备被创建后由dev_get_index函数分配)、refcnt表示网络设备的引用次数。
硬件配置字段:内存共享字段(描述网络适配器与内核共享的内存空间,指定发送包和接受包所在的区域)、I/O基地址(用于驱动程序搜索设备)、设备使用的中断号irq、分配给设备的DMA通道号、多端口设备使用的不同端口if_port(网络介质类型决定)。
物理层数据字段:指定2层协议头部长度、最大传输单元mtu(以太网1500byte)、网络设备输出队列的最大长度、网络设备类型type、地址字段(广播地址、多播地址表等)。
网络层数据字段:协议信息字段(xxx_ptr,指向xxx协议信息)、网络地址信息字段(协议地址族family例如AF_INET、协议地址长度、网络设备地址pa_addr)等。
设备驱动程序中的函数指针:
- int (*init)指向设备初始化函数,注册设备时调用,初始化struct net_device;
- void (*uninit)指向注销设备函数,删除设备时调用;
- int (*open)指向打开设备接口的函数,注册设备需要的系统资源(I/O端口、irq、dma);
- int (*stop)指向停止设备的函数;
- …
网络设备的创建和注册过程
一个网络设备被使用前,需要先被创建成为一个struct net_device并注册。下面描述注册过程:
设备驱动被编译成模块,内核在加载模块时进行初始化,调用init_module。init_module依次调用alloc_etherdev、do_netcard_probe、register_netdev。
alloc_etherdev创建一个以太网设备,返回管理该设备的struct net_device,其中alloc_netdev函数为新创建的网络设备分配struct net_device结构空间,ether_setup初始化与以太网相关的设备信息并将协议相关的函数指针添加到struct net_device结构中。
driver/net/net_init.c
do_netcard_probe设置struct net_device_device主要字段。
register_netdev函数注册该设备,最后调用register_netdevice向dev_base链表添加设备。不需要网络设备时需要unregister_netdev函数注销设备,关闭活动状态的设备并从dev_base链表中删除设备。
net/core/dev.c
网络设备的开启与关闭
net/core/dev.c
*开启网络设备函数dev_open(struct net_device dev)。如果网络设备已经激活或者它尚未被注册,函数返回错误信息。
- 判断设备是否激活;
- 使用set_bit函数修改设备状态为__LINK_STATE_START,调用net_device中的open指向函数设置该设备;
- 激活网络设备的队列和调度器;
- 将事件(NETDEV_UP)登记到通知链:notifier_call_chain(&netdev_chain,事件,dev)。
*关闭网络设备函数dev_close(struct net_device dev)。
- 如果网络设备未被激活,则不需要关闭;
- 将事件(NETDEV_GOING_DOWN)登记到通知链:notifier_call_chain(&netdev_chain,事件名,dev);
- 删除包调度器中的相应信息:dev_deactivate;
- 清除设备的活动状态:clear_bit;
- 调用net_device中的stop指向函数,执行停止操作。
通知链
kernel/sys.c
struct notifier_block类型定义了通知链中每个元素的结构。
1 | struct notifier_block{ |
通知链注册事件函数notifier_chain_register(**notifier_block列表,notifier_block待登记块)**。可以向通知链中等级一个事件,事件发生时可以从notifier_block链表中找到该事件对应的元素,执行记录的函数。
通知链注销事件函数notifier_chain_unregister(**notifier_block列表,notifier_block待注销块)**。可以向通知链中注销一个事件。
事件发生时,使用*notifier_call_chain(通知链名,事件名,void)。向某个通知链表发送消息,按顺序调用链表块中记录的函数**。