定位一个进程出现超时或卡死的方法
当定位一个进程出现超时或者卡死的问题,我们通常会怀疑是否是io超时或者其他调度问题,我们需要在这个进程出现卡死或超时,获取当时的调用栈信息,通过调用栈信息来分析可能的原因。如下为一个journald进程监控的方法:
workdir=/root/journald coredir=${workdir}/cores function prepare() { mkdir -p ${workdir} mkdir -p ${coredir} systemctl start systemd-journald systemctl start rsyslog } function monitor_one() { local -i count="$1" message="${count}: this is a log to monitor journald." logger "${message}" & sleep 2 contents=$(cat /var/log/messages | grep "${message}") if [ -z "$contents" ] ; then echo "journald in stuck...." pid=$(pgrep -f /usr/lib/systemd/systemd-journald) filename="journald-$(date +%s)-${count}" cat /proc/${pid}/stack > ${coredir}/${filename}.stack gcore -o ${coredir}/${filename} "${pid}" local -i core_count=$(ls -l ${coredir} | grep journald | wc -l) if [ ${core_count} -gt 100 ] ; then exit fi fi } function monitor_journal() { local -i count=0 while : ; do ((count++)) monitor_one $count sleep 4 done } prepare monitor_journal
这里主要的内容是当出现进程卡住时,获取进程的stack信息,使用gcore工具,在进程不重启的情况下,获取进程的core信息。获取到这些后用于问题分析。
/proc/[pid]/stack
/proc/[pid]/stack 示当前进程的内核调用栈信息,只有内核编译时打开了 CONFIG_STACKTRACE 编译选项,才会生成这个文件。举例如下:
1
|
$ cat /proc/2406/stack
|
gcore
当调试一个程序的时候,理想状态是不重启应用程序就获取core文件。
gcore命令可以使用下面步骤来获取core文件:
1. 确认gdb软件包已经被正确安装。
2. 使用调试参数编译程序(例如: gcc中使用"-g"选项),编译后不要去除文件的调试符号信息。
3. 执行应用程序。
4. 执行gcore命令生成指定应用程序的core文件并且保存在当前目录下。
下面为一个监控dbus的脚本:
workdir=/root/dbus coredir=${workdir}/cores function prepare() { mkdir -p ${workdir} mkdir -p ${coredir} systemctl start systemd-journald systemctl start rsyslog systemctl start sshd systemctl start dbus } function monitor_dbus() { local -i count=0 local -i ecount=0 while : ; do ((count++)) /usr/bin/ssh 127.0.0.1 pwd >/dev/null 2>&1 & local -i ssh_pid=$! sleep 4 process=$(ps -Lwwo pid,ppid,tid,psr,pri,stat,wchan:30,uname,vsize,rss,start_time,args --no-header -p ${ssh_pid} | grep "/usr/bin/ssh") if [ -n "${process}" ] ; then ##当ssh进程超过4s都还没有退出的话,说明dbus可能占用了很长时间 echo "dbus in stuck...." date_text=$(date +%s) dbus_pid=$(pgrep -f /bin/dbus-daemon) machined_pid=$(pgrep -f /usr/lib/systemd/systemd-machined) dbus_filename="dbus-${date_text}-${count}" machined_filename="machined-${date_text}-${count}" systemd_filename="systemd-${date_text}-${count}" cat /proc/1/stack > ${coredir}/${systemd_filename}.stack ps -eLwwo pid,ppid,tid,psr,pri,stat,wchan:30,uname,vsize,rss,start_time,args > ${coredir}/${systemd_filename}.ps top > ${coredir}/${systemd_filename}.top cat /proc/meminfo > ${coredir}/${systemd_filename}.mem cat /proc/interrupts > ${coredir}/${systemd_filename}.intr #gcore -o ${coredir}/${dbus_filename} "${dbus_pid}" #gcore -o ${coredir}/${machined_filename} "${machined_pid}" #gcore -o ${coredir}/${systemd_filename} 1 local -i core_count=$(ls -l ${coredir} | grep dbus | wc -l) ((ecount++)) if [ $ecount -gt 3 ] ; then cp -frpa /run/cgroup_time.txt ${coredir}/cgroup_time.txt echo 3 > /proc/sys/vm/drop_caches echo c >/proc/sysrq-trigger fi if [ ${core_count} -gt 100 ] ; then exit fi else ((ecount=0)) fi done } prepare monitor_dbus