赞
踩
处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。
通过ps命令我们会看到,一般情况下,进程列表中的绝大多数进程都处于TASK_INTERRUPTIBLE状态(除非机器的负载很高)。毕竟CPU就这么一两个,进程动辄几十上百个,如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来。
与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。
绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。否则你将惊奇的发现,kill -9竟然杀不死一个正在睡眠的进程了!于是我们也很好理解,为什么ps命令看到的进程几乎不会出现TASK_UNINTERRUPTIBLE状态,而总是TASK_INTERRUPTIBLE状态。
而TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。
在进程对某些硬件进行操作时(比如进程调用read系统调用对某个设备文件进行读操作,而read系统调用最终执行到对应设备驱动的代码,并与对应的物理设备进行交互),可能需要使用TASK_UNINTERRUPTIBLE状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。这种情况下的TASK_UNINTERRUPTIBLE状态总是非常短暂的,通过ps命令基本上不可能捕捉到。
不可中断状态的进程则是正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的I/O响应,也就是我们在ps命令中看到的D状态(Uninterruptible Sleep,也称为Disk Sleep)的进程。
比如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题。
所以,不可中断状态实际上是系统对进程和硬件设备的一种保护机制。
linux系统中也存在容易捕捉的TASK_UNINTERRUPTIBLE状态。执行vfork系统调用后,父进程将进TASK_UNINTERRUPTIBLE状态,直到子进程调用exit或exec 通过下面的代码就能得到处于TASK_UNINTERRUPTIBLE状态的进程:
- [root@localhost ~]# cat test.c
- void main() {
- if (!vfork()) sleep(100);
- }
-
- [root@localhost ~]# gcc -o test test.c
-
- [root@localhost ~]# ./test
-
- [root@localhost ~]# ps -aux | grep test
- root 19454 0.0 0.0 4212 352 pts/6 D+ 10:02 0:00 ./test
- root 19455 0.0 0.0 4212 352 pts/6 S+ 10:02 0:00 ./test
处于uninterruptiblesleep状态的进程通常是在等待IO,比如磁盘IO,网络IO,其他外设IO,如果进程正在等待的IO在较长的时间内都没有响应,那么就很会不幸地被ps看到了,同时也就意味着很有可能有IO出了问题,可能是外设本身出了故障,也可能是比如挂载的远程文件系统已经不可访问了(由down掉的NFS服务器引起的D状态)。
正是因为得不到IO的相应,进程才进入了uninterruptible sleep状态,所以要想使进程从uninterruptible sleep状态恢复,就得使进程等待的IO恢复,比如如果是因为从远程挂载的NFS卷不可访问导致进程进入uninterruptible sleep状态的,那么可以通过恢复该NFS卷的连接来使进程的IO请求得到满足。
在内核源码 fs/proc/array.c 里,其文字定义为“ "D (disk sleep)", /* 2 */ ”(由此可知 D 原是Disk的打头字母),对应着 include/linux/sched.h 里的“ #define TASK_UNINTERRUPTIBLE 2 ”。举个例子,当 NFS 服务端关闭之时,若未事先 umount 相关目录,在 NFS 客户端执行 df 就会挂住整个登录会话,按 Ctrl+C 、Ctrl+Z 都无济于事。断开连接再登录,执行 ps axf 则看到刚才的 df 进程状态位已变成了 D ,kill -9 无法杀灭。正确的处理方式,是马上恢复 NFS 服务端,再度提供服务,刚才挂起的 df 进程发现了其苦苦等待的资源,便完成任务,自动消亡。若 NFS 服务端无法恢复服务,在 reboot 之前也应将 /etc/mtab 里的相关 NFS mount 项删除,以免 reboot 过程例行调用 netfs stop 时再次发生等待资源,导致系统重启过程挂起。
第二客户端机器我们将运行另一个副本的wrk,但是这个脚本我们使用50的并发连接来请求相同的文件。因为这个文件被经常访问的,它将保持在内存中。在正常情况下,NGINX很快的处理这些请求,但是工作线程如果被其他的请求阻塞性能将会下降。所以我们暂且叫它“加载恒定负载”。
性能将由服务器上ifstat监测的吞吐率(throughput)和从第二台客户端获取的wrk结果来度量。
现在,没有线程池的第一次运行不会给我们带来非常令人兴奋的结果:
- % ifstat -bi eth2
- eth2
- Kbps in Kbps out
- 5531.24 1.03e+06
- 4855.23 812922.7
- 5994.66 1.07e+06
- 5476.27 981529.3
- 6353.62 1.12e+06
- 5166.17 892770.3
- 5522.81 978540.8
- 6208.10 985466.7
- 6370.79 1.12e+06
- 6123.33 1.07e+06
如你所见,上述的配置可以产生一共1G的流量,从top命令上我们可以看到所有的工作线程在阻塞io上花费了大量的时间(下图D状态):
- top - 10:40:47 up 11 days, 1:32, 1 user, load average: 49.61, 45.77 62.89
- Tasks: 375 total, 2 running, 373 sleeping, 0 stopped, 0 zombie
- %Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 67.7 id, 31.9 wa, 0.0 hi, 0.0 si, 0.0 st
- KiB Mem: 49453440 total, 49149308 used, 304132 free, 98780 buffers
- KiB Swap: 10474236 total, 20124 used, 10454112 free, 46903412 cached Mem
-
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
- 4639 vbart 20 0 47180 28152 496 D 0.7 0.1 0:00.17 nginx
- 4632 vbart 20 0 47180 28196 536 D 0.3 0.1 0:00.11 nginx
- 4633 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.11 nginx
- 4635 vbart 20 0 47180 28136 480 D 0.3 0.1 0:00.12 nginx
- 4636 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.14 nginx
- 4637 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.10 nginx
- 4638 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.12 nginx
- 4640 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.13 nginx
- 4641 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.13 nginx
- 4642 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.11 nginx
- 4643 vbart 20 0 47180 28276 536 D 0.3 0.1 0:00.29 nginx
- 4644 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.11 nginx
- 4645 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.17 nginx
- 4646 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.12 nginx
- 4647 vbart 20 0 47180 28208 532 D 0.3 0.1 0:00.17 nginx
- 4631 vbart 20 0 47180 756 252 S 0.0 0.1 0:00.00 nginx
- 4634 vbart 20 0 47180 28208 536 D 0.0 0.1 0:00.11 nginx<
- 4648 vbart 20 0 25232 1956 1160 R 0.0 0.0 0:00.08 top
- 25921 vbart 20 0 121956 2232 1056 S 0.0 0.0 0:01.97 sshd
- 25923 vbart 20 0 40304 4160 2208 S 0.0 0.0 0:00.53 zsh
在这种情况下,吞吐率受限于磁盘子系统,而CPU在大部分时间里是空转状态的。从wrk获得的结果来看也非常低:
- Running 1m test @ http://192.0.2.1:8000/1/1/1
- 12 threads and 50 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 7.42s 5.31s 24.41s 74.73%
- Req/Sec 0.15 0.36 1.00 84.62%
- 488 requests in 1.01m, 2.01GB read
- Requests/sec: 8.08
- Transfer/sec: 34.07MB
请记住,文件是从内存送达的!第一个客户端的200个连接创建的随机负载,使服务器端的全部的工作进程忙于从磁盘读取文件,因此产生了过大的延迟,并且无法在合适的时间内处理我们的请求。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。