昆玉市网站建设_网站建设公司_代码压缩_seo优化
2025/12/30 11:46:07 网站建设 项目流程

文章目录

  • 孤儿进程
    • 孤儿进程的含义
    • 制造孤儿进程
    • 孤儿进程的特点
    • 为什么需要孤儿进程机制
    • 孤儿进程的管理
      • 查找孤儿进程
      • 监控孤儿进程
      • 正确处理孤儿进程
  • 守护进程
    • 实现守护进程
      • 脱离终端
      • 关闭stdio流
      • 设置umask
      • 设置工作路径
  • 孤儿进程 vs 僵尸进程 vs 守护进程

孤儿进程

孤儿进程的含义

  • 父进程先于子进程终止,子进程成为孤儿进程,被 init 进程收养
  • 通俗比喻:
    • 就像父母双亡的孩子被政府(init进程)收养:
    • 父进程先死了
    • 子进程还活着
    • 被 init进程(PID=1) 收养
    • 继续正常运行
正常进程 → 父进程死亡 → 变成孤儿 → 被init收养 → 正常结束 (父母死了) (无父) (政府收养) (正常生活)

早期Linux是由init进程直接管理它,Ubuntu并不是由 init进程 接管的

而是由一个systemd接管的

init实际执行的就是systemd,因此被systemd的进程收养的孤儿进程,相当于被 init收养了

  • 是守护进程的基础

制造孤儿进程

  • test.c
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>intmain(){pid_tpid=fork();if(pid==0){// 子进程printf("子进程 PID=%d 启动\n",getpid());printf("子进程的父进程 PID=%d\n",getppid());// 子进程睡眠5秒,等父进程先死sleep(5);printf("5秒后...\n");printf("子进程 PID=%d 还在运行\n",getpid());printf("现在的父进程 PID=%d (应该是1)\n",getppid());sleep(5);// 再运行一段时间printf("子进程结束\n");}else{// 父进程printf("父进程 PID=%d 创建了子进程 %d\n",getpid(),pid);printf("父进程立即结束\n");// 父进程立即退出,子进程变成孤儿exit(0);}return0;}

孤儿进程的特点

  • 父进程变成 init(PID=1)
# 运行以上的程序,然后用另一个终端查看 $ ps aux | grep -E "PID|孤儿进程的程序名" # 可以看到子进程的PPID(父进程ID)变成了1
  • 完全正常运行
    • 继续执行代码
    • 占用系统资源
    • 可以被信号控制
    • 正常退出时会被 init 回收
  • 与僵尸进程的区别
僵尸进程先于父进程结束便于父进程读取进程退出的状态
孤儿进程后于父进程结束常用于创建守护进程

为什么需要孤儿进程机制

  • 保证进程不会失去管理
// 如果没有孤儿进程收养机制:// 父进程死亡 → 子进程变成"野进程" → 无法被管理 → 资源泄漏// 有了收养机制:// 父进程死亡 → init收养 → 正常管理 → 资源正确释放
  • init进程的责任
# init进程(PID=1)是系统所有进程的祖先进程 # 它的责任包括: # 1. 收养所有孤儿进程 # 2. 等待孤儿进程结束 # 3. 回收孤儿进程资源

孤儿进程的管理

查找孤儿进程

# 方法1:使用ps查看PPID=1的进程ps-eo pid,ppid,pgid,sid,cmd|awk'$2 == 1 && $1 != 1'# 方法2:查找被init收养的进程ps-ef|awk'$3 == 1 && $2 != 0'# 方法3:使用pstree查看进程树pstree -p|grep-A5-B5"init"

监控孤儿进程

#!/bin/bash# monitor_orphans.sh - 监控孤儿进程whiletrue;doecho"=== 检查孤儿进程$(date)==="# 查找孤儿进程orphan_count=$(ps-eo pid,ppid,cmd|awk'$2 == 1 && $1 != 1'|wc-l)if[$orphan_count-gt0];thenecho"发现$orphan_count个孤儿进程:"ps-eo pid,ppid,user,cmd,start_time --sort=-start_time|awk'$2 == 1 && $1 != 1'echo"详细信息:"forpidin$(ps-eo pid,ppid|awk'$2 == 1 && $1 != 1 {print $1}');doecho"进程$pid:"echo" 运行时间:"$(ps-oetime=-p $pid)echo" 内存使用:"$(ps-orss=-p $pid)"KB"echo" CPU使用:"$(ps-o %cpu=-p $pid)"%"# 检查是否正常ifkill-0$pid2>/dev/null;thenecho" 状态:运行正常"elseecho" 状态:已结束"fiecho"---"doneelseecho"没有发现孤儿进程"fiecho""sleep60# 每分钟检查一次done

正确处理孤儿进程

  • // 如果发现孤儿进程异常,可以:
#include<stdio.h>#include<stdlib.h>#include<signal.h>#include<unistd.h>intmain(){// 方法1:优雅地终止孤儿进程pid_torphan_pid=1234;// 假设的孤儿进程PID// 先尝试友好终止if(kill(orphan_pid,SIGTERM)==0){printf("已发送SIGTERM给进程 %d\n",orphan_pid);// 等待一段时间sleep(5);// 检查是否还在运行if(kill(orphan_pid,0)==0){printf("进程 %d 还在运行,发送SIGKILL\n",orphan_pid);kill(orphan_pid,SIGKILL);}else{printf("进程 %d 已终止\n",orphan_pid);}}else{printf("进程 %d 不存在或无法终止\n",orphan_pid);}return0;}

守护进程

  • 守护程序是在后台运行并监督系统或向其他进程提供功能的服务进程
  • 利用孤儿进程被init收养这一机制实现一个守护进程

实现守护进程

脱离终端

if(fork()>0)exit(0);//进程组首进程不能成为会话首进程,所以这一步很关键setsid();//成立一个会话

关闭stdio流

close(0);close(1);close(2);

或者

close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);

设置umask

#include<sys/types.h>#include<sys/stat.h>mode_tumask(mode_tmask);

umask()的调用总会成功,并返回进程的前一umask

#include<stdio.h>#include<sys/stat.h>#include<sys/types.h>intmain(intargc,constchar*argv[]){umask(0222);//设置本进程的umask//由于后面不再做任何事所以这里省略了错误判断mkdir("/home/linux/process/tmp_mkdir",0777);perror("mkdir");//提示目录文件是否创建成功return0;}

设置工作路径

if(-1==chdir("/")){perror("chdir");exit(EXIT_FAILURE);}

孤儿进程 vs 僵尸进程 vs 守护进程

特性孤儿进程僵尸进程守护进程
父进程状态已终止未调用wait()已终止(故意)
进程状态运行中已终止运行中
占用资源正常占用占用PID,不占内存正常占用
能否被杀死可以不可以可以
回收者init进程父进程init进程
产生方式父进程先死父进程不wait故意fork两次
是否正常正常现象程序bug设计如此

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询