1 进程介绍
# 1 进程概念
  1.正在执行的程序
  2.正在计算机上执行的程序具体实例
  3.能分配处理器(cpu),并由处理器执行的实体
# 2 进程与程序
  程序:存放代码的文件 ---> 静态
  进程:程序的运行过程 ---> 动态   是程序的具体实例
# 3 进程的状态
  进程是一种动态描述,并不代表所有的进程都在运行
  进程在内存中因策会略或调度需求,会处于各种状态
  
# 4 就绪态、执行态、阻塞态
  1.就绪状态
  当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行
  
  2.执行(Running)状态
  当进程已获得处理机,其程序正在处理机上执行
  
  3.阻塞(Blocked)状态
  正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态
  引起进程阻塞的事件可有多种,eg:等待I/O完成、申请缓冲区不能满足、等待信件(信号)等
2 进程查看
2.1 查看进程
2.1.1 ps 基本语法
# 1 ps命令  Process Status的缩写
查看当前系统进程的命令
  -a   : 显示当前终端的所有进程
  -u   : 显示进程的用户信息
  -x   : 显示没有控制终端的进程,同时显示各个命令的具体路径
  
  -e   : 显示所有进程
  -f   : 显示程序间的关系
  # 查看系统进程   常用组合
  ps -aux    # 中括号是系统进程  [内核态进程]
  ps -ef
# 2 -aux 和 -ef之间的区别?
   ps -aux : 会打印出cpu、内存相关的使用情况
   
   ps -ef  : 会打印出PPID(父级PID)、带有UUID等
2.1.2 结果字段详解
# ps -aux 结果详解
eg: ps aux | head -5
USER   PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root     1  0.0  0.3 128400  7104 ?     Ss   8月12   0:05 /usr/lib/systemd/systemd
root     2  0.0  0.0      0     0 ?     S    8月12   0:00 [kthreadd]
root     4  0.0  0.0      0     0 ?     S<   8月12   0:00 [kworker/0:0H]
root     5  0.0  0.0      0     0 ?     S    8月12   0:01 [kworker/u256:0]
# 各字段详细解释
USER   运行进程的用户
PID    进程ID
%CPU   CPU占用率   
%MEM   内存占用率
    
VSZ    占用虚拟内存,单位:kb(killobytes)    进程预申请的内存
         VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大小,产生这个的可能性很多 
         比如内存映射,共享的动态库,或者向系统申请了更多的堆,都会扩展线性空间大小。
RSS    占用实际内存,单位:kb(killobytes)
          RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小
    
    
TTY     进程运行的终端    teletypewriter   n.电传打字机
        # 注:
          1.linux系统的真实终端,就是系统支持的多用户登录  默认只存在6个
            用tty命令查看,显示路径  /dev/tty1
          
          2.linux系统的伪终端 pts (pseudo-terminal slave),
            是通过SSH或者Telnet远程连接Linux系统 的虚拟终端
            用tty命令查看,显示路径  /dev/pts/0  
        # 若结果为 ?  表示为不在任何终端上运行的系统自带的进程
             
STAT    进程状态    man ps (/STATE)            
   R     正在运行
   
   S     可中断睡眠(Sleep),即在睡眠的过程中可以接收信号唤醒 ---> 执行的IO操作可以得到硬件设备的响应
   D     不可中断睡眠(Disk Sleep),即在睡眠的过程中不可以接收信号唤醒 ---> 执行的IO操作得不到硬件设备的响应
         # 注:硬盘太忙了,加载不过来,不给响应。 只能等待,进程都杀不死
         
   T     停止的进程 
   Z     僵尸进程
   X     死掉的进程  # 几乎看不见,因为死了就立即回收了
      
   # 以下通常都是附加修饰上面几个主状态的
   <     代表优先级较高的进程
   N     代表优先级较低的进程
            # nice命令  设置命令的执行优先级  从-20到19,值越高优先级越低  ***
              好心值  越好心,越排最后 ---> 欺负老好人
              nice -20 sleep 2000  &   # '&'表示后台运行该命令进程  
              
   s     表示包含子进程    
   +     表示是前台运行的进程组   无+号则表示后台运行的进程
 
   l     代表以线程的方式运行,即多线程
   |     代表多进程
    
START  进程的启动时间
TIME   进程占用CPU的总时间
COMMAND  进程 所执行的进程命令 或进程文件
         # 带[]号的代表内核态进程
         # 不带[]号的代表用户态进程
