什么是僵尸进程, 孤儿进程
僵尸进程
: 当一个进程终止,但其父进程还没有通过wait()
或waitpid()
系统调用来获取其终止状态时,该进程就变成了僵尸进程。僵尸进程是指已经完成执行但仍然在进程表中保留条目的进程。这种情况通常发生在父进程没有充分回收子进程资源的情况下。僵尸进程会占用系统资源,因此父进程应该及时处理子进程的终止状态,以避免僵尸进程的产生。
孤儿进程
: 孤儿进程是指其父进程提前终止或者意外终止,而子进程仍然在继续执行。当父进程退出时,孤儿进程会被init进程(在现代系统中通常是systemd或者initd等)接管。init进程会成为孤儿进程的新父进程,并负责回收其资源,确保其能够正常终止,避免它变成僵尸进程。通过将孤儿进程的父进程设置为init进程,系统确保了即使原始父进程退出,子进程仍然能够得到适当的处理,防止了孤儿进程的出现。
创建僵尸进程
下面的代码可以创建僵尸进程, 前10秒子进程是正常进程, 之后20秒子进程变成僵尸进程, 再之后程序进入循环等待状态, 此时子进程一直是僵尸进程
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
| package main import ( "fmt" "os" "os/exec" "os/signal" "syscall" "time" )
func main(){ cmd := exec.Command("/bin/bash","-c","sleep 10","echo \"hello zombie\"") err := cmd.Start() if err != nil { fmt.Println("启动子进程失败") } fmt.Println("父进程 PID: ",os.Getpid()) fmt.Println("子进程 PID: ",cmd.Process.Pid) time.Sleep(30 * time.Second) sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigCh cmd.Process.Kill() cmd.Process.Wait() os.Exit(0) }()
select {} }
|
通过下面的命令编译上面的代码, 之后执行三遍生成的二进制文件, 且挂起到后台, 此时便产生了三个父进程以及对应三个僵尸子进程
1 2 3 4 5 6 7
| mkdir zombie && cd zombie go mod init zombie go mod tidy go build ./zombie & ./zombie & ./zombie &
|
这里还有一份C语言的
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
| #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h>
int main() { pid_t child_pid = fork();
if (child_pid == 0) { printf("子进程:%d\n", getpid()); exit(0); } else if (child_pid > 0) { printf("父进程:%d\n", getpid()); sleep(360); wait(NULL); printf("父进程退出\n"); } else { perror("fork failed"); return 1; }
return 0; }
|
1 2 3
| gcc zombie.c -o zombie ./zombie & ./zombie &
|
批量杀死僵尸进程
下面的shell
脚本, 获取系统所有的僵尸进程的PID, 然后用这个PID获取其父进程的PID, 最后杀死所有的父进程, 这样僵尸进程也就都变成了孤儿进程, 然后被init进程回收资源, 从而达到批量杀死僵尸进程的目的
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
| #!/bin/bash
zombie_pids=$(ps -aux | grep -v "grep" | grep "Z" | awk 'NR>1{print $2}' | tr '\n' ' ') father_pids=""
if [ "$zombie_pids" == "" ];then exit 0 fi
for child_pid in $zombie_pids do father_pid=$(ps -o ppid= -p $child_pid | awk '{print $1}') father_pids="$father_pids $father_pid" done
unique_father_pids=$(echo $father_pids | tr ' ' '\n' | sort -u)
if kill -9 $unique_father_pids ;then echo "kill father_pids done" fi
log_path="$(pwd)"
echo "$(date +"%F %T") father_pids: $(echo $unique_father_pids | tr '\n' ' ')" | tee -a $log_path/father_pids_killed.log echo "$(date +"%F %T") zombie_pids: $zombie_pids" | tee -a $log_path/zombie_pids_get.log
|
1 2
| chmod +x kill-zombie.sh ./kill-zombie.sh
|
截图