sk_buff整理笔记(五、队列管理函数)

本文详细探讨了Linux内核中sk_buff结构的队列管理函数,包括初始化、插入、出队列、清空及遍历等操作。通过分析各个函数的实现细节,如skb_queue_head_init(), skb_queue_head(), skb_queue_tail(), skb_unlink()等,深入理解sk_buff队列的使用和工作原理。" 125086850,9768946,CentOS7下卸载与安装MySQL5.7详细教程,"['MySQL', 'CentOS', '系统管理', '数据库管理']
摘要由CSDN通过智能技术生成

        从开始的sk_buff结构及相关结构体的成员变量分析到sk_buff结构的一些操作函数,然后是sk_buff结构的内存申请和释放,接着是几个克隆拷贝函数的区别,再到现在要分析得列表管理函数。这有关sk_buff结构的一系列blog差不多就接近尾声了,仔细的分析了这几块内容,虽不能说对sk_buff结构非常了解,但也有了个全新的认识。感觉收获匪浅。下面开始分析下队列管理函数。

初始化函数:

        头结点初始化函数:void skb_queue_head_init(struct sk_buff_head *list);首先获取到sk_buff_head结构体中自旋锁,因为队列管理函数都是原子操作(要么不操作,要不一定要操作完,操作时不能被打扰),所以获取到锁才可以操作,防止异步中断。然后创建个空的链表。函数实现如下:

static inline void skb_queue_head_init(struct sk_buff_head *list)
{
	spin_lock_init(&list->lock);// 获得头结点中的自旋锁
	__skb_queue_head_init(list); // 调用函数初始化头结点
}
static inline void __skb_queue_head_init(struct sk_buff_head *list)
{
	list->prev = list->next = (struct sk_buff *)list;// 创建一个链表
	list->qlen = 0;// 链表节点个数为零
}


插入函数:

        插入分为从队列的头部插入和队列的尾部插入,从队列头部插入由skb_queue_head()函数实现,从队列尾部插入由skb_queue_tail()函数实现。

头部插入函数实现:

void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
{
	unsigned long flags;

	spin_lock_irqsave(&list->lock, flags); // 获取自旋锁,并且上锁
	__skb_queue_head(list, newsk); // 真正的插入操作
	spin_unlock_irqrestore(&list->lock, flags); // 解锁操作
}
static inline void __skb_queue_head(struct sk_buff_head *list,
				    struct sk_buff *newsk)
{
	__skb_queue_after(list, (struct sk_buff *)list, newsk);
// 第一个list本来表示的是一个链表指针,第二个list是由第一个队列指针list强转为链表头部结点
}
static inline void __skb_queue_after(struct sk_buff_head *list,
				     struct sk_buff *prev,
				     struct sk_buff *newsk)
{
	__skb_insert(newsk, prev, prev->next, list); // 这里的prev实则是链表头部结点, 
}

        可以看出头部插入函数一开始就是获取自旋锁,然后上锁操作,接下来才是真正的数据操作,最后一步是解锁。数据操作调用了_skb_queue_head()函数,而该函数其实什么也没做只是调用了_skb_queue_after()函数,同样的_skb_queue_after()函数也是什么都没做只是仅仅调用了_skb_insert()。所以归根结底还是_skb_insert()函数在做插入操作,而其他函数只是在传参数的时候告诉该函数往哪个位置上插入。后面会着重的分析下_skb_insert()插入函数。

尾部插入函数实现:

void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
{
	unsigned long flags;

	spin_lock_irqsave(&list->lock, flags); // 同头部插入函数一样,上锁操作
	__skb_queue_tail(list, newsk); // 调用插入函数
	spin_unlock_irqrestore(&list->lock, flags); // 解锁操作
}
static inline void __skb_queue_tail(struct sk_buff_head *list,
				   struct sk_buff *newsk)
{
	__skb_queue_before(list, (struct sk_buff *)list, newsk); // 调用函数实现参数的变化
}
static inline void __skb_queue_before(struct sk_buff_head *list,
				      struct sk_buff *next,
				      struct sk_buff *newsk)
{
	__skb_insert(newsk, next->prev, next, list); // 调用函数实现真正的插入操作
}
        尾部插入函数其实和头部插入函数非常相似(本来也应该相似,因为他们都是插入操作,仅仅不同的只是插入位置而已),上锁----》插入----》解锁,其他调用的函数也是什么都没有做仅仅是到最后调用_skb_insert()函数来插入,以及在传参数时,控制插入的位置而已。


不管是从队列的头部插入还是从队列的尾部插入,其本质都是调用_skb_insert()函数来进行插入处理。下面分析该函数的实现:

_skb_insert()函数实现:

static inline void __skb_insert(struct sk_buff *newsk,
				struct sk_buff *prev, struct sk_buff *next,
				struct sk_buff_head *list)
{
	newsk->next = next;
	newsk->prev = prev;
	next->prev  = prev->next = newsk;
	list->qlen++;
}
该函数实现的是在prev和next之间插入newsk结构体,所以如果是队列头部插入:则prev这个形参就要用头结点来传入,next就要用头结点->next来传
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值