【发布时间】:2020-06-10 16:52:31
【问题描述】:
我有两个线程,每个线程同时从同一设备捕获数据包,但是当第二个线程到达pcap_compile() 函数时程序崩溃。此外,每个线程都有自己的变量,不使用全局变量。他们似乎获得了相同的设备句柄,因此程序崩溃了。为什么我需要两个线程?因为我想通过指定的pcap 过滤器将发送和接收的数据包分开。那么我该如何解决呢?还是使用一个线程并使用tcp标头中的地址手动对发送和接收的数据包进行排序更好?
【问题讨论】:
我有两个线程,每个线程同时从同一设备捕获数据包,但是当第二个线程到达pcap_compile() 函数时程序崩溃。此外,每个线程都有自己的变量,不使用全局变量。他们似乎获得了相同的设备句柄,因此程序崩溃了。为什么我需要两个线程?因为我想通过指定的pcap 过滤器将发送和接收的数据包分开。那么我该如何解决呢?还是使用一个线程并使用tcp标头中的地址手动对发送和接收的数据包进行排序更好?
【问题讨论】:
pcap_compile 不是线程安全的。您必须用临界区/互斥锁包围单独线程可能遇到的所有调用,以防止由于编译表达式的解析器中的非线程安全状态而导致错误(对于血淋淋的细节,它使用 YACC 为解析表达式和为此生成的代码明显不是线程安全的)。
您需要为您计划用于捕获的每个线程显式打开一次设备,如果您在多个线程中重复使用相同的设备句柄,那么它根本不会满足您的要求。您应该在计划使用它的线程中打开 pcap 句柄,因此计划进行捕获的每个线程都应该使用自己的pcap_open。
要使用临界区保护对pcap_compile 的调用,您可以创建一个简单的包装器(Windows 临界区的 C++ 包装器):
class lock_interface {
public:
virtual void lock() = 0;
virtual void unlock() = 0;
};
class cs : public lock_interface {
CRITICAL_SECTION crit;
public:
cs() { InitializeCriticalSection(&crit); }
~cs() { DeleteCriticalSection(&crit); }
virtual void lock() {
EnterCriticalSection(&crit);
}
virtual void unlock() {
LeaveCriticalSection(&crit);
}
private:
cs(const locker &);
cs &operator=(const cs &);
};
class locker {
lock_interface &m_ref;
public:
locker(lock_interface &ref) : m_ref(ref) { m_ref.lock(); }
~locker() { m_ref.unlock(); }
private:
locker(const locker &);
locker &operator=(const locker &);
};
static cs section;
int
wrapped_pcap_compile(pcap_t *p, struct bpf_program *fp, const char *str, int optimize, bpf_u_int32 netmask)
{
locker locked(section);
pcap_compile(p, fp, str, optimize, netmask);
}
【讨论】:
如果你使用的是 C++11,你可以有类似的东西:
int thread_safe_pcap_compile_nopcap(int snap_len, int link_type,
struct bpf_program *fp, char const *str,
int optimize, bpf_u_int32 netmask) {
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
return pcap_compile_nopcap(snap_len, link_type, fp, str, optimize, netmask);
}
pcap_compile 函数类似。
【讨论】: