关键数据结构
1. sk_buf
sk_buf{
布局字段
通用字段
功能专用字段
管理函数
}
1.1 布局字段
内核为了方便搜寻和组织数据结构,使用了双向链表来维护所有的sk_buf结构,但是该表的组织比传统的双向链表更为复杂。
struct sk_buf_head{
struct sk_buf *next
struct sk_buf *prev
__u32 qlen //元素的数目。
spinloct_t lock //防止对表的并发访问。
}
struct sk_buf {
struct sk_buf *next
struct sk_buf *prev
struct sk_buf_head *list //指向专一的sk_buf_head数据结构。
struct sock *sk //指向缓冲区的套接字数据结构,当数据由进程接收时,就需要这个指针。T4以及用户程序使用。
unsigned int len //缓冲区数据块的大小(含数据包的头)。
unsigned int data_len //只计算片段中的数据大小。
unsigned int mac_len //mac报头的大小。
atomic users //引用计数,使用sk_buf实例的数目。
unsigned int truesize //此缓冲区的总大小。
unsigned char *head //缓冲区数据的开端。
unsigned char *end //缓冲区数据的尾端。
unsigned char *data //缓冲区实际数据的开端。
unsigned char *tail //缓冲区实际数据的尾端。
void (*destructor)(...) //函数指针可初始化为一个函数,但缓冲区被删除时,可完成某项工作。
}
1.2 通用字段
struct sk_buf {
...
struct timeval stamp //时间戳,表示封包何时被接收,或有时用于封包预定传输的时间。
struct net_device *dev //用于描述一个网络设备。
struct net_device *input_dev //已被接收封包来自的设备。
struct net_device *real_dev //此字段只对虚拟设备有意义。
union {...} h //h是针对L4(传输层)。
union {...} nh //nh是针对L3(网络层)。
union {...} mac //mac是针对L2(网络接口层)。
struct dst_entry dst //这个结构由路由子系统使用。
char cb[40] //“控制缓冲区”,为每一层内部起维护作用。
unsigned int csum //校验和。
unsigned char ip_summed //状态标识。
unsigned char cloned //相当于一个boolean标识位,改结构是不是另一个sk_buf拷贝来的。
unsigned char pkt_type //根据帧的L2目的地址进行划分。
__u32 priority //标示真被传输或转发的封包QoS等级。
unsigned short protocol //从L2层来看,用于下一层较高的协议。
unsigned short security //封包的安全级别。
}
1.3 功能专用字段
struct sk_buf {
...
/**
以下参数由防火墙使用。
*/
unsigned long mfmark
__u32 nfcache
__u32 nfctinfo
struct nf_conntrack *nfct
unsigned int nf debug
struct nf_brudge_info *nf_bridge
union {...} private //这个联合由HIPPI(高性能并行串口)使用。
/**
以下参数由于流量控制使用。
*/
__u32 tc_index
__u32 tc_verd
__u32 tc_classid
struct sec_path *sp //由IPsec协议组使用,已记录转换信息。
}
1.4 管理函数
分配内存:alloc_skb和dev_alloc_skb
alloc_skb是分配缓冲区的主要函数。alloc_skb通过调用kmem_cache_alloc函数,从一个缓存中渠道sk_buff数据结构,然后调用kmalloc以取得一个数据缓冲区。
skb = kmem_cache_alloc(...)
...
size = SKB_DATA_ALIGN(size)
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask)
dev_alloc_skb是由设备驱动程序使用的缓冲区分配函数,应该在中断模式中执行。此函数只是包裹了alloc_skb的函数,为了优化的原因在申请之上加了16个字节。因为此函数是由中断事件处理的函数调用,所以要求原子操作(GFP_ATOMIC)。
释放内存:kfree_skb和dev_kfree_skb
这两个函数会释放一个缓冲区,使其返回缓存池子。dev_kfree_skb不做任何的事情,只是简单的调用kfree_skb。只有当skb->users = 1的时候,这个函数才会释放缓冲区。否则,递减该计数器。
数据预留及对齐:skb_reserve、skb_put、skb_push以及skb_pull
skb_resever会在缓冲区头部预留一些空间,通常允许插入一些包头,或者强迫数据对齐某个边界。注意,sk_reserve并没有将任何东西移入数据缓冲区内,只是更新了两个指针。
skb_push会把一个数据块添加到缓冲区的开端,而skb_put会把一个数据块添加到缓冲区的结尾。skb_pull是吧head指针向前移,吧一个数据块从缓冲区的头部删除。
skb_shared_info结构和skb_shinfo函数
skb_shared_info用以保持数据块区域的附加信息,此数据结构标记数据尾端的end指针后。
struct skb_shared_info {
automic_t dataref //数据块的用户数目。
/**
以下字段用来处理IP片段。
*/
unsigned int nr_frags
struct sk_buff *frag_list
skb_frag_t frags[MAX_SKB_FRAGS]
/**
以下字段用来处理TCP节段卸载。
*/
unsigned short tso_size
unsigned short tso_seqs
}
skb_buff结构没有指向skb_shared_info的字段,为了访问该结构,必须使用返回end指针的skb_shinfo宏。
缓冲区的克隆和拷贝
当一个缓冲区需要被不同消费者处理时,那些消费者可能需要sk_buf描述符的内容。skb_clone只克隆sk_buff结构,共享数据缓冲区。当一个缓冲区被克隆时,数据不能被修改。当程序员想要修改缓冲区的数据的时候可以使用pskb_copy和skb_copy。
2. net_device
net_device{
标识符
配置字段
设备状态字段
列表管理字段
链路层多播字段
流量管理字段
功能专用字段
通用字段
函数指针
}
2.1 标识符
struct net_device {
int ifindex //唯一id,设备注册时分配给每个设备。
int iflink //由虚拟隧道使用,标识抵达隧道另一端的设备。
unsigned short dev_id //在zSeries OSA NIC上由ipv6使用。
}
2.2 配置字段
struct net_device {
...
char name[IFNAMSIZ] //设备的名称。
unsigned long mem_start
unsigned long men_end //描述设备所用的共享内存,用于设备和内核之间。
unsigned long base_addr //设备自有内存映射到IO内存的起始地址。
unsigned int irq //设备好内核对话的中断信号。
unsigned char if_port //此接口使用的端口类型。
unsigned char dma //设备使用的dma通道。
unsigned short flags //有些位表示网络设备功能,其他位表示状态改变。
unsigned short gflags //几乎不用,兼用方式存在。
unsigned short priv_flags //存储用户空间不可用的标识。
int features //用于存储其他一些设备功能。
unsigned mtu //最大传输单元,设备能处理帧的最大尺寸。
unsigned short type //设备所属的类型。
unsigned short hard_header_len //以字节为单位设备头的大小。
unsigned char broadcast[MAX_ADDR_LEN] //链路层广播地址。
unsigned char dev_addr[MAX_ADDR_LEN] //设备层地址。
unsigned char addr_len //依赖于设备的类型。
int promiscuity //混杂模式计数器。
}
2.3 设备状态字段
struct net_device {
...
unsigned long state //网络队列子系统的一组标识。
enum {...} reg_state //设备的注册状态。
unsigned long trans_start //最后一个帧的启动时间。
unsigned long last_rx //最后一个封包的到达时间。
struct net_device *master //有些协议容许一组设备集群做单一设备,如EQL。
spinlock_t xmit_lock //使驱动程序函数hard_start_xmit访问串行化。
int xmit_lock_owner //持有该锁的CPU ID。
/**
以下六个字段都是指针,指向特定协议专属的数据结构。
*/
void *atalk_ptr
void *ip_ptr
void *dn_ptr
void *ip6_ptr
void *ec_ptr
void *ax25_ptr
}
2.4 列表管理字段
struct net_device {
...
struct net_device *next //链接到全局列表的下一个元素。
/**
链接到两个hash表的bucket列表。
*/
struct hlist_node name_hlist
struct hlist_node index_hlist
}
2.5 链路层多播
struct net_device {
...
struct dev_mc_list *mc_list //指向结构链表表头的指针。
struct mc_count //此设备多播地址数目。
int allmulti //非零时,此设备监听所有多播地址。
}
2.6 流量管理
struct net_device {
...
struct net_device *next_sched //由软中断之一使用。
/**
以下字段管理入口和出口封包队列,以及不同CPU对此设备的访问。
*/
struct Qdisc *qdisc
struct Qdisc *qdisc_sleeping
struct Qdisc *qdisc_ingress
struct list_head qdisc_list
spinlock_t queue_lock //避免对出口队列的并发访问。
spinlock_t ingress_lock //针对入口流量做相同的事。
unsigned long tx_queue_len//设备传送队列的长度。
}
2.7 功能专用字段
struct net_device {
...
struct divert_blk *divert //分流器,容许改变输入封包的源和目的地址。
struct net_bridge_port *br_port //此设备配置成桥接端口需要额外的信息。
/**
以下三个函数由vlan代码所用。
*/
void (*vlan_rx_register) (...) //设备注册为vlan标记功能。
void (*vlan_rx_add_vid) (...) //把vlan添加至设备。
void (*vlan_rx_kill_vid) (...) //从设备中删除vlan。
/**
选用Netpoll功能使用。
*/
int netpoll_rx
void (*poll_controller) (...)
}
2.8 通用字段
struct net_device {
...
atomic_t refcnt //引用计数,变成0之前无法注册。
/**
看门狗定时器。
*/
int watchdog_timeo
struct timer_list watchdog_timer
/**
由NAPI功能使用。
*/
int (*poll) (...)
struct list_head poll_list
int quota
int weight
/**
由无线设备使用的参数和函数指针。
*/
const struct iw_handler_def *wireless_handlers
struct iw_public_data *wireless_data
struct list_head todo_list //网络设备的注册和出除名是以两部进行的。
struct class_device class_dev //新的内核驱动程序基础框架使用。
}
2.9 函数指针
struct net_deivce {
...
struct ethtool_ops *ethtool_ops //用于取出不同设备参数的配置。
/**
初始化、清理、摧毁、开启以及关闭一个设备。
*/
int (*init) (...)
void (*uninit) (...)
void (*destructor) (...)
int (*open) (...)
int (*close) (...)
/**
驱动程序收集的一些统计数据可以使用用户空间程序使用。
*/
struct net_device_stats* (*get_status) (...)
struct iw_statistics* (*get_wireless_status) (...)
int (*hard_start_xmit) (...) //用于传输一个帧。
/**
由邻居层使用。
*/
int (*hard_header) (...)
int (*rebuild_header) (...)
int (*hard_header_cache) (...)
void (*header_cache_update) (...)
int (*header_cache_parse) (...)
int (*neigh_setup) (...)
int (*do_ioctl) (...) //系统调用,用于向设备发送指令。
void (*set_multicast_list) (...) //设备程序配置设备以监听这些地址。
int (*set_mac_address) (...) //改变设备mac地址。
int (*set_config) (...) //配置驱动程序参数。
int (*change_mtu) (...) //改变设备的MTU值。
void (*tx_timeout) (...) //看门狗定时器到期时调用。
int (*accpet_fastpath) (...) //快速交换的额一种内核功能。
}
标签:...,struct,int,skb,unsigned,缓冲区,Linux,内幕,数据结构
From: https://www.cnblogs.com/cjswangtao/p/16987405.html