前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >​[linux][process]进程crash类问题处理方法

​[linux][process]进程crash类问题处理方法

作者头像
皮振伟
发布2018-04-09 11:11:37
3.9K0
发布2018-04-09 11:11:37
举报
文章被收录于专栏:皮振伟的专栏

前言: 进程crash一般比较讨厌,尤其是segmentation fault,所谓的“踩内存”,是最讨厌的。 分析: 1,status 进程的状态,一般使用ps aux命令查看: 其中STAT列就是进程的状态。这里说明一下,COMMAND列中以[]包围的进程是内核启动的进程。

或者查看cat /proc/PID/status:

其中State行也是进程状态。其实ps的命令也是通过遍历/proc目录下的进程目录实现的。 关于进程的状态,参考代码linux-4.0.4/fs/proc/array.c

再打开linux-4.0.4/include/linux/sched.h

对比可以发现: D状态对应的就是TASK_UNINTERRUPTIBLE状态。处于TASK_UNINTERRUPTIBLE状态的进程,不占用CPU执行,而且不能被杀掉!!那就意味着,处于D状态的进程,是没有办法处理的。一般触发D状态的情况是:进程通过系统调用陷入到kernel中,kernel中又需要等待driver唤醒。 典型的陷入D状态的复现办法:在client端,使用nfs协议mount到server端;在server端用iptables限制住2049端口(nfs使用2049端口);在client端随便访问一下nfs的目录。bingo,D状态的进程出现了。 T状态可以通过给进程发送SIGSTOP信号看到。 Z状态对应的EXIT_ZOMBIE状态。这个时候,进程已经退出了,就差父进程执行wait了。 分析问题之前,一定要先分析清楚进程的状态。否则,不能因为进程的log不打印了,就判断出进程挂了。因为还可能是D状态和T状态。 2,exit 进程退出,有两种原因:要么进程自己主动执行了exit()(或者_exit()函数,再或者直接使用syscall exit_group);要么被kernel杀掉,而kernel杀掉进程,会选择使用信号,所以,根据信号,可以简单知道进程被杀掉的原因。 3,signal 进程可以选择自己实现信号的handler。默认情况下,进程对信号的处理行为,参考代码linux-4.0.4/include/linux/signal.h:

主要分成几类: SIG_KERNEL_STOP_MASK:直接杀掉进程。 SIG_KERNEL_COREDUMP_MASK:杀掉,尝试生成coredump文件。 SIG_KERNEL_IGNORE_MASK:不处理。 SIG_KERNEL_ONLY_MASK:SIGKILL信号和SIGSTOP信号只能使用kernel默认的handler,不能被用户实现。 常见信号的原因: SIGABRT:用户进程调用了abort函数,或者assert失败。 SIGFPE:除以0了。 SIGPIPE:常见的就是TCP连接断开了。 SIGEGV:访问了内存空间之外的内存,或者访问的内存权限不匹配,例如写只读的内存,再或者就是OOM了。 4,coredump coredump生成,出了符合信号的要求外,还需要limit条件符合:

查看进程的limit情况,其中Max core file size行就是coredump文件的最大size。 注意,ulimit -c只能修改当前的shell的limit,并不能修改已经启动的进程的limit。而在shell中继续运行的进程,则可以继承shell中的limit。 另外,生成coredump的pattern要符合条件: 查看当前的core pattern : cat /proc/sys/kernel/core_pattern 修改当前的core pattern : echo “/core/patter/you/want” /proc/sys/kernel/core_pattern 参考linux-4.0.4/fs/coredump.c

可见,在core pattern中可以使用变量控制符。例如core_%p,那么就会生成带着pid的coredump文件。 5,segmentation fault segmentation fault的体现就是进程收到了SIGEGV。 a,访问越界或访问权限引起 cat /proc/PID/maps可以查看到某进程的虚拟内存区间的layout。

第一列,如00400000-0040c000,表示如果进程可以访问地址范围。0不在范围内,所以访问NULL就必然导致SIGEGV。 第二列,如 r-xp,表示权限。例如写 r-xp,必然SIGEGV。 所以,一点经验: 使用gdb分析coredump的时候,使用命令info registers,如果edi,esi,edx,ecx,r8,r9(arm是r0,r1,r2,r3)中的某个数值很小(比0稍微大一点的话,很可能是使用空指针访问了数据结构),优先看一下,如果传的是地址的话,那么很可能就是凶手。 软件的debug版本上,可以在一片连续的内存上下分别放置一个rwx权限都没有的page,如果overflow了,或者被别人给踩了,都可以提前发现。毕竟分析访问权限引起的SIGEGV更简单,可以直接敲bt看到trace。 b,stack被踩坏了 解决这类问题很麻烦,有时候还需要看缘分。 stack被踩坏了,那么是看不到backtrace的。而且,可能踩坏的还不只是stack。没有coredump的时候,只能通过dmesg,查到出问题时候的PC寄存器的值;有coredump的情况下,可以拿到更多的寄存器的值。运气好的话,可以用PC值直接定位到出问题的函数,运气不好的话,可能你看到的会是memset函数(一包代码中可能成百上千处使用了memset,哭死了)。这时候,也比较考察gdb的基本功,info registers,x命令查看内存。必要时,还需要使用objdump反汇编,分析汇编。addr2line也可能帮得上忙。 当然,如果在arm上,会舒服一点,毕竟arm还有lr寄存器,哪怕是栈坏了,还是可以通过pc拿到最后一级bt,用lr拿到倒数第二级bt,容易很多。 作者在这类问题上也算过来人,窃以为:这种状况是进程crash中最难处理的,能不能顺利解决,还要看复现概率,看栈的破坏程度,看运气,还要看想象力。 c,oom 保存好log,dmesg中比较明显。 后记: 推荐阅读一个kernel的进程管理部分的代码(linux/kernel目录),busybox(很多命令组成的工具集,代码更简单些),bionic(Android使用的精简版的libc,当然,直接翻翻glibc也不错)。 Good luck~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AlwaysGeek 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档