Linux kernel 等待事件和唤醒事件之间的竞争条件

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(),然后等待 中断发生->写入新数据()被调用。写入数据并

我有一个驱动程序,希望向用户发送状态更改通知。在当前的实现中,它使用proc文件系统来实现这一点。读取过程围绕一个
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()
    但没有等待的线程正在等待
  • 进程调用read和block
  • 注意:这一切都发生在单处理器系统上。此外,只有一个线程读取和一个线程写入新数据


    如何避免错过第二个中断?(一种解决方案是使用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;                              \
    })