我正在编写相当简单的pcap“实时”捕获引擎,但pcap_dispatch的数据包处理回调实现应该花费相对长的时间进行处理。 pcap是否在单独的线程中运行每个“pcap_handler”回调?如果是,“pcap_handler”是否是线程安全的,还是应该注意保护关键部分? 或者,pcap_dispatch回调是否以串行方式工作?例如。数据包2的“pcap_handler”只有在数据包1的“pcap_handler”完成后才被调用?如果是这样,是否有办法避免累积延迟? 谢谢, -V
答案 0 :(得分:18)
Pcap基本上是这样的:有一个内核模式驱动程序捕获数据包并将它们放在大小为 B 的缓冲区中。用户模式应用程序可以随时使用pcap_loop
,pcap_dispatch
或pcap_next
请求任意数量的数据包(后者基本上是pcap_dispatch
一个数据包)。
因此,当您使用pcap_dispatch
请求某些数据包时, libpcap 会进入内核并请求缓冲区中的下一个数据包(如果没有超时代码,则什么东西开始,但这与此讨论无关),将其转移到用户空间并从缓冲区中删除它。在此之后,pcap_dispatch
会调用您的处理程序,减少它的数据包待办事项计数器,并从头开始。因此,pcap_dispatch
仅在已处理请求的数据包数量,发生错误或发生超时时才返回。
正如您所看到的, libpcap 完全是非线程的,就像大多数C API一样。但是,内核模式驱动程序显然很乐意将数据包传递到多个线程(否则您将无法从多个进程捕获),并且完全是线程安全的(每个用户模式都有一个单独的缓冲区)处理)。
这意味着您必须自己实现所有并行性。你想做这样的事情:
pcap_dispatch(P, count, handler, data);
.
.
.
struct pcap_work_item {
struct pcap_pkthdr header;
u_char data[];
};
void handler(u_char *user, struct pcap_pkthdr *header, u_char *data)
{
struct pcap_work_item *item = malloc(sizeof(pcap_pkthdr) + header->caplen);
item->header = *header;
memcpy(item->data, data, header->caplen);
queue_work_item(item);
}
请注意,我们必须将数据包复制到堆中,因为在回调返回后header
和data
指针无效。
函数queue_work_item
应找到一个工作线程,并为其分配处理数据包的任务。既然你说你的回调需要'相对较长时间',你可能需要大量的工作线程。找到合适数量的工人需要进行微调。
在这篇文章的开头我说内核模式驱动程序有缓冲区来收集等待处理的传入数据包。此缓冲区的大小是实现定义的。 snaplen
pcap_open_live
参数仅控制捕获一个数据包的字节数,但是,无法以便携方式控制数据包的数量。它可能是固定大小的。随着越来越多的数据包到达,它可能会变大。但是,如果溢出,所有其他数据包将被丢弃,直到有足够的空间让下一个数据包到达。如果要在高流量环境中使用应用程序,则需要确保* pcap_dispatch *回调快速完成。我的示例回调只是将数据包分配给一个工作人员,所以即使在高流量环境下也能正常工作。
我希望这能解答你所有的问题。