0%

Linux进程出现D状态

问题状态

生产环境使用了开源软件srs服务来调取实时视频,但是调取时提示出现了暂无数据,请稍后再试的错误,一直调取不到视频信息。

问题排查

查看了srs服务、文件服务、通信服务是否正常,云服务器负载情况,内存使用情况,网络情况,最后发现srs服务器进程状态为D,进程无法用 kill 杀掉,导致了生产环境不可用。

ps 的手册里说 D 状态是 uninterruptible sleep,Linux 进程有两种睡眠状态,一种 interruptible sleep,处在这种睡眠状态的进程是可以通过给它发信号来唤醒的,比如发 HUP 信号给 nginx 的 master 进程可以让 nginx 重新加载配置文件而不需要重新启动 nginx 进程;另外一种睡眠状态是 uninterruptible sleep,处在这种状态的进程不接受外来的任何信号,这也是为什么之前我无法用 kill 杀掉这些处于 D 状态的进程,无论是 kill, kill -9 还是 kill -15,因为它们压根儿就不受这些信号的支配。

进程为什么会被置于 uninterruptible sleep 状态呢?处于 uninterruptible sleep 状态的进程通常是在等待 IO,比如磁盘 IO,网络 IO,其他外设 IO,如果进程正在等待的 IO 在较长的时间内都没有响应,那么就很会不幸地被 ps 看到了,同时也就意味着很有可能有 IO 出了问题,可能是外设本身出了故障,也可能是比如挂载的远程文件系统已经不可访问了,我这里遇到的问题就是由 down 掉的 NFS 服务器引起的。

正是因为得不到 IO 的相应,进程才进入了 uninterruptible sleep 状态,所以要想使进程从 uninterruptible sleep 状态恢复,就得使进程等待的 IO 恢复。

查看message日志文件找到相关内核报错日志

默认情况下, Linux会最多使用40%的可用内存作为文件系统缓存。当超过这个阈值后,文件系统会把将缓存中的内存全部写入磁盘, 导致后续的IO请求都是同步的。将缓存写入磁盘时,有一个默认120秒的超时时间。 出现上面的问题的原因是IO子系统的处理速度不够快,不能在120秒将缓存中的数据全部写入磁盘。IO系统响应缓慢,导致越来越多的请求堆积,最终系统内存全部被占用,导致系统失去响应。

从系统中看下 hung_task 相关的参数及其参数值

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@aliy-nt1 ~]# sysctl -a | grep 'vm.dirty'
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 20
vm.dirty_writeback_centisecs = 500

[root@aliy-nt1 ~]# sysctl -a | grep hung
kernel.hung_task_check_count = 4194304
kernel.hung_task_panic = 0
kernel.hung_task_timeout_secs = 120
kernel.hung_task_warnings = 0

通过sar查看一段时间内的cpu使用情况,CPU使用率不高。

1
sar -f /var/log/sa/sa17

查看内存,内存使用也不高

1
sar -r -f /var/log/sa/sa17

查看硬盘IO

1
sar -d -f /var/log/sa/sa17

检查磁盘分区是否存在坏块,发现没有坏块情况

1
2
3
4
5
6
7
8
9
10
11
12
[root@aliy-nt1 ~]# /sbin/badblocks -v /dev/vda
正在检查从 0 到 41943039的块
Checking for bad blocks (read-only test): done
Pass completed, 0 bad blocks found. (0/0/0 errors)
[root@aliy-nt1 ~]# /sbin/badblocks -v /dev/vdb
正在检查从 0 到 41943039的块
Checking for bad blocks (read-only test): done
Pass completed, 0 bad blocks found. (0/0/0 errors)
[root@aliy-nt1 ~]# /sbin/badblocks -v /dev/vdc
正在检查从 0 到 104857599的块
Checking for bad blocks (read-only test): done
Pass completed, 0 bad blocks found. (0/0/0 errors)

查找网上解决方法如下:

根据应用程序情况,对vm.dirty_ratio,vm.dirty_background_ratio两个参数进行调优设置。

1
2
3
4
5
6
7
8
9
10
11
# sysctl -w vm.dirty_ratio=10
# sysctl -w vm.dirty_background_ratio=5
# sysctl -p

如果系统永久生效,修改/etc/sysctl.conf文件。加入如下两行:
#vi /etc/sysctl.conf

vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

sysctl -p

但是观察了一段时间,进程依然状态会导致为D,查看了srs配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
listen              1935;
max_connections 1000;
srs_log_tank file;
srs_log_file ./objs/srs.log;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
# crossdomain on;
}
stats {
network 0;
disk sda sdb xvda xvdb;
}
vhost __defaultVhost__ {
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
hstrs on;
}
# mix_correct on;
http_hooks {
enabled on;
on_publish http://10.25.208.243:1241/callbackOnPublish;
on_unpublish http://10.25.208.243:1241/callbackOnUnpublish;
on_play http://10.25.208.243:1241/callbackOnPlay;
on_stop http://10.25.208.243:1241/callbackOnStop;
on_dvr http://10.25.208.243:1241/callbackOnDvr;
}
dvr {
enabled on;
dvr_path /mnt/mov/[stream]-[timestamp].flv;
dvr_plan session;
dvr_duration 30;
dvr_wait_keyframe on;
time_jitter full;
}
hls {
enabled on;
hls_path /mnt/mov;
hls_m3u8_file [stream].m3u8;
hls_ts_file [stream]-[seq].ts;
hls_fragment 10;
hls_window 60000;
}
}

srs录制视频、语音是通过阿里云NFS文件服务挂载到本地/mnt/mov文件,作为临时视频中转服务,后期会自动上传到OSS对象存储服务,是否因为频繁的对该NFS读写IO性能导致srs出现hung呢?
查看相关文档,阿里云由此文档
https://help.aliyun.com/knowledge_detail/53839.html?spm=a2c4g.11186623.4.7.jXaKcm

1
2
3
4
5
6
7
8
9
10
11
Linux nfs客户端对于同时发起的NFS请求数量进行了控制,若该参数配置较小会导致IO性能较差,请查看该参数:cat /proc/sys/sunrpc/tcp_slot_table_entries

[root@aliy-nt1 srs]# cat /proc/sys/sunrpc/tcp_slot_table_entries
2

默认编译的内核该参数最大值为256,可适当提高该参数的值来取得较好的性能,请以root身份执行以下命令:

echo "options sunrpc tcp_slot_table_entries=128" >> /etc/modprobe.d/sunrpc.conf
echo "options sunrpc tcp_max_slot_table_entries=128" >> /etc/modprobe.d/sunrpc.conf
sysctl -w sunrpc.tcp_slot_table_entries=128
修改完成后,您需要重新挂载文件系统或重启机器。