Linux kernel 等待事件和唤醒事件之间的竞争条件
我有一个驱动程序,希望向用户发送状态更改通知。在当前的实现中,它使用proc文件系统来实现这一点。读取过程围绕一个Linux kernel 等待事件和唤醒事件之间的竞争条件,linux-kernel,ipc,linux-device-driver,race-condition,Linux Kernel,Ipc,Linux Device Driver,Race Condition,我有一个驱动程序,希望向用户发送状态更改通知。在当前的实现中,它使用proc文件系统来实现这一点。读取过程围绕一个read()循环到proc文件系统。read()用wait\u event\u interruptible()阻塞,直到内核得到一个中断,该中断导致write\u new\u data()函数调用wake\u interruptible()。以下是基本代码(删除了所有不必要的混乱): 现在考虑以下流程: 用户进程调用read(),然后等待 中断发生->写入新数据()被调用。写入数据并
read()
循环到proc文件系统。read()
用wait\u event\u interruptible()
阻塞,直到内核得到一个中断,该中断导致write\u new\u data()
函数调用wake\u interruptible()。以下是基本代码(删除了所有不必要的混乱):
现在考虑以下流程:
read()
,然后等待写入新数据()
被调用。写入数据并调用唤醒可中断()
read()
已唤醒,读取数据,但进程尚未重新运行读取(未计划
要运行,由于下一次中断,无法运行write\u new\u data()
再次触发,调用wake\u-up\u-interruptable()
但没有等待的线程正在等待如何避免错过第二个中断?(一种解决方案是使用netlink套接字,但我想知道是否有一种方法可以在/proc land中实现)由于中断可能发生在调用
wait\u event\u interruptable
和flag=0
之间,因此会以不必要的方式影响flag
变量
请注意,即使在UP机器上,内核也可能会根据配置而抢占,因此代码会受到影响
此外,我建议不要使用简单的“int”标志。相反,您应该使用atomic\u t
和atomic\u dec/inc.*
操作。请参阅内核中完成的实现,它的功能与您在这里所做的类似
关于问题本身:
如果您查看“等待事件可中断”的代码,您会发现如果条件为真,则不会发生睡眠-因此您的问题不是问题。Dan,以下是“等待事件可中断”的代码:
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
如果一个中断恰好发生在“If(!(condition))”和“\uuu wait\u event\u interruptable”之间,则会发生休眠,读取过程将被阻塞,直到另一个中断发生。好吧,为了简单起见,我取出了带有自旋锁的代码部分,该自旋锁控制标志的更改。感谢您提供有关atomic_dec/inc和补全的提示-我将阅读更多关于这些的内容。我的基本问题仍然没有得到回答…你所呈现的流程有一些不太正确的地方。在第二次中断之后,flag==1,下一次调用
read
,即使它被中断延迟,似乎也无法阻止,因为wait\u Event\u interruptible
会因为提供的条件而立即退出。这里我们不同意。我的理解是,如果叫作wake时,等待队列上没有线程(不叫作wait_事件),那么条件为true这一事实将无济于事,进程将一直休眠,直到有人再次叫作wake_(只有到那时才会检查条件是否为true)。不,这不是工作方式。只需查看\uu wait\u event\u interruptable
宏的定义。如果在调用schedule()之前满足了条件,它就会返回,因此如果条件已经满足,它就不会休眠。从代码上看,看起来你是非常正确的,我是非常错误的…我将用一个简单的模块对其进行测试并报告back否,查看uu wait_event_interrupts()-如果(条件)在休眠之前中断,它会执行;我的意思是看uuu wait u event u interruptable()。它再次检查情况。为了进一步说明,即使中断发生在第二次检查之后,对schedule()的调用也会立即返回,因为在中断中调用wake_。在再次检查条件之前,\uuuu wait\u event\u可中断调用prepare\u to\u wait()的原因是为了防止条件更新时出现争用。因此,API是有效的,但问题中的标志的处理仍然是一个bug。我发现:\uuu wait\u event\u interruptible():prepare\u to\u wait(&wq,&\uu wait,TASK\u interruptible);如果(条件)中断;如果(!signal_pending(current))ret=计划超时(ret)代码>如果条件和唤醒()发生在调用prepare_to_wait()之前,并且如果此代码在调用prepare_to_wait()之后被抢占,在这种情况下,调度程序将不会再次运行它,因为它处于任务可中断状态。它是什么类型的驱动程序?它是什么样的状态?
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})