2.1.3 进程树、查看某用户进程
# 1 查看进程树    安装软件包 psmisc
pstree 参数  [进程id]  # 默认查看系统所有的进程树 名字
    -p  # 显示进程并显示pid 括号里为pid号
  
# 2 查看以某个用户开启的进程 
ps -aux | grep "用户名"
pgrep -lu "用户名"   # pgrep命令 查询出进程的pid   是ps 和 grep的组合命令
  -l 表示查询进程pid和名称  
  -u 查询指定某个用户开启的进程
# eg: 
ps -aux | grep [1]01
  101  21485  0.0  0.1   9688  2052 ?   S  Apr04  0:06 nginx: worker process
pgrep -lu 101
  21485 nginx
2.2 动态查看进程
2.2.1 top 基本语法
# top命令
  查看进程实时变化状态
  
  选项
  -d : 指定刷新的时间频率    单位为秒 s
  -p : 查看指定进程的信息
  -u : 查看指定用户的进程
  
  -n : 查看指定top次数的信息
  -b : 以批处理(batch)模式运行,直接将结果输出到文件
# eg:
top    # 默认 top命令是3秒刷新一次  
top -d 1  # 1秒刷新一次
top -d 1 -p 进程的pid  # 查看1秒刷新的具体某个进程
top -d 1 -p `pgrep nginx | head -1` 
top -d 1 -p `pgrep sshd | head -1`,33  # 查看sshd以及pid为33的进程
top -d 1 -u nginx  # 查看指定用户进程
top -b -n 2 > top.txt  # 将2次top信息写入到文件
2.2.2 结果内部命令
### top结果 内部命令
M   按内存使用  排序 # ***
P   按CPU使用  排序
N   以PID的大小 排序
<   排序字段 向左移  # 即:以 当前排序字段的左边一位 排序  ***
>   排序字段 向后移
R   对排序进行反转   # ***
f   自定义显示字段  再在选择的字段中 按空格勾选  # ***
l   显示所有CPU的负载      # 就是系统信息的第一行
1   展开显示cpu多核的信息  # 数字的1    ***
z   彩色显示
s   修改刷新频率            # ***
r   修改进程的优先级(Renice) # ***
    # nice值越小,优先级越高  -20到19
k   给某个进程发送信号  # 输入pid,再输入信号值  
    # eg: 9信号,即变成了强制关闭某个进程  kill -9 进程id 
h或? 帮助
q    退出top页面
2.2.3 结果字段详解
# 显示字段信息解释
### 第一部分: 系统整体统计信息
# 第一行: 时间和cpu平均负载
  当前系统时间 当前系统状态(up\down) 系统启动时间   登录的用户数量   1分钟负载  5分钟负载  15分钟负载
  load average: 0.86, 0.56, 0.78   # CPU 分别为1分钟、5分钟、15分钟的平均负载
  
# 第二行tasks: 当前运行的进程数目,分别为 进程总数、正在运行的、睡眠的、停止的、僵尸进程数
  
# 第三行%CPU(s): 总体CPU使用率和各个核心的使用情况  按1查看每个cpu具体状态
    us   用户态进程占用cpu时间的百分比
    sys  内核态进程占用cpu时间的百分比
    ni   优先被调度的进程占cpu时间的百分比
    id   cpu空闲的百分比
    wa   cpu等待io的百分比
    hi   硬件中断,处理硬件中断所占用CPU的时间
    si   软件中断,处理软件中断所占用CPU的时间
    st   被偷走的cpu   # 虚拟机占用物理CPU的时间
    
    # 拓展阅读 linux中断:https://www.cnblogs.com/linhaifeng/articles/13916102.html
    
# 第四行KiB Mem : 总体内存的使用情况,分别为 总内存、可用内存、已使用内存、缓存
# 第五行KiB Swap: 虚拟内存的使用情况,分别为 总内存、可用内存、已使用内存、可用内存(加上真实内存一起)
  
### 第二部分: 各进程的实时信息
# PR :Priority    优先级    其值越小越早被执行
# NI :Nice Value  nice值    
  两个都是查看进程的优先级,一般常用 NI  # nice值越小,优先级越高  从-20到19
  关系是 PR = NI+ 20
