Linux-LInux下僵尸进程与孤儿进程是如何产生的?分别有什么作用呢?如何杀死僵尸进程?可以提供一个较为通用的shell脚本吗?

Linux-LInux下僵尸进程与孤儿进程是如何产生的?分别有什么作用呢?如何杀死僵尸进程?可以提供一个较为通用的shell脚本吗?

浮生未歇 发布于 2017-02-20 字数 0 浏览 1192 回复 3

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

瑾兮 2017-09-20 3 楼

孤儿进程
父进程已经退出,子进程还存在,那么子进程将会变成孤儿进程,孤儿进程将会被init进程收养和清理,孤儿进程不会对系统造成危害。
僵尸进程
僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。

杀死僵尸进程
结束掉僵尸进程的父进程,让僵尸进程变成孤儿进程,孤儿进程会自动被init进程收养和清理。
ps -ef|grep defunct|grep -v grep|awk '{print "kill -9" $2,$3}'

偏爱自由 2017-06-24 2 楼

参: @僵尸进程问题

1:如何产生:
僵尸进程,子进程先于父进程结束,而父进程未用wait或waitpid来监听子进程的状态,使子进程的资源无法释放。
孤儿进程:父进程先于子进程结束。

2:作用
僵尸进程:几乎没有什么作用,不会被调度。
孤儿进程:最经典的一个作用应该是避免产生僵尸进程,通过fork两次的方式,刻意杀掉其父进程,使之成为守护进程。

3:如何杀死僵尸进程

4:杀死僵尸进程的shell脚本
再提供一个网络版本 Automatic “zombie” processes killing (shell script)

#!/bin/bash
# Zombie processes killing script.
# Must be run under root.
case "$1" in
--admin)
stat=`ps ax | awk '{print $1}' | grep -v "PID" | xargs -n 1 ps lOp | grep -v "UID" | awk '{print"pid: "$3" *** parent_pid: "$4" *** status: "$10" *** process: "$13}' | grep ": Z"`

