Zephyr的net pkt內存由net_pkt和net_buf組成,net_pkt通过slab管理, net_buf通过buf pool管理。net_pkt作为管理结构,将一组net_buf用链表串在一起行程net pkt,用于管理和存储网络封包。本文基于rx和tx分析了net_buf的初始化和管理机制。
概述
net_buf_pool存放了所有的net_buf,net_buf容纳的数据长度是固定的大小的,每个net_buf指向一片固定大小的内存data buf用于保存pkt数据,一个net_buf和一片data buf一一对应,见图一
图一
net_buf
tx和rx的net_buf通过net_buf_pool来管理,通过net_buf_pool来完成net_buf内存定义,初始化,分配,释放。
net buf pool
zephyr用NET_PKT_DATA_POOL_DEFINE定义了两个数组rx_bufs和tx_bufs,在配置文件内通过CONFIG_NET_BUF_RX_COUNT和CONFIG_NET_BUF_TX_COUNT定义其pool内容纳net_bufr的个数。该阶段在编译时就完成1
2
3// subsys/net/ip/net_pkt.c
NET_PKT_DATA_POOL_DEFINE(rx_bufs, CONFIG_NET_BUF_RX_COUNT);
NET_PKT_DATA_POOL_DEFINE(tx_bufs, CONFIG_NET_BUF_TX_COUNT);
展开为1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53static struct net_buf net_buf_rx_bufs[CONFIG_NET_BUF_RX_COUNT] __noinit;
static u8_t __noinit net_buf_data_rx_bufs[CONFIG_NET_BUF_RX_COUNT][CONFIG_NET_BUF_DATA_SIZE];
static const struct net_buf_pool_fixed net_buf_fixed_rx_bufs = {
.data_size = CONFIG_NET_BUF_DATA_SIZE,
.data_pool = (u8_t *)net_buf_data_rx_bufs,
};
static const struct net_buf_data_alloc net_buf_fixed_alloc_rx_bufs = {
.cb = &net_buf_fixed_cb,
.alloc_data = (void *)&net_buf_fixed_rx_bufs,
};
struct net_buf_pool rx_bufs __net_buf_align
__attribute__((section(._net_buf_pool.static.rx))) =
{
.alloc = &net_buf_fixed_rx_bufs,
.free = {
._queue =
{
.wait_q = {{(&rx_bufs.free.wait_q)}, {(&rx_bufs.free.wait_q)}},
.data_q = {{(&rx_bufs.free.data_q)}, {(&rx_bufs.free.data_q)}},
}
},
.__bufs = net_buf_rx_bufs,
.buf_count = CONFIG_NET_BUF_RX_COUNT,
.uninit_count = CONFIG_NET_BUF_RX_COUNT,
.destroy = NULL,
}
static struct net_buf net_buf_tx_bufs[CONFIG_NET_BUF_TX_COUNT] __noinit;
static u8_t __noinit net_buf_data_tx_bufs[CONFIG_NET_BUF_TX_COUNT][CONFIG_NET_BUF_DATA_SIZE];
static const struct net_buf_pool_fixed net_buf_fixed_tx_bufs = {
.data_size = CONFIG_NET_BUF_DATA_SIZE,
.data_pool = (u8_t *)net_buf_data_tx_bufs,
};
static const struct net_buf_data_alloc net_buf_fixed_alloc_tx_bufs = {
.cb = &net_buf_fixed_cb,
.alloc_data = (void *)&net_buf_fixed_tx_bufs,
};
struct net_buf_pool tx_bufs __net_buf_align
__attribute__((section(._net_buf_pool.static.tx))) =
{
.alloc = &net_buf_fixed_tx_bufs,
.free = {
._queue =
{
.wait_q = {{(&tx_bufs.free.wait_q)}, {(&tx_bufs.free.wait_q)}},
.data_q = {{(&tx_bufs.free.data_q)}, {(&tx_bufs.free.data_q)}},
}
},
.__bufs = net_buf_tx_bufs,
.buf_count = CONFIG_NET_BUF_TX_COUNT,
.uninit_count = CONFIG_NET_BUF_TX_COUNT,
.destroy = NULL,
}
从上面的展开可以看到一个net buf pool由4个部分组成:
- 一片连续内存data buf: net_buf_data_tx_bufs[CONFIG_NET_BUF_TX_COUNT][CONFIG_NET_BUF_DATA_SIZE];
- 一个net_buf_data_alloc提供连续内存的分配和释放函数:net_buf_fixed_alloc_tx_bufs
- 一组net_buf用于管理net_buf_data_alloc alloc的buf和net_buf_data_tx_bufs[n]一一对应: net_buf_tx_bufs
- 一个net_buf_pool用于管理net_buf pool:tx_bufs
四个部分的关系见图二:
图二
从展开的代码也可以看到net_buf pool 4个部分在定义的时候已经确定了连接关系,因此无需额外的初始化.
net_buf_pool
net_buf_pool用于管理net_buf pool1
2
3
4
5
6
7
8
9
10
11struct net_buf_pool {
struct k_lifo free; //free net_buf LIFO,释放后的net_buf放到这个LIFO内
const u16_t buf_count; //pool含有net_buf的总数
u16_t uninit_count; //未初始化的net_buf,系统初始化完成后所有的net_buf都是unint状态,每alloc一个就取一个出去,当释放时就只会放大free LIFO内
void (*const destroy)(struct net_buf *buf);
const struct net_buf_data_alloc *alloc;
struct net_buf * const __bufs;
};
net_buf结构
struct net_buf {
union {
sys_snode_t node;
struct net_buf *frags; //指向下一个net_buf
};
u8_t ref; //引用一次+1, free一次-1, -到0时方可做真正的free
u8_t flags;
u8_t pool_id; //指向自己所属的pool
union {
struct {
u8_t *data; //指向buffer的data起始位置 = __buf+header len
u16_t len; //data指针中拥有数据的长度
u16_t size; //data指针最大数据容量
u8_t *__buf; //net buf指向的data buf的起始位置
};
struct net_buf_simple b; //实际代码操作过程中使用simple buf的API操作net buf
};
/** System metadata for this buffer. */
u8_t user_data[CONFIG_NET_BUF_USER_DATA_SIZE] __net_buf_align;
};
分配net_buf
net_pkt_get_data从net buf pool分配并初始化为一个net_buf,调用关系如下net_pkt_get_data->_pkt_get_data->net_pkt_get_reserve_data->net_buf_alloc->net_buf_alloc_fixed->net_buf_alloc_len,数据的设置一共分2层
- net_buf_alloc_len完成从指定pool内分配一个net_buf(net_buf_alloc,net_buf_alloc_fixed只是包装)并建立net_buf和data buf对应联系,见图三
- net_pkt_get_reserve_data(net_pkt_get_data,_pkt_get_data只是包装和获取一些参数)改变net_buf中data的指针(跳过header,指向payload)
图二
1 | struct net_buf *net_buf_alloc_len(struct net_buf_pool *pool, size_t size, |
net_buf和net_pkt关联
从图一可以看到,net_buf链和net_pkt关联形成一个完整的网络封包,这个关联过程由net_pkt_frag_add完成:
- 当net_pkt内frags为NULL时(为发生过关联),frags指向net_buf即可
- 当frags不为NULL时,将add的net_buf放入frags指向的链表尾
1
2
3
4
5
6
7
8
9
10
11
12
13void net_pkt_frag_add(struct net_pkt *pkt, struct net_buf *frag)
{
NET_DBG("pkt %p frag %p (%s:%d)", pkt, frag, caller, line);
//第一次add加到frags中
if (!pkt->frags) {
pkt->frags = frag;
return;
}
//再次add添加到链表尾
net_buf_frag_insert(net_buf_frag_last(pkt->frags), frag);
}
net_pkt&net_buf的使用
zephyr使用下面几个API将数据cp到net_pkt所持有的net_buf中
- net_pkt_append_u8
- net_pkt_append_be16
- net_pkt_append_be32
- net_pkt_append_le32
- net_pkt_append_all
前面四个API都是调整了字节序然后调用net_pkt_append_all完成的,因此只用分析net_pkt_append_all,net_pkt_append_all通过net_pkt_append->net_pkt_append_bytes完成数据拷贝1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32static inline u16_t net_pkt_append_bytes(struct net_pkt *pkt,
const u8_t *value,
u16_t len, s32_t timeout)
{
struct net_buf *frag = net_buf_frag_last(pkt->frags);
u16_t added_len = 0;
do {
u16_t count = min(len, net_buf_tailroom(frag));
void *data = net_buf_add(frag, count); //获取net_buf所属data buf空闲位置的指针net_buf->data+net_buf->len,然后将net_buf->len增加
memcpy(data, value, count); //拷贝数据到data buf内
len -= count;
added_len += count;
value += count;
if (len == 0) {
return added_len;
}
//如果当前的net_buf放不完append的数据,再开net_buf
frag = net_pkt_get_frag(pkt, timeout);
if (!frag) {
return added_len;
}
net_pkt_frag_add(pkt, frag);
} while (1);
/* Unreachable */
return 0;
}
net_buf free
使用net_pkt_frag_unref->net_buf_unref释放frag
net_buf可能被多次引用当ref大于0时,只对buf->ref–,直到没人ref时(ref==0),使用pool->alloc->cb->unref对buf->__buf进行释放(未做任何事情),再使用net_buf_destroy将buf退回到free LIFO中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32void net_buf_unref(struct net_buf *buf)
{
while (buf) {
struct net_buf *frags = buf->frags;
struct net_buf_pool *pool;
// ref --
if (--buf->ref > 0) {
return;
}
//无其它使用者ref时, 将data buf释放
if (buf->__buf) {
data_unref(buf, buf->__buf);
buf->__buf = NULL;
}
buf->data = NULL;
buf->frags = NULL;
net_buf_destroy(buf); //将net_buf放回pool
buf = frags;
}
}
static inline void net_buf_destroy(struct net_buf *buf)
{
struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id);
k_lifo_put(&pool->free, buf);
}
参考代码
https://github.com/zephyrproject-rtos/zephyr/tree/master/subsys/net/ip