# VIRT:virtual memory usage   虚拟内存    理解为 进程预申请的内存空间大小
1.进程"需要的"虚拟内存大小,包括进程使用的库、代码、数据等
2.假如进程申请100m的内存,但实际只使用了10m,那么它会增长100m,而不是实际的使用量
# RES:resident memory usage   常驻内存    理解为 进程实际使用内存,其中包含与其他进程共享的内存
1.进程当前使用的内存大小,但不包括swap out
   当某进程向OS请求内存发现不足时,OS会把内存中暂时不用的数据交换出去,放在SWAP分区中,这个过程称为SWAP OUT
   当某进程又需要这些数据且OS发现还有空闲物理内存时,又会把SWAP分区中的数据交换回物理内存中,这个过程称为SWAP IN
2.包含其他进程的共享
3.如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
4.关于库占用内存的情况,它只统计加载的库文件所占内存大小
# SHR:shared memory    共享内存
1.除了自身进程的共享内存,也包括其他进程的共享内存
2.虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
3.计算某个进程所占的物理内存大小公式:RES – SHR  # ***
4.swap out后,它将会降下来,因为内存充裕了,大家就没必要合租内存了
# DATA        数据内存
1.数据占用的内存。如果top没有显示,按f键、然后用空格选中DATA项目、然后按q则可以显示出来
2.真正的该程序要求的数据空间,是真正在运行中要使用的
# 拓展阅读swap分区:https://www.cnblogs.com/linhaifeng/articles/13915855.html
CPU的平均负载
# 0 查看系统负载:  w
# 1 什么是平均负载?
是指单位时间内,系统处于可运行状态(R状态)和不可中断状态(D状态)的平均进程数,也就是平均活跃进程数
# PS: 
  1.平均负载与CPU使用率并没有直接关系
  2.平均负载其实就是单位时间内的活跃进程数   # ***
# 2 平均负载多少合理? 
假设现在分别在4、2、1核的CPU上,如果平均负载为2时,意味着什么呢?
------------------------------------------------
核心数      平均负载        含义
4             2         有一半(50%)的CPU是空闲状态
2             2         CPU刚好完全被占用
1             2         至少一半的进程是抢不到CPU
-------------------------------------------------
# 3 平均负载的三个数值我们该关注哪一个?
    三个值相当于三个样本,我们应该统筹地看
    # 注:单核cpu 负载超过0.7,cpu负载就比较高了
  
1.如果1分钟,5分钟,15分钟的负载数值相差不大,代表系统的负载很'稳定'
2.如果1分钟的值,远小于15分钟的值,那么证明系统的平均负载逐渐降低
  即我们的系统刚刚经历过大风浪,但目前已逐渐趋于平均。
  至于15分钟区间内,系统负载上升的原因,还需要我们认真查明
3.如果15分钟的值,远小于1分钟的值,那么证明系统的平均负载逐渐升高
  有可能是临时的,也有可能持续上升,需要观察
4.一旦1分钟的平均负载接近或超过了CPU的个数,就意味着系统正在发生过载的问题
  这时候就得分析问题了, 并且要想办法优化
  
  
# eg:假设我们在有2个 CPU 系统上看到平均负载为 2.73,6.90,12.98
即在过去 1 分钟内,系统有 136% 的超载 (2.73/2=136%)
而在过去 5 分钟内,有 345% 的超载 (6.90/2=345%)
而在过去 15分钟内,有 649% 的超载,(12.98/2=649%)
但从整体趋势来看,系统的负载是在逐步的降低
# 4 每秒查询系统负载 shell命令
while true ; do w ; sleep 1; clear; done  
# 5 补充:Linux系统工具
# 1 stress测试    模拟计算密集型  yum install stress -y
   stress是Linux系统压力测试工具,可用作异常进程模拟平均负载升高的场景
   stress --cpu 4 --timeout 3000  # 3000代表持续执行3000秒
# 2 mpstat    yum install sysstat -y
  mpstat是多核CPU性能分析工具,用来实时检查每个CPU的性能指标,以及所有CPU的平均指标
  mpstat -P ALL 3   # 3s输出一组所有指标
# 3 pidstat
  pidstat是一个常用的进程性能分析工具,用来实时查看进程的CPU,内存,IO,以及上下文切换等性能指标
  pidstat -u 1 5  # 1秒一次,总共输出5次
3 管理进程
3.1 设置进程优先级
### 1 启动进程时用nice命令设置设置优先级
# nice 命令
nice [-n <优先级>] [--help] [--version] [执行指令]
  选项
    -n <优先级>    指定优先级
                  # 若未指定优先级,则以缺省值10来调整程序运行优先级
                    既在当前程序运行优先级基础之上 增加10 
    --help        帮助信息
    --version     版本信息