if ((${#stat} > 0));then
        echo zombie processes found:
        echo .
        ps ax | awk '{print $1}' | grep -v "PID" | xargs -n 1 ps lOp | grep -v "UID" | awk '{print"pid: "$3" *** parent_pid: "$4" *** status: "$10" *** process: "$13}' | grep ": Z"
        echo -n "Kill zombies? [y/n]: "
        read keyb
        if [ $keyb == 'y' ];then
        echo killing zombies..
        ps ax | awk '{print $1}' | grep -v "PID" | xargs -n 1 ps lOp | grep -v "UID" | awk '{print$4" status:"$10}' | grep "status:Z" | awk '{print $1}' | xargs -n 1 kill -9
        fi
    else
        echo no zombies found!
    fi
;;
--cron)
    stat=`ps ax | awk '{print $1}' | grep -v "PID" | xargs -n 1 ps lOp | grep -v "UID" | awk '{print"pid: "$3" *** parent_pid: "$4" *** status: "$10" *** process: "$13}' | grep ": Z"`
if ((${#stat} > 0));then
ps ax | awk '{print $1}' | grep -v "PID" | xargs -n 1 ps lOp | grep -v "UID" | awk '{print$4" status:"$10}' | grep "status:Z" | awk '{print $1}' | xargs -n 1 kill -9
    echo `date`": killed some zombie proceses!" >> /var/log/zombies.log
    fi
;;
*)  echo 'usage: zombies {--cron|--admin}'
;;
esac

exit 0
夜无邪 2017-05-03 1 楼

僵尸进程:先于父进程终止,但是父进程没有对其进行善后处理(获取终止子进程有关信息,释放它仍占有的资源)。消灭僵尸进程的唯一方法是终止其父进程。

孤儿进程:该进程的父进程先于自身终止。其特点是PPID=1(init进程的ID)。一个孤儿进程可以自成孤儿进程组。

僵尸进程产生的原因:

一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用 exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。

怎么查看僵尸进程:

  利用命令ps,可以看到有标记为Z的进程就是僵尸进程。

僵尸进程清除的方法

  1.改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。

  SIGCHLD信号:子进程结束时, 父进程会收到这个信号。如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。

  2. kill -18 PPID (PPID是其父进程)

  这个信号是告诉父进程,该子进程已经死亡了,请收回分配给他的资源。

  SIGCONT也是一个有意思的信号。如前所述,当进程停止的时候,这个信号用来告诉进程恢复运行。该信号的有趣的地方在于:它不能被忽略或阻塞,但可以被捕获。缺省行为是丢弃该信号。

  3.终止父进程

  如果方法2不能终止,可采用终止其父进程的方法(如果其父进程不需要的话)父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。

  先看其父进程又无其他子进程,如果有,可能需要先kill其他子进程,也就是兄弟进程。方法是:

  kill —15 PID1 PID2 (PID1,PID2是僵尸进程的父进程的其它子进程)。

  然后再kill父进程:kill —15 PPID

  这样僵尸进程就可能被完全杀掉了。

以下是自动杀超时终端、僵尸进程的shell程序(脚本来源于网络)

### shell程序名:chk_tty
### 功能一、找出非正常退出而导致系统内出现僵尸进程,这种进程有时会不停地向CPU提出请求,导致CPU空闲率总为0;
### 功能二、找出超时登录的终端;
TTY=`tty |awk -F '/' '{print $3}'`
N=0
cat /dev/null >> chk_tty.log
while [ 1 ]
do
TIME=`date '+%Y%m%d-%H:%M:%S'`
echo $TIME
sar -u 1 2
#非正常退出检查
TTYN_A=`who |grep ttyp |awk -F ' ' '{print $2}'|sort|uniq|wc -l|
awk -F ' ' '{print $1}'`
TTYN_B=`ps -ef|grep ttyp|grep -v tty0|grep -v tty1|
grep -v login -c -p|awk -F ' ' '{print $6}'|
sort|uniq|wc -l|awk -F ' ' '{print $1}'`
echo TTYN_A=$TTYN_A TTYN_B=$TTYN_B
cat /dev/null >tmp_tty.sh
if [ $TTYN_A != $TTYN_B ]
then
ps -ef | grep ttyp|awk -F ' ' '{printf sh chkttysh %sn,$6}'|
sort|uniq |grep -v $TTY$>tmp_tty.sh
sh tmp_tty.sh; #rm tmp_tty.sh
fi
# 超时登录检查
sleep 5
cat /dev/null >tmp_outtime.sh
who -u|grep ttyp|grep -v ^root|grep -v ^cbps|
grep -v rewrite|grep -v ' . '|grep :|sed s/://g|
awk -F ' ' '{if ($6>40) printf killout %s %s 40n,$2,$7; }' >tmp_outtime.sh
sleep 1
sh tmp_outtime.sh; #rm tmp_outtime.sh
sar -u 1 2
echo sleep 199;sleep 199
done
### shell程序名:chkttysh
### 功能:对chk_tty检出的进程判断,符合即杀出;
TY=$1
FLAG=`ps -f -t $TTY | grep login -c -p|wc -l|awk -F ' ' '{print $1}'`
if [ $FLAG -lt 1 ]
then
TODAY=`date '+%Y%m%d-'`
USER=`ps -f -t $TTY|grep ttyp|awk -F ' ' '{print $1}'`
USER_ID=`ps -f -t $TTY|grep ttyp|awk -F ' ' '{print $2}'`
USERTIME=`ps -f -t $TTY|grep ttyp|awk -F ' ' '{print $5}'|cut -b1-8`
SYSCALL=`sar -c 1 2|grep Average|awk -F ' ' '{print $2}'`
if [ x$USERTIME != $USERTIME -a $SYSCALL -gt 50000 ]
then
grep $TODAY /etc/term.log|grep $TTY|grep $USER|grep $USER_ID
STR_FLAG=`grep $TODAY chk_tty.log|grep $TTY|grep $USER|grep $USER_ID$`
echo $TODAY $USER ID=$USER_ID $USERTIME $TTY SYSCALL=$SYSCALL
if [ x$STR_FLAG = x ]
then
echo |x$STR_FLAG| |x|
grep $TODAY /etc/term.log|grep $TTY|grep $USER|grep $USER_ID >> chk_tty.log
fi
# 杀出,此功能要慎重使用,如错杀进程,会导致系统崩溃
if [ $USER_ID -gt 100 ]
then grep $TODAY /etc/term.log|grep $TTY|grep $USER|grep $USER_ID
echo kill -9 $USER_ID
kill -9 $USER_ID
fi
echo $TTY is Death processes ! Killed ! >tmp_chkttysh.txt
wrtty tmp_chkttysh.txt; sleep 1
echo >tmp_chkttysh.txt
n=5
while [ $n -gt 0 ]
do
wrtty tmp_chkttysh.txt; sleep 1
n=`expr $n - 1`
done
fi
rm tmp_chkttysh.txt
fi
### shell程序名:killout
### 功能:杀终端
if [ $# -lt 3 ]
then echo format: killout ttyp ID OUT_M
exit
fi
TTY=$1
ID_A=$2
OUT_M=$3
TIME=`date '+%Y%m%d-%T '`
# 确保所杀进程正确,避免误杀造成系统崩溃
ID_B=`ps -f -t $TTY | grep login -c -p$ |awk -F ' ' '{print $2}'`
N_line=`ps -f -t $TTY |wc -l|awk -F ' ' '{print $1}'`
# 如果传递来的进程号与 ps 命令查到的进程号不杀出;
# 如果指定终端进程少于 3 个不杀出;
# 如果传送来的进程号是空值不杀出;
# 如果传送业的进程号小于 100 不杀出;
#if [ $ID_A != $ID_B -o $N_line -lt 3 -o x$ID_A = x -o $ID_A -lt 100 ]
if [ $ID_A != $ID_B -o $N_line -lt 3 -o x$ID_A = x ]
then
echo $TIME >>killout.err
echo ID_A=$ID_A ID_B=$ID_B N_line=$N_line >>killout.err
who -u |grep $tty >> killout.err
ps -f -t $TTY >> killout.err
sleep 1
exit
fi
echo >/dev/$TTY
echo >/dev/$TTY
echo >/dev/$TTY
echo >/dev/$TTY
echo r 中 心 通 告 >/dev/$TTY
echo >/dev/$TTY
echo r$TTY 用户: >/dev/$TTY
echo >/dev/$TTY
echo r 因此终端空闲时间超过 $OUT_M 分钟,所以被强制退出,登录记录如下!!! >/dev/$TTY
echo >/dev/$TTY
echo r 监测时间:$TIME >/dev/$TTY
echo r
w -x |grep $TTY >/dev/$TTY
echo >/dev/$TTY
echo 要显示正在进行的操作请按 CTRL+R 进行屏幕刷新 >/dev/$TTY
echo >/dev/$TTY
echo $TIMEc >>outtime.log
w -x |grep $TTY >>outtime.log
onstat -u |grep $TTY |awk -F ' ' '{print $3}'|sed s/^/onmode -z / >tmp_sessid.sh
sh tmp_sessid.sh
kill -9 $ID_A
### shell程序名:wrtty
### 功能:与sco5.0下wall,好处是只显示自己希望显示的内容。
if [ $# -lt 3 ]
then echo format: killout ttyp ID OUT_M
exit
fi
TTY=$1
ID_A=$2
OUT_M=$3
TIME=`date '+%Y%m%d-%T '`
# 确保所杀进程正确,避免误杀造成系统崩溃
ID_B=`ps -f -t $TTY | grep login -c -p$ |awk -F ' ' '{print $2}'`
N_line=`ps -f -t $TTY |wc -l|awk -F ' ' '{print $1}'`
# 如果传递来的进程号与 ps 命令查到的进程号不杀出;
# 如果指定终端进程少于 3 个不杀出;
# 如果传送来的进程号是空值不杀出;
# 如果传送业的进程号小于 100 不杀出;
#if [ $ID_A != $ID_B -o $N_line -lt 3 -o x$ID_A = x -o $ID_A -lt 100 ]
if [ $ID_A != $ID_B -o $N_line -lt 3 -o x$ID_A = x ]
then
echo $TIME >>killout.err
echo ID_A=$ID_A ID_B=$ID_B N_line=$N_line >>killout.err
who -u |grep $tty >> killout.err
ps -f -t $TTY >> killout.err
sleep 1
exit
fi
echo >/dev/$TTY
echo >/dev/$TTY
echo >/dev/$TTY
echo >/dev/$TTY
echo r 中 心 通 告 >/dev/$TTY
echo >/dev/$TTY
echo r$TTY 用户: >/dev/$TTY
echo >/dev/$TTY
echo r 因此终端空闲时间超过 $OUT_M 分钟,所以被强制退出,登录记录如下!!! >/dev/$TTY
echo >/dev/$TTY
echo r 监测时间:$TIME >/dev/$TTY
echo r
w -x |grep $TTY >/dev/$TTY
echo >/dev/$TTY
echo 要显示正在进行的操作请按 CTRL+R 进行屏幕刷新 >/dev/$TTY
echo >/dev/$TTY
echo $TIMEc >>outtime.log
w -x |grep $TTY >>outtime.log
onstat -u |grep $TTY |awk -F ' ' '{print $3}'|sed s/^/onmode -z / >tmp_sessid.sh
sh tmp_sessid.sh
kill -9 $ID_A