用 libnetfilter_queue 获得数据包, libtins 操作数据包,放回 netfilter 时出现问题:
vector<uint8_t> vec = pkt.serialize();
nfq_set_verdict(qh, id, NF_ACCEPT, size, &vec[0]);
上面这样,(据我测试)只有长度 135 字节以上的数据包才能成功放回,而这样:
uint8_t arr[MAX_BUFSIZ];
copy(vec.begin(), vec.end(), arr);
nfq_set_verdict(qh, id, NF_ACCEPT, size, arr);
就完全没问题。
为什么呢?&vec[0] 和 arr 的地址有什么区别么?
按理说 vector 也是一段连续内存,应该一样的啊。求解,多谢!
template <typename T>
class alloc {
public:
typedef T value_type;
typedef T* pointer;
typedef std::size_t size_type;
alloc() throw() {}
~alloc() throw() {}
value_type pool[1<<20];
size_type cur = 0;
pointer allocate(size_type num, const void* = 0)
{
pointer p = &pool[cur];
cur += num;
return p;
}
void deallocate(pointer p, size_type num) {}
};
vector< uint8_t, alloc<uint8_t> > vec(size);
用这个allocator开的vector,没有上述问题。用malloc或者默认的new开的vector则存在问题。
我有点明白了…… 如下,删掉free/delete,用malloc/new就也没问题了
pointer allocate(size_type num, const void* = 0)
{
return (pointer)malloc(num * sizeof(value_type));
}
void deallocate(pointer p, size_type num) {}
不让vector回收掉内存即可。
但回收内存时函数已返回,netlink应当已经送出iovec消息,将vector的这段内存拷贝进内核了,外面回收内存不会影响它了啊……
额……大家散了吧……
原因是vector在scope结束时deallocate内存,然后我的vector定义在一个if里,还没等调用netlink就回收了。 去掉if或定义在外面就好了。
不过还是有一些收获。比如知道了malloc/new会重复利用小块内存:
alloc 29 bytes at 28261216
alloc 29 bytes at 28261216 #复用小块内存,netlink re-inject失败
alloc 233 bytes at 28261456 # re-inject成功
alloc 29 bytes at 28261216
alloc 233 bytes at 282614328 # 新开内存,re-inject成功
所以小包未能成功放回(回收后的内存很快挪作他用了),大包则没问题(每次都是新开的内存)。
@MCVector 说的对,和vector没关系。(*/ω\*) 掩面而逃233
1
mug 2016-05-05 17:23:15 +08:00
vector 的内部不一定是连续的。
|
2
Monad 2016-05-05 17:38:28 +08:00 1
反对楼上 摘自 N3690.pdf
没用过 libnetfilter_queue, 楼主你的问题我猜测是 size 有问题... |
3
araraloren 2016-05-05 17:43:56 +08:00
不要这么用 vector 以及其它模板, C 接口就老老实实使用 C 数据结构。。
|
4
kkhaike 2016-05-05 17:58:22 +08:00
我也觉得是 size 有问题,你把他替换成 vec.size()试试
|
5
aigebiu OP |
7
boydfd 2016-05-05 19:05:00 +08:00
stl 里面小块内存(比如 128 以下)是通过内存池分配的。大于 128 才会通过全局的 malloc 分配。问题应该就出在这,你结合自己的程序考虑一下看
|
8
aigebiu OP @boydfd 谢谢!应该是这个原因 vec.reserve() 一下就好了…… 能否具体讲下小块内存的“内存池”?或者相关资料?多谢
|
9
Monad 2016-05-05 20:04:41 +08:00
@aigebiu 这种东西对上层来说应该是透明的,不应该是这个影响,除非你依赖了某些分割(比如\0 )之类的,
另外把 const unsigned char* 转成 unsigned char* ,如果传入的参数本身就是 const ,那这是未定义行为,也有可能会影响结果。 如果可以上段完整的 gist ,我编译调一调。 |
11
allenx 2016-05-05 20:26:54 +08:00
vector 内部连续,可以当做起始地址为:&vec[0],长度为 sizeof(T)*vec.size()的一段内存。
|
12
aigebiu OP @Monad 截出一个片段 https://gist.github.com/isofew/3aa6c0eb716655fa60e53e46ae298d56 我觉得还是 stl 实现的问题
|
13
neoblackcap 2016-05-05 21:08:30 +08:00
c++03 开始已经要求 vector 对内部存储要连续了。
|
14
neoblackcap 2016-05-05 21:08:46 +08:00
@mug c++03 开始已经要求 vector 对内部存储要连续了。
|
15
kkhaike 2016-05-05 21:27:22 +08:00
看了下源码,我觉得原因应该是这个传入的长度一定要是 4 的倍数
``` #define NFA_ALIGNTO 4 #define NFA_ALIGN(len) (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1)) ``` 再加上 @boydfd 所说的 ,小内存是内存池,越界访问出了问题?“成功放回”指的是什么? 反正 vector 一定是内部连续的,这样使用确实没问题。 |
16
MCVector 2016-05-05 21:28:00 +08:00 via Android
std::vector 表示这个锅我不背
|
17
aigebiu OP @kkhaike 成功将修改后的包放回 netfilter 然后走 routing->forward->postrouting->网卡 出去
现在只是 verdict 传回去了,包的内容没传过去,还是原来的包,走 routing->input->用户进程 这条路了。 4 字节的包一样不行。 |
18
ryd994 2016-05-05 22:28:38 +08:00
uint8_t * arr =malloc(size*8);
copy(vec.begin(), vec.end(), arr); nfq_set_verdict(qh, id, NF_ACCEPT, size, arr); 这样会怎样? |
20
colatin 2016-05-06 00:09:10 +08:00
依稀记得 1byte vector 有优化,内部并不是 uint8_t []方式分配内存的
|
22
colatin 2016-05-06 00:15:45 +08:00
应该是内存池和对齐的问题
|
23
proudzhu 2016-05-06 09:03:51 +08:00 via Android
人家 vector 就不是给你这么用的,出问题了去看标准库实现,猜来猜去没啥用
|
24
zwzmzd 2016-05-06 09:21:38 +08:00
试一下如下的代码呢?
··· vector<uint8_t> v_arr(MAX_BUFSIZ); copy(vec.begin(), vec.end(), v_arr.begin()); nfq_set_verdict(qh, id, NF_ACCEPT, size, &v_arr[0]); ··· 我看了一下你提到的两种实现,第一种使用 vector 的实现,主要数据是放在堆中的;而第二种开辟临时数组的方式,数据是存放在栈上的。相对来讲,数据放在堆上很有可能因为编程者的失误导致部分区域被覆盖,建议检查下之前有没有内存越界的错误。 C++里面我一直是使用 vector+resize()代替 malloc()的,这样有个好处是块临时变量会在块结束时自动释放。用到现在还没发现&vec[0]和 arr[]有任何区别 |
25
linux40 2016-05-06 10:34:08 +08:00 via Android
@zwzmzd &vec[0]和 arr[]没区别是建立在你用的 Alloc 是 std::allocator ,标准没有说 Alloc::pointer 一定是指针(T*)哟,标准也没有说&t 的返回值一定是指针哟。
|
27
aigebiu OP @zwzmzd 这样没问题。但就像我 14 楼提到的,直接在 vec.reserve(MAX_BUFSIZ)也可以修复问题,静态开一个 vector 相当于调 reserve ,仍然是在堆上分配内存吧。而且 new char[] 在堆上也没问题,应该不是堆/栈的原因。
顺便纠正之前的几个错误,一个是 64 位 ub 下是 121 字节临界(之前忘减 14 字节 eth 头了),不过反正这个数的绝对值意义不大,也无所谓。 另一个可能的错误是,之前认为是内存分配的原因,但我自己写了个 allocator ,每次都在全局上 new 内存,问题仍然出现,而且临界大小不变。 我写的 allocator patch : https://gist.github.com/isofew/2ffa93faf6ebe3899de55b87115e1551 @colatin @kkhaike @boydfd @linux40 uint8_t arr[MAX_BUFSIZ] 静态开输出如下: alloc 29 byte(s) at 30174048 &vec[0]: 30174048, arr: 140734511709248 dealloc 29 byte(s) at 30174048 alloc 128 byte(s) at 30174208 &vec[0]: 30174208, arr: 140734511709248 dealloc 128 byte(s) at 30174208 new uint8_t[size] 动态开输出如下: alloc 29 byte(s) at 21465952 &vec[0]: 21465952, arr: 21466000 dealloc 29 byte(s) at 21465952 alloc 128 byte(s) at 21466160 &vec[0]: 21466160, arr: 21466304 dealloc 128 byte(s) at 21466160 不管哪种开法,都是&vec[0]小包不行大包可以, arr 全都可以。 |
28
aigebiu OP 新发现,如果静态分配 vector 的内存,问题就解决了;如果用 malloc 申请内存,问题也解决了。
看来问题要变成 new 和 malloc 的区别了 233 除了调 constructor 外还有什么区别呢?请教楼上诸位了 输出: (static) alloc 29 byte(s) at 140733249528912 &vec[0]: 140733249528912, buffer: 140733250577904 dealloc 29 byte(s) at 140733249528912 malloc 29 byte(s) at 35519328 &vec[0]: 35519328, buffer: 140731255293104 free 29 byte(s) at 35519328 就不传 patch 了,静态的大概是这样: value_type pool[1<<20]; size_type cur = 0; pointer allocate(size_type num, const void* = 0) { pointer p = &pool[cur]; cur += num; std::cerr << "(static) alloc " << num << " byte(s) at " << (size_type)p << std::endl; return p; } 动态( malloc )的大概是这样: pointer allocate(size_type num, const void* = 0) { T* p = (T*)(malloc(num * sizeof(T))); std::cerr << "malloc " << num << " byte(s) at " << (size_type)p << std::endl; return p; } void deallocate(pointer p, size_type num) { std::cerr << "free " << num << " byte(s) at " << (size_type)p << std::endl; free((void*)p); } |
29
3dwelcome 2016-05-06 11:49:22 +08:00 via Android
很负责的告诉楼主、 vector 内存是连续的、不管那个 stl 实现都是如此。
你的问题最大可能性、是预留空间不足。库里都是指针、没办法帮你修正 vector 数组大小。而你程序外部的修正、很可能把库里记录的地址给冲掉了。 |
30
araraloren 2016-05-06 11:56:36 +08:00
@aigebiu `gcc`的`stl`实现中`vector`本身默认的`allocator`就是简单的`new`操作,而对于内置类型`uint_8`并没有什么构造区别
|
31
aigebiu OP @araraloren 是,内置类型没有构造一说,所以我在考虑“调 constructor 外还有什么区别”。 27 、 28 楼里提到,在自己实现的 allocator 中,用 new 就有问题,用 malloc 就成功了,说明他俩分到的内存是有区别的。
|
32
aigebiu OP @3dwelcome 能否详细讲下预留空间不足的问题?为什么同样是小空间,用 malloc 给 vector 开的内存就没问题呢?
|
33
aigebiu OP @aigebiu
@araraloren @3dwelcome 抱歉 刚才仔细看了下 malloc 也有问题,只有静态分配的没问题。(开了个 pool 数组的那个) @zwzmzd 这样看来,可能确实和堆/栈有关。 |
34
araraloren 2016-05-06 14:08:48 +08:00
@aigebiu 那就是了,因为`new`的实现也是简单的调用`malloc`,并没有什么太高级的东西。。
还有我再次想说的是,不要那么使用`vector`,自己实现一个简单的指针资源管理都比直接用`vector`强。。 |
35
aigebiu OP @araraloren 嗯是 要不是因为它提供的接口是 vector 我也不愿意这么折腾 自己 malloc free 最方便 谢谢提醒
|
36
neoblackcap 2016-05-06 14:41:01 +08:00
@aigebiu 返回 vector 这样的容器很好啊。你 vec 变量生命周期完了那么久自己释放资源了,你 malloc 跟 free 还有可能忘记了。
|
37
yuyang1110 2016-05-06 18:23:45 +08:00
@aigebiu 跑个题,其实可以用 vector.data() http://en.cppreference.com/w/cpp/container/vector/data
|
38
linux40 2016-05-07 09:46:41 +08:00 via Android
new 并不是简单的 malloc ,这样认为的,自己回去补充知识吧。
|