# eg:让命令以新的优先级执行
nice -n 5 ls  # nice -n -20 命令
### 2 已运行的进程设置新的优先级
renice -20 进程pid
3.2 给进程发送信号
3.2.1 进程信号
# 1 查看所有进程支持的信号   是linux系统的标准信号,软件基本上是遵循的
kill -l  
  # 注:
    给进程发送信号,并不代表该进程一定会执行该信号,取决于软件的本身设置是否支持接受处理这些信号
# 2 常见信号: 重点:1、9、15 
# HUP (1):  1 挂起信号  2 可让进程重新加载配置   ***
本信号在用户终端连接(正常或非正常)结束时发出
通常是在终端的控制进程结束时, 通知同一session内的各个进程作业, 这时它们与控制终端不再关联
登录Linux时,系统会分配给登录用户一个终端(Session)
在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session
当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到HUP信号
该信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止
但可以捕获该信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也能继续下载
此外,对于与终端脱离关系的守护进程,该信号用于通知它重新读取配置文件
# INT(2):  中断  
通常因为按下ctrl+c而产生的信号,用于通知前台进程组终止进程
# QUIT(3): 退出
和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制
进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号
# TSTP(20): 停止进行运行
通常因为按下ctrl+z而产生的信号
# KILL (9):  立即终止   ***
用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略
# TERM (15): 正常退出  # kill的默认信号  ***
是kill默认发送的信号,终止进程,与9信号不同的是该信号可以被阻塞和处理,会保存进程相关的信息
通常用TERM信号来要求程序自己正常退出,如果进程终止不了,我们才会尝试SIGKILL
# CONT(18):  被暂停的进程将继续恢复运行
# STOP(19): 暂停进程
# CHLD: 子进程结束时,向父进程发送的信号
若父进程没有处理这个信号,也没有等待(wait)子进程
子进程虽然终止,但是还会在内核进程表中占有表项,此时的子进程称为僵尸进程
  # 避免僵尸进程的产生
  父进程捕捉它
  或忽略SIGCHILD信号
    或者wait它派生的子进程,
    或者父进程先终止,这时子进程的终止自动由init进程来接管
3.2.2 kill发送信号
### 1 进程重新加载配置运行
kill -1 进程id号
  # 对比更新配置文件后,进程重新启动的好处是 该进程并不会退出结束,进程pid并不会改变
### 2 关闭进程
# 2.1 正常关闭进程   默认
kill 进程id号  # <==> kill -15 进程号
  
  
# 2.2 强行关闭某进程
kill -9 进程id号 
# 2.3 关闭软件的所有进程
pkill -9 软件名         ps + kill 的组合命令
  eg:pkill -9 nginx  # 关闭nginx所有进程
    
killall 软件名
  eg: killall nginx   # 关闭nginx所有进程
  
  
# 2.4 暂停和恢复进程
kill -19 进程id号   # 暂停进程
kill -18 进程id号   # 恢复进程
3.3 后台运行进程
### 前提:   hup信号的第一个作用:挂起信号  HUP信号(hang up 挂断)信号
当网络断开 或者 终端直接关闭时   # 注:一定是终端整体叉掉关闭,不是单纯的exit 或 logout
Linux会向终端发送HUP信号,然后终端在结束前,会关闭其所有的子进程  
# 若想进程在后台一直运行,不会因为以上而关闭进程
方案原理1: 让进程忽略Linux的HUP信号
方案原理2: 让进程运行在新的会话里,从而成为不属于此终端的子进程
       就不会在当前终端挂掉的情况下一起被带走
       
       
# 故:强调注意!!!
使用xshell等远程连接linux终端,在关闭时 养成良好的行为习惯
1.exit   退出终端
2.logout 关闭连接
3.3.1 忽略HUP信号的方式
### 以下的几种方式原理都一样
  0、2、3 都是直接将原终端的进程 的父进程ppid 都变成1(init进程)
  而1 是等原终端关闭时,父进程ppid 才会变成1
### 0 正常退出 或 终端连接关闭
exit 或 logout  
### 1 nohup命令
nohup的用途就是让提交的命令忽略 hangup 信号,该命令通常与&符号一起使用
nohup命令会从终端解除进程的关联,进程会丢掉STDOUT、STDERR的链接
标准输出和标准错误 默认会被重定向到 nohup.out文件中
一般我们可在结尾加上"&"来将命令同时放入后台运行
也可用"&>filename "来更改默认的重定向文件名
1.在终端1 执行命令
nohup ping www.baidu.com &
nohup ping www.baidu.com &>/etc/null &
2.在终端2 查看进程信息 
ping -ef | grep [p]ing
  root  30302 29951  0 15:00 pts/0    00:00:00 ping www.baidu.co
