美文网首页
linux唤醒丢失问题(Lost Wake-Up Problem

linux唤醒丢失问题(Lost Wake-Up Problem

作者: 浙南旧事 | 来源:发表于2021-10-30 17:44 被阅读0次

linux中的唤醒丢失问题,是同步机制中的一个经典问题。
在下面的文章中:
https://www.linuxjournal.com/article/8144
第一个问题比较好理解:

Process A:
1  spin_lock(&list_lock);
2  if(list_empty(&list_head)) {
3      spin_unlock(&list_lock);
4      set_current_state(TASK_INTERRUPTIBLE);
5      schedule();
6      spin_lock(&list_lock);
7  }
8
9  /* Rest of the code ... */
10 spin_unlock(&list_lock);

Process B:
100  spin_lock(&list_lock);
101  list_add_tail(&list_head, new_node);
102  spin_unlock(&list_lock);
103  wake_up_process(processa_task);

如果先执行完进程A的第3行,然后再执行进程B的100~103行,接着再执行进程A的第4行及之后的代码。这样进程A将进入睡眠状态,进程B前面的那次唤醒操作就丢失了。

第二个问题就没有这么好理解:

4253  /* Wait for kthread_stop */
4254  set_current_state(TASK_INTERRUPTIBLE);
4255  while (!kthread_should_stop()) {
4256          schedule();
4257          set_current_state(TASK_INTERRUPTIBLE);
4258  }
4259  __set_current_state(TASK_RUNNING);
4260 return 0;

这份代码是linux-2.6.11/kernel/sched.c,migration_thread线程里的。其他地方会置上停止标志并唤醒它,让其退出。
直接这么看代码,没有看出问题在哪里,跟唤醒丢失有什么关系。

先改写一下代码,去掉set_current_state的部分,代码变成:

4253  /* Wait for kthread_stop */
4254  while (!kthread_should_stop()) {
4255          schedule();
4256  }
4257 return 0;

乍一看,也没有问题,其他地方置上停止标志,唤醒它,等它被调度到了,它就退出了。
问题是,本意是让这个线程大部分时间睡眠,等其他地方置上停止标志,唤醒它了,它才醒来并退出。但上面这份代码,就会频繁的(只要调度到了)测试停止标志,与本意不符。

为了这个线程大部分时间都睡眠,就改一下代码:

4253  /* Wait for kthread_stop */
4254  while (!kthread_should_stop()) {
4255          set_current_state(TASK_INTERRUPTIBLE);
4256          schedule();
4257  }
4258 return 0;

这时候就看出问题了,如果这个线程执行完4254行,在执行4255行之前,其他地方置上停止标志,并尝试唤醒此线程。由于此线程虽然被调度出去了,但还是TASK_RUNNING状态,因此唤醒操作无效。然后此线程被调度进来,并把状态设置成TASK_INTERRUPTIBLE,然后再触发调度进入睡眠。至此,前面那个唤醒操作就丢失了。

如果只在一处将线程状态设置成TASK_INTERRUPTIBLE,都是有隐患的(先不考虑最后设成TASK_RUNNING的部分)。
比如,在最开始处设置:

4253  /* Wait for kthread_stop */
4254  set_current_state(TASK_INTERRUPTIBLE);
4255  while (!kthread_should_stop()) {
4256          schedule();
4257  }
4258 return 0;

如果此线程睡眠之后,被信号唤醒了,它的状态就变成TASK_RUNNING,此后就会频繁的测试停止标志了。
如果在schedule之后设置:

4253  /* Wait for kthread_stop */
4254  while (!kthread_should_stop()) {
4255          schedule();
4256          set_current_state(TASK_INTERRUPTIBLE);
4257  }
4258 return 0;

这个问题好像也不大?只是跟最开始正确的代码相比,正确的代码第一次进入schedule就睡眠了,而这里的代码,第一次进入schedule时还是TASK_RUNNING,后面还会被正常调度到,第二次进入schedule时才开始睡眠。
另外,正确的代码语义比较完整,每次测试停止标志前都设置了TASK_INTERRUPTIBLE,而这里的代码的语义相对就不完整,第一次没有设置TASK_INTERRUPTIBLE,后面才开始设置TASK_INTERRUPTIBLE。

相关文章

网友评论

      本文标题:linux唤醒丢失问题(Lost Wake-Up Problem

      本文链接:https://www.haomeiwen.com/subject/sjigaltx.html