S
。处在这种睡眠状态的进程是可以通过给它发送信号来唤醒的。D
。处在这种睡眠状态的进程无法立即处理任何发送给它的信号,这也是无法用 kill
杀掉它的原因。$ dd if=/dev/zero of=/tmp/disk1 count=4906 bs=1M
$ losetup --show -f /tmp/disk1
/dev/loop0
$ pvcreate /dev/loop0
Physical volume "/dev/loop0" successfully created.
$ vgcreate vgtest1 /dev/loop0
Volume group "vgtest1" successfully created
$ lvcreate -n lvtest1 -L 1G vgtest1
Logical volume "lvtest1" created.
$ mkfs.ext4 -m0 /dev/vgtest1/lvtest1
$ mount /dev/vgtest1/lvtest1 /mnt/lvtest1
// 写 IO 的同时暂停 lv
$ dmsetup suspend /dev/vgtest1/lvtest1 && dd if=/dev/urandom of=/mnt/lvtest1/file.img bs=1M count=1024
// 恢复 lv 的 IO。
$ dmsetup resume /dev/vgtest1/lvtest1
$ mount /dev/vgtest1/lvtest1 /mnt/lvtest1
$ dmsetup suspend /dev/vgtest1/lvtest1 && dd if=/dev/urandom of=/mnt/lvtest1/file.img bs=1M count=1024
$ echo w > /proc/sysrq-trigger
$ ps -eo ppid,pid,user,stat,pcpu,comm,wchan:32 |grep D
PPID PID USER STAT %CPU COMMAND WCHAN
2099 2178 root D+ 0.0 dd -
2210 2231 root D+ 0.0 ls -
2264 2304 root D+ 0.0 mount -
2413 2435 root D+ 0.0 mount - -
D状态的dd
导致某些设备处于执行IO的不可中断的模式
。mount进程读取超级块先关资源时候hang主了.在了解这个问题之前需要了解下外设磁盘和系统之间的通信方式。目前系统判断设备上的数据是否就绪采用了轮询
和中断
两种方式。轮询
方式是不断的重复询问设备上的数据是否可用,如果可用,CPU就读取数据;中断
方式中系统为每个CPU提供了中断线,可由各个系统设备共享。每个中断通过一个唯一的标识,内核对使用的每个中断提供一个中断服务。中断将暂停正常系统工作,在外设的数据已经就绪,需要由内核或者应用处理,外设会引发一个中断,系统就不需要频繁检查是否有新的数据可用,外设有新数据的情况会自动通知系统。既然IO通过方式,D状态
的mount 进程设备处于做IO操作,无法被打断的状态,新的进程再次mount务必要进行一些IO操作,也必然产生中断,但是这个中断无法被响应,就一直处于D+
状态。// mount进程的stack
$ cat /proc/2304/stack
[<0>] generic_file_buffered_read+0x35b/0xbc0
[<0>] new_sync_read+0x10f/0x150
[<0>] vfs_read+0x91/0x140
[<0>] ksys_read+0x4f/0xb0
[<0>] do_syscall_64+0x5b/0x1a0
[<0>] entry_SYSCALL_64_after_hwframe+0x65/0xca
// 内核的日志,这时候可以看到寄存器的某些值是无法被访问,同时内核hang在了io调度这块
kernel: task:mount state:D stack: 0 pid: 2304 ppid: 2264 flags:0x00000080
kernel: Call Trace:
kernel: __schedule+0x2c4/0x700
kernel: schedule+0x37/0xa0
kernel: io_schedule+0x12/0x40
kernel: generic_file_buffered_read+0x35b/0xbc0
kernel: ? file_fdatawait_range+0x20/0x20
kernel: new_sync_read+0x10f/0x150
kernel: vfs_read+0x91/0x140
kernel: ksys_read+0x4f/0xb0
kernel: do_syscall_64+0x5b/0x1a0
kernel: entry_SYSCALL_64_after_hwframe+0x65/0xca
kernel: RIP: 0033:0x7f30b6ffd5a5
kernel: Code: Unable to access opcode bytes at RIP 0x7f30b6ffd57b.
kernel: RSP: 002b:00007ffee1830e68 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
kernel: RAX: ffffffffffffffda RBX: 000056069ab0d640 RCX: 00007f30b6ffd5a5
kernel: RDX: 0000000000000040 RSI: 000056069ab0d668 RDI: 0000000000000003
kernel: RBP: 000056069ab0d4e0 R08: 000056069ab0d640 R09: 000056069ab0d630
kernel: R10: 0000000000000000 R11: 0000000000000246 R12: 000000003fff0000
kernel: R13: 0000000000000040 R14: 000056069ab0d530 R15: 000056069ab0d658