3.直接关闭终端1
4.在终端2 继续查看进程信息 
ping -ef | grep [p]ing
  root  30302     1  0 15:00 ?        00:00:00 ping www.baidu.com
  
  发现后台运行的ping进程 依旧在运行
  并且它的父进程pid号 由原来的 29951 变成了1(即init进程)
  即让运行的进程归属于init的子进程,故init结束,该子进程才会结束
### 2 setsid命令
setsid原理与nohup是一样的,setsid是直接将进程的父pid设置成1
1.在终端2中执行命令
setsid ping www.baidu.com  # 也可以在后面加&符号
2.直接关闭终端2
3.在终端1中查看
ps -ef |grep [p]ing
  root  102335   1  0 17:53 ?   00:00:00 ping www.baidu.com
  
  
  
### 3 在子shell中提交命令
1.在终端2中执行命令
(ping www.baidu.com &)  # 用英文的括号,把命令括起来提交执行  提交的命令并不在作业列表中
2.关闭终端2
3.在终端1中查看
ps -ef |grep [p]ing
  root 102361   1  0 17:55 ?   00:00:00 ping www.baidu.com
3.3.2 screen管理后台进程  <推荐>
### 1 通过命令 最后加 '&', 表示后台运行该命令进程  (强烈不推荐,了解即可)
      且若该任务输出内容到标准输出中(eg: echo、ls、ping)
      即便使用了&,也会等待这些输出任务在前台运行完毕
nice -20 sleep 2000  &
ping www.baidu.com &   # 依旧会在前台打印传输信息
# eg:  jobs、bg、fg的使用
sleep 5000 &  # 运行程序(时),让其在后台执行
  [1] 31143
  
sleep 4000    # 再Ctrl+z,将前台的程序挂起(暂停)到后台
jobs   # 中括号内的编号就是作业编号,%1代表作业1
  [1]-  运行中    sleep 5000 &
  [2]+  已停止    sleep 4000
bg %2  # 让作业2在后台运行
  [2]+ sleep 4000 &
fg %1  # 将作业1调回到前台
jobs
  [2]+  运行中    sleep 4000 &
  
kill %2  # 将作业1关闭
### 2 后台运行进程: 通过screen 来管理进程    推荐使用***
# 原理分析: 同上面忽略hup信号的几种方式都一样,只是中间多了一个 screen程序 来管理后台的进程
screen程序会帮我们管理运行的命令,退出screen,我们的命令还会继续运行
若关闭screen所在的终端,则screen程序的ppid变为1,所以screen不会死掉,对应着它帮我们管理的命令也不会退出
# 操作方式
1.安装
yum install screen -y
2.创建一个screen窗口  指定名称
screen -S myjobs
3.screen窗口中执行耗时任务、进程
wget ...
4.后台运行任务   平滑退出screen 但任务还在后台运行
ctrl+a+d   # 注:exit 才是真正关闭screen窗口
5.查看所有的screen
screen -list   或 screen -ls
6.进入正在运行的screen 
screen -r myjobs  # 调到前台执行
7.终止screen
exit  或 Ctrl + d
# 其他参数
screen -x 终端会话名  # 会话共享演示
4 查看网络状态
# netstat 命令 查看网络状态
netstat -tnlp  # 查看正在监听的,且使用tcp协议的进程
  -a   显示所有连线中的Socket
  -t   tcp协议
  -u   udp协议
  -l   listen 显示监控中的服务器的Socket
  -p   PID/Program name 显示正在使用Socket的程序识别码和程序名称
  -n   直接使用IP地址,而不通过域名服务器
       不将IP地址解析为主机名,不将端口号解析成协议名(80 ---> http)
