我编写了内核模块,它执行nf_register_hook,并使用字符设备机制,通过设备读取钩子将抓取的数据包获取到用户空间。我使用全局缓冲区和缓冲区大小变量,这就是为什么我需要在新的数据包到来或用户读取我的设备时锁定它。我使用了splinlock_irqsave和spin_unlock_irqrestore(&locker,flags),但是我的模块死锁了,系统死机了。
unsigned int main_hook(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int(*okfn)(struct sk_buff*)) {
unsigned long flags;
spin_lock_irqsave(&locker,flags);
...
spin_unlock_irqrestore(&locker,flags);
}
ssize_t sniffer_dev_read(struct file *filep, char *buff, size_t count, loff_t *offp) {
spin_lock_irqsave(&locker,flags);
...
spin_unlock_irqrestore(&locker,flags);
}
main_hook is registered in nf_register_hook()
sniffer_dev_read is registered in register_chrdev当用户从设备读取数据时,系统进入死锁状态。想法?或者可能irq保存/恢复与netfiler钩子/字符设备读取不兼容,我必须在这里使用特殊锁定?
发布于 2011-08-24 20:45:11
首先,您应该在启用锁调试选项的情况下重新编译内核,然后重试。他们可以帮助指出原因。
在spin_lock_irqsave中有几种可能的死锁原因。它可能是递归锁定(也就是说,您试图在保持旋转锁锁定的代码段中再次调用spin_lock_* )。这可能是你在锁定旋转锁的情况下睡眠(永远不要这样做-对于你调用的每个函数,你必须知道它是否可以睡眠)。这可能是AB/BA死锁(代码的一部分先锁定A,然后锁定B;另一部分先锁定B,然后锁定A;如果第一部分锁定了A,但没有锁定B,第二部分锁定了B,但没有锁定A,则您有一个死锁)。诸若此类。锁定调试选项可以检测并警告您其中的许多问题。
由于您要锁定的是“全局缓冲区和缓冲区大小变量”,请尝试将锁定区域减少到最小。而不是在函数的顶部锁定,在结束时解锁,尽可能多地在锁之外进行操作,并且仅在操作缓冲区时进行锁定。理想情况下,被锁定的部分应该只是几条没有函数调用的指令。在这种情况下,死锁要难得多。
现在我说了所有这些,我的心理调试(即猜测问题在哪里)的尝试:您正在调用copy_to_user (它可以睡眠),同时持有旋转锁。
发布于 2011-08-24 17:54:53
您不应该使用自旋锁来锁定可以从不同上下文级别使用的资源。它忙-阻塞它被锁定的处理器。
main_hook是从中断/下半上下文调用的吗?如果是这样的话,你可以使用work_queues来拥有"job“(memcpy...)以较低的优先级完成。作为一般的规则,你应该尽可能的在一个自旋锁内。
发布于 2011-08-24 20:18:47
我的猜测是,你可能有一些简单的编程故障(例如,尝试遵守NULL等),但由于spin_lock_irqsave禁用中断,当您进入异常模式时,您将禁用中断,因此整个机器都被锁定。
请注意,由于您的NF钩子在bottom half上下文中运行,因此您实际上不需要禁用中断--只需禁用bottom half。这将使调试变得更容易。
https://stackoverflow.com/questions/7173337
复制相似问题