Linux内核源码学习,套接字缓存sk_buff的操作函数

前言

上一篇解析了sk_buff的结构体成员,这一篇就来解析sk_buff的操作函数。

分配sk_buff:alloc_skb()

函数声明:

struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
			    int fclone, int node)
  • size,待分配sk_buff的线性存储区的长度
  • gfp_mask,分配内存的方式
  • fclone,预测是否会克隆,用于确定从哪个高速缓存中分配
  • node,当支持NUMA(非均匀质存储结构)时,用于确定何种区域中分配sk_buff

图中padding是为了对齐而填充的区域,填充区域上面区域的大小为size

释放sk_buff:kfree_skb()

函数声明:

void kfree_skb(struct sk_buff *skb)

只在 skb->users 为1的情况下才释放内存,否则只是简单的递减

为协议首部预留空间:skb_reserve()

在数据缓存区头部预留一定的空间,只能用于空的sk_buff

只是简单地更新了分别指向负载起始和结尾的data和tail指针

添加用户之前调用:skb_put()

例子:

unsigned char *data = skb_put(skb,   user_data_len);
memcpy(data, 0x11,  user_data_len);

修改tail指针,下移len字节,同时更新数据区长度len

添加首部:skb_push()

例子:

unsigned char *tcp_header = skb->push(skb, sizeof(struct udphdr));
struct tcphdr *tcp;
tcp = tcp_header;
tcp->source = htons(1025);
tcp->dest = htons(2095);
tcp->len = htons(user_data_len);
tcp->check = 0;

忽略其他层的头部:skb_pull()

将data指针往下移动,在数据区首部忽略len字节长度的数据,通常用于接收的数据包后在各层由下往上传递时,上层忽略下层的首部

小结

克隆sk_buff:skb_clone()

只复制sk_buff描述符,同时增加数据缓存区的引用计数

拷贝sk_buff及其数据:pskb_copy() & skb_copy()

  • 修改的数据在skb->head和skb->end之间,可使用pskb_copy()来复制这部分数据
  • 如果同时需要修改聚合分散IO存储区中的数据,就必须使用skb_copy()

添加尾部数据:skb_add_data()

将指定用户空间的数据添加到skb_buff的数据缓存区的尾部

删除尾部数据:skb_trim()

与skb_add_data()操作相反

拆分数据:skb_split()

根据指定长度拆分sk_buff,使得原sk_buff中的数据长度为指定长度,剩下的数据保存到拆分得到的sk_buff中

LEN:拆分长度,hlen:线性数据长度 + 当拆分数据的长度小于线性数据长度时,直接拆分线性数据区即可

  • 拆分数据的长度大于线性数据长度时,则需要拆分非线性区域中的数据,拆分长度LEN大于hlen并且LEN小于hlen+S1

其它函数

There are a bunch of skb support functions provided by the sk_buff layer.

allocation / free / copy / clone and expansion functions

  • struct sk_buff *alloc_skb(unsigned int size, int gfp_mask) +This function allocates a new skb. This is provided by the skb layer to initialize some privat data and do memory statistics. The returned buffer has no headroom and a tailroom of /size/ bytes.

  • void kfree_skb(struct sk_buff *skb) +Decrement the skb’s usage count by one and free the skb if no references left.

  • struct sk_buff *skb_get(struct sk_buff *skb) +Increments the skb’s usage count by one and returns a pointer to it.

  • struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) +This function clones a skb. Both copies share the packet data but have their own struct sk_buff. The new copy is not owned by any socket, reference count is 1.

  • struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)

    • Makes a real copy of the skb, including packet data. This is needed, if You wish to modify the packet data. Reference count of the new skb is 1.
  • struct skb_copy_expand(const struct sk_buff *skb, int new_headroom, int new_tailroom, int gfp_mask)

    • Make a copy of the skb, including packet data. Additionally the new skb has a haedroom of /new_headroom/ bytes size and a tailroom of /new_tailroom/ bytes.

anciliary functions

  • int skb_cloned(struct sk_buff *skb)

    • Is the skb a clone?
  • int skb_shared(struct sk_Buff *skb)

    • Is this skb shared? (is the reference count > 1)?

operations on lists of skb’s

  • struct sk_buff *skb_peek(struct sk_buffhead *list)

    • peek a skb from front of the list; does not remove skb from the list
  • struct sk_buff *skb_peek_tail(struct sk_buffhead *list)

    • peek a skb from tail of the list; does not remove sk from the list
  • __u32 skb_queue_len(sk_buffhead *list)

    • return the length of the given skb list
  • void skb_queue_head(struct sk_buffhead *list, struct sk_buff *newsk)

    • enqueue a skb at the head of a given list
  • void skb_queue_tail(struct sk_buffhead *list, struct sk_buff *newsk)

    • enqueue a skb at the end of a given list.
  • int skb_headroom(struct sk_buff *skb)

    • return the amount of bytes of free space at the head of skb
  • int skb_tailroom(struct sk_buff *skb)

    • return the amount of bytes of free space at the end of skb
  • struct sk_buff *skb_cow(struct sk_buff *skb, int headroom)

    • if the buffer passed lacks sufficient headroom or is a clone it is copied and additional headroom made available.
  • void struct sk_buff *skb_dequeue(struct sk_buffhead *list)

    • skb_dequeue() takes the first buffer from a list (dequeue a skb from the head of the given list) If the list is empty a NULL pointer is returned. This is used to pull buffers off queues. The buffers are added with the routines skb_queue_head() andskb_queue_tail().
  • struct sk_buff *sbk_dequeue_tail(struct sk_buffhead *list)

    • dequeue a skb from the tail of the given list

总结

这篇笔记包括了大部分sk_buff的操作函数,为以后使用打下基础