# eg: 常用
netstat -tunalp         # 常用 tunalp
netstat -an |grep :80   # 常用 an   windows上也可使用
lsof -i:22              # 查看端口22信息
5 僵尸与孤儿进程
5.1 僵尸进程
# 0 linux操作系统的设计规定:父进程应该具备随时获取子进程状态的能力 ***
如果子进程先于父进程运行完毕,此时若linux操作系统立刻把该子进程的所有资源全部释放掉
那么父进程来查看子进程状态时,会突然发现自己刚刚开启的子进程,找不到了
这就违背了linux操作系统的设计规定
# 1 什么是僵尸进程
操作系统负责管理进程,应用程序若想开启子进程,都是在向操作系统发送系统调用
当一个子进程开启起来以后,它的运行与父进程是异步的,彼此互不影响,谁先结束都不一定
若子进程先于父进程运行完毕/死掉,那么linux系统在清理子进程的时候
会将子进程占用的重型资源都释放掉 (eg:占用的内存空间、cpu资源、打开的文件等)
但是会保留一部分子进程的关键状态信息
eg: 进程号 the process ID
进程退出状态 the termination status of the process
进程运行时间 the amount of CPU time taken by the process等
此时子进程就相当于死了但是没死干净,因而得名"僵尸进程"
# 所有的子进程结束后都会进入僵尸进程的状态 ***
# 2 故问题来了,僵尸进程残存的那些数据不需要回收吗???
当然需要回收了,但是僵尸进程毕竟是linux系统出于好心,为父进程准备的数据
至于回收操作,应该是父进程觉得自己无需查看僵尸进程的数据了,觉得留着僵尸进程的数据也没啥用了
然后由父进程发起一个系统调用wait / waitpid来通知linux操作系统,清理僵尸进程的数据
然后操作系统再清理掉僵尸进程的残余状态
# 3 分三种情况讨论
1.linux系统自带的一些优秀的开源软件,这些软件在开启子进程时
父进程内部都会 及时调用wait/waitpid 来通知操作系统回收僵尸进程
所以,通常看不到优秀的开源软件堆积僵尸进程,因为很及时就回收了,与linux系统配合的很默契
2.一些水平良好的程序员开发的应用程序,这些程序员技术功底深厚
知道父进程要对子进程负责,会在父进程内考虑调用wait/waitpid来通知操作系统回收僵尸进程
但是发起系统调用wait/waitpid的时间可能慢了些
于是我们可以在linux系统中通过命令查看到僵尸进程状态 # ps aux | grep [Z]+
3.一些垃圾程序员,只知道开子进程,父进程也不结束,wait/waitpid的系统调用更是没听说过
这个时候,操作系统中会堆积很多僵尸进程,此时计算机会进入一个奇怪的现象
就是内存充足、硬盘充足、cpu空闲,但是,启动新的软件就是无法启动起来
为啥,因为操作系统负责管理进程,每启动一个进程就会分配一个pid号
而pid号是有限的,正常情况下pid也用不完,但怕就怕堆积一堆僵尸进程,他吃不了多少内存,但能吃一堆pid
# 4 如何手动清理僵尸进程
针对情况3,只有一种解决方案,就是杀死父进程  kill 父进程PID  
那么僵尸的子进程会被linux系统中pid为1的顶级进程(init或systemd)接管
顶级进程会定期发起系统调用wait/waitpid来通知操作系统清理僵尸
针对情况2,可以发送信号给父进程,通知它快点发起系统调用wait/waitpid来清理僵尸的儿子
kill -CHLD 父进程PID
5.2 孤儿进程
# 孤儿进程
父进程先死掉,而它的一个或多个子进程还在运行,这些子进程将成为孤儿进程
每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 进程号为1的顶级进程(init或systemd)
并由顶级进程对它们完成状态收集工作
顶级进程会循环地wait(),这些孤儿进程
# eg:
创建完子进程后,主进程所在的这个脚本就退出了
当父进程先于子进程结束时,子进程会被顶级进程收养,成为孤儿进程,而非僵尸进程
import os, sys, time
# 当前父进程的id  和 父进程的父进程 id
print('im father', 'pid', os.getpid(), 'ppid', os.getppid())
pid = os.fork()
# 执行pid=os.fork()则会生成一个子进程   linux系统才支持fork方法生成子进程
# 返回值pid有两种值:
#    如果返回的pid值为0,表示在子进程当中
#    如果返回的pid值>0,表示在父进程当中,此时pid是子进程的id
if pid == 0:
    # 保证主线程退出完毕
    time.sleep(1)
    print('im child', 'pid', os.getpid(), 'ppid', os.getppid())
else:
    print('father died..')
    sys.exit(0)
执行文件,输出结果:
im father pid 32515 ppid 32015
father died..
im child 32516 1
看子进程已经被pid为1的顶级进程接收了
所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已,孤儿进程声明周期结束自然会被顶级进程来销毁