六、使用休眠的注意事项 (1) 永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或者 RCU 锁时不能睡眠;关闭中断也不能睡眠,终端例程中也不可休眠。 持有一个信号量时休眠是合法的,如果代码在持有一个信号量时睡眠,任何其他的等待这个信号量的线程也会休眠。发生在持有信号量时的休眠必须短暂,而且决不能阻塞那个将最终唤醒你的进程。 (2)当进程被唤醒,它并不知道休眠了多长时间以及休眠时发生什么;也不知道是否另有进程也在休眠等待同一事件,且那个进程可能在它之前醒来并获取了所等待的资源。所以不能对唤醒后的系统状态做任何的假设,并必须重新检查等待条件来确保正确的响应。
(3)除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠。使进程可被找到意味着:需要维护一个等待队列的数据结构。它是一个进程链表,其中包含了等待某个特定事件的所有进程的相关信息。
七、不可在中断例程中休眠的原因 如果在某个系统调用中把当前进程休眠,是有明确目标的,这个目标就是过来call这个系统调用的进程(注意这个进程正在running)。 但是中断和进程是异步的,在中断上下文中,当前进程大部分时候和中断代码可能一点关系都没有。要是在这里调用了休眠代码,把当前进程给休眠了,那就极有可能把无关的进程休眠了。再者,如果中断不断到来,会殃及许多无辜的进程。 在中断中休眠某个特定进程是可以实现的,通过内核的task_struct链表可以找到的,不论是根据PID还是name。但是只要这个进程不是当前进程,休眠它也可能没有必要。可能这个进程本来就在休眠;或者正在执行队列中但是还没执行到,如果执行到他了可能又无须休眠了。 还有一个原因是中断也是所谓的原子上下文,有的中断例程中会禁止所有中断,有的中断例程还会使用自旋锁等机制,在其中使用休眠也是非常危险的。 下面会介绍。 八、不可在持有自旋锁、seqlock、RCU 锁或关闭中断时休眠的原因 其实自旋锁、seqlock、RCU 锁或关闭中断期间的代码都称为原子上下文,比较有代表性的就是自旋锁spinlock。 对于UP系统,如果A进程在拥有spinlock时休眠,这个进程在拥有自旋锁后主动放弃了处理器。其他的进程就开始使用处理器,只要有一个进程B去获取同一个自旋锁,B必然无法获取,并做所谓的自旋等待。由于自旋锁禁止所有中断和抢占,B的自旋等待是不会被打断的,并且B也永远获得不了锁。因为B在CPU中运行,没有其他进程可以运行并唤醒A并释放锁。系统就此锁死,只能复位了。 对于SMP系统,如果A进程在拥有spinlock时休眠,这个进程在拥有自旋锁后主动放弃了处理器。如果所有处理器都为了获取这个锁而自旋等待,由于自旋锁禁止所有中断和抢占,,就不会有进程可能去唤醒A了,系统也就锁死了。 并不是所一旦系统获得自旋锁休眠就会死,而是有这个可能。但是注意了计算机的运行速度之快,只要有亿分之一的可能,也是很容易发生。 所有的原子上下文都有这样的共性:不可在其中休眠,否则系统极有可能锁死。 如果你对此还有怀疑,眼见为实。我编写了一个故意锁死系统的及其简单的驱动:
只要对其设备节点做两次读写操作,系统必死。我在X86 的SMP系统,ARMv5、ARMv6、ARMv7中都做了如下的实验(单核(UP)系统必须配置CONFIG_DEBUG_SPINLOCK,否则自旋锁是没有实际效果(起码不会有“自旋”), 系统可以多次获取自旋锁,没有实验效果。之后博文中有详细描述)。现象都和上面叙述的死法相同,看了源码就知道(关键在read\write方法)。以下是实验记录:- # insmod spin_lock_sleep.ko
- spin_lock sleep module loaded!
- # cat /proc/devices
- Character devices:
- 1 mem
- 4 /dev/vc/0
- 4 tty
- 4 ttyS
- 5 /dev/tty
- 5 /dev/console
- 5 /dev/ptmx
- 7 vcs
- 10 misc
- 13 input
- 14 sound
- 21 sg
- 29 fb
- 81 video4linux
- 89 i2c
- 90 mtd
- 116 alsa
- 128 ptm
- 136 pts
- 252 spin_lock_sleep
- 253 ttyO
- 254 rtc
- Block devices:
- 1 ramdisk
- 259 blkext
- 7 loop
- 8 sd
- 11 sr
- 31 mtdblock
- 65 sd
- 66 sd
- 67 sd
- 68 sd
- 69 sd
- 70 sd
- 71 sd
- 128 sd
- 129 sd
- 130 sd
- 131 sd
- 132 sd
- 133 sd
- 134 sd
- 135 sd
- 179 mmc
- # mknod spin_lock_sleep c 252 0
- # cat spin_lock_sleep
- spin_lock_sleep_read:prepare to get spin_lock! PID:1227
- spin_lock_sleep_read:have got the spin_lock! PID:1227
- spin_lock_sleep_read:prepare to sleep! PID:1227
- spin_lock_sleep_write:prepare to get spin_lock! PID:1229
- BUG: spinlock cpu recursion on CPU#0, sh/1229
- lock: dd511c3c, .magic: dead4ead, .owner: cat/1227, .owner_cpu: 0
- Backtrace:
- [<c005e9fc>] (dump_backtrace+0x0/0x118) from [<c04335e4>] (dump_stack+0x20/0x24)
- r7:00000002 r6:dd511c3c r5:dd511c3c r4:dd7ef000
- [<c04335c4>] (dump_stack+0x0/0x24) from [<c02178d0>] (spin_bug+0x94/0xa8)
- [<c021783c>] (spin_bug+0x0/0xa8) from [<c0217a58>] (do_raw_spin_lock+0x6c/0x160)
- r5:bf04c408 r4:dd75e000
- [<c02179ec>] (do_raw_spin_lock+0x0/0x160) from [<c0435ec4>] (_raw_spin_lock+0x18/0x1c)
- [<c0435eac>] (_raw_spin_lock+0x0/0x1c) from [<bf04c1a4>] (spin_lock_sleep_write+0xb4/0x190 [spin_lock_sleep])
- [<bf04c0f0>] (spin_lock_sleep_write+0x0/0x190 [spin_lock_sleep]) from [<c011ee90>] (vfs_write+0xb8/0xe0)
- r6:dd75ff70 r5:400d7000 r4:dd43bf00
- [<c011edd8>] (vfs_write+0x0/0xe0) from [<c011ef8c>] (sys_write+0x4c/0x78)
- r7:00000002 r6:dd43bf00 r5:00000000 r4:00000000
- [<c011ef40>] (sys_write+0x0/0x78) from [<c005a380>] (ret_fast_syscall+0x0/0x48)
- r8:c005a5a8 r7:00000004 r6:403295e8 r5:400d7000 r4:00000002
- 此时在另一个终端(ssh、telnet等)中执行命令:
- echo 'l' > spin_lock_sleep
- BUG: spinlock lockup on CPU#0, sh/1229, dd511c3c
- Backtrace:
- [<c005e9fc>] (dump_backtrace+0x0/0x118) from [<c04335e4>] (dump_stack+0x20/0x24)
- r7:dd75e000 r6:dd511c3c r5:00000000 r4:00000000
- [<c04335c4>] (dump_stack+0x0/0x24) from [<c0217b0c>] (do_raw_spin_lock+0x120/0x160)
- [<c02179ec>] (do_raw_spin_lock+0x0/0x160) from [<c0435ec4>] (_raw_spin_lock+0x18/0x1c)
- [<c0435eac>] (_raw_spin_lock+0x0/0x1c) from [<bf04c1a4>] (spin_lock_sleep_write+0xb4/0x190 [spin_lock_sleep])
- [<bf04c0f0>] (spin_lock_sleep_write+0x0/0x190 [spin_lock_sleep]) from [<c011ee90>] (vfs_write+0xb8/0xe0)
- r6:dd75ff70 r5:400d7000 r4:dd43bf00
- [<c011edd8>] (vfs_write+0x0/0xe0) from [<c011ef8c>] (sys_write+0x4c/0x78)
- r7:00000002 r6:dd43bf00 r5:00000000 r4:00000000
- [<c011ef40>] (sys_write+0x0/0x78) from [<c005a380>] (ret_fast_syscall+0x0/0x48)
- r8:c005a5a8 r7:00000004 r6:403295e8 r5:400d7000 r4:00000002
|