从开始的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来传