进程管理下

详见egon博客:https://egonlin.com/?p=210

特殊进程

僵尸进程

  • 僵尸进程是操作系统的一种优化机制。
  • 一个进程死掉之后,会把其占用的cpu、内存资源都释放掉,但是会保留该进程的状态信息,例如:pid号、存在过的一些运行信息。
  • 这些保留下来的信息都是操作系统给父进程准备的。
  • 每个进程死掉之前都会进入僵尸进程的状态
  • 僵尸进程通常由父进程来回收
  • kill -CHLD 父进程PID

#1、什么是僵尸进程

操作系统负责管理进程

我们的应用程序若想开启子进程,都是在向操作系统发送系统调用

当一个子进程开启起来以后,它的运行与父进程是异步的,彼此互不影响,谁先死都不一定

linux操作系统的设计规定:父进程应该具备随时获取子进程状态的能力

如果子进程先于父进程运行完毕,此时若linux操作系统立刻把该子进程的所有资源全部释放掉,那么父进程来查看子进程状态时,会突然发现自己刚刚生了一个儿子,但是儿子没了!!!

这就违背了linux操作系统的设计规定

所以,linux系统出于好心,若子进程先于父进程运行完毕/死掉,那么linux系统在清理子进程的时候,会将子进程占用的重型资源都释放掉(比如占用的内存空间、cpu资源、打开的文件等),但是会保留一部分子进程的关键状态信息,比如进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等,此时子进程就相当于死了但是没死干净,因而得名”僵尸进程”,其实僵尸进程是linux操作系统出于好心,为父进程准备的一些子进程的状态数据,专门供父进程查阅,也就是说”僵尸进程”是linux系统的一种数据结构,所有的子进程结束后都会进入僵尸进程的状态

# 2、那么问题来了,僵尸进程残存的那些数据不需要回收吗???

当然需要回收了,但是僵尸进程毕竟是linux系统出于好心,为父进程准备的数据,至于回收操作,应该是父进程觉得自己无需查看僵尸进程的数据了,父进程觉得留着僵尸进程的数据也没啥用了,然后由父进程发起一个系统调用wait / waitpid来通知linux操作系统说:哥们,谢谢你为我保存着这些僵尸的子进程状态,我现在用不上他了,你可以把他们回收掉了。然后操作系统再清理掉僵尸进程的残余状态,你看,两者配合的非常默契,但是,怕就怕在。。。

# 3、分三种情况讨论

1、linux系统自带的一些优秀的开源软件,这些软件在开启子进程时,父进程内部都会及时调用wait/waitpid来通知操作系统回收僵尸进程,所以,我们通常看不到优秀的开源软件堆积僵尸进程,因为很及时就回收了,与linux系统配合的很默契

2、一些水平良好的程序员开发的应用程序,这些程序员技术功底深厚,知道父进程要对子进程负责,会在父进程内考虑调用wait/waitpid来通知操作系统回收僵尸进程,但是发起系统调用wait/waitpid的时间可能慢了些,于是我们可以在linux系统中通过命令查看到僵尸进程状态

[root@egon ~]# ps aux | grep [Z]+

3、一些垃圾程序员,技术非常垃圾,只知道开子进程,父进程也不结束,就在那傻不拉几地一直开子进程,也压根不知道啥叫僵尸进程,至于wait/waitpid的系统调用更是没听说过,这个时候,就真的垃圾了,操作系统中会堆积很多僵尸进程,此时我们的计算机会进入一个奇怪的现象,就是内存充足、硬盘充足、cpu空闲,但是,启动新的软件就是无法启动起来,为啥,因为操作系统负责管理进程,每启动一个进程就会分配一个pid号,而pid号是有限的,正常情况下pid也用不完,但怕就怕堆积一堆僵尸进程,他吃不了多少内存,但能吃一堆pid

# 4、如果清理僵尸进程

针对情况3,只有一种解决方案,就是杀死父进程,那么僵尸的子进程会被linux系统中pid为1的顶级进程(init或systemd)接管,顶级进程会定期发起系统调用wait/waitpid来通知操作系统清理僵尸

针对情况2,可以发送信号给父进程,通知它快点发起系统调用wait/waitpid来清理僵尸的儿子

kill -CHLD 父进程PID

# 5、结语

僵尸进程是linux系统出于好心设计的一种数据结构,一个子进程死掉后,相当于操作系统出于好心帮它的爸爸保存它的遗体,之说以会在某种场景下有害,是因为它的爸爸不靠谱,儿子死了,也不及时收尸(发起系统调用让操作系统收尸)

说白了,僵尸进程本身无害,有害的是那些水平不足的程序员,他们总是喜欢写bug,好吧,如果你想看看垃圾程序员是如何写bug来堆积僵尸进程的,你可以看一下这篇博客https://www.cnblogs.com/linhaifeng/articles/13567273.html

孤儿进程

父进程先死掉,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被进程号为1的顶级进程(init或systemd)所收养,并由顶级进程对它们完成状态收集工作。
进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为顶级进程,而顶级进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,顶级进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
 
我们来测试一下(创建完子进程后,主进程所在的这个脚本就退出了,当父进程先于子进程结束时,子进程会被顶级进程收养,成为孤儿进程,而非僵尸进程),文件内容
 
import os
import sys
import time
 
pid = os.getpid()
ppid = os.getppid()
print 'im father', 'pid', pid, 'ppid', ppid
pid = os.fork()
#执行pid=os.fork()则会生成一个子进程
#返回值pid有两种值:
#    如果返回的pid值为0,表示在子进程当中
#    如果返回的pid值>0,表示在父进程当中
if pid > 0:
    print 'father died..'
    sys.exit(0)
 
# 保证主线程退出完毕
time.sleep(1)
print 'im child', os.getpid(), os.getppid()
 
执行文件,输出结果:
im father pid 32515 ppid 32015
father died..
im child 32516 1
 
看,子进程已经被pid为1的顶级进程接收了,所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已,孤儿进程声明周期结束自然会被顶级进程来销毁。

守护进程

守护进程(Daemon process)是在Unix和类Unix(如Linux)操作系统中,一种在后台运行的特殊进程。这类进程通常并不通过交互式终端控制,而是在系统启动后自动运行,进行一些系统管理任务。至于”守护”这两个字,它取自英文”daemon”,在计算机科学中,“daemon”是希腊神话中的神灵和鬼神的意思,指的是那些在后台默默工作的服务进程。

以下几点是守护进程的一些主要特征:

1、守护进程在后台运行,通常不需要与用户交互。

2、守护进程通常长时间运行,直到系统关闭。

3、守护进程没有控制终端,也不拥有标准输入设备,标准输出设备或标准错误设备。

守护进程与普通进程的区别主要体现在:

1、生命周期:普通进程通常由用户启动,任务完成后结束;而守护进程在系统启动后自动运行,直到系统关闭。

2、控制:普通进程通常有控制终端,可以直接与用户交互;而守护进程没有控制终端,通常通过系统日志、配置文件等方式进行管理和配置。

管理进程

优先级设置(nice值)

# 0、储备知识
一个进程在运行的时候会涉及到内核态与用户态的转换
关于进程的优先级,top命令查看到两个相关的值PR与NI
PR-》内核态使用的值
NI-》用户态使用的值

系统调度器最终是根据PR的值来决定进程的运行顺序
PR值越小优先级越高
PR值=20+NICE值 # NICE值的范围为-20到+19

用户无法修改内核态的PR值,但是
我们可用nice命令修改用户态得NI值,来影响内核态的PR值

# 1、命令
nice [-n <优先级>] [--help] [--version] [执行指令]

# 2、选项介绍:
若 nice命令未指定优先级的调整值,则以缺省值10来调整程序运行优先级,既在当前程序运行优先级基础之上增加10。 
-n <优先级>    指定优先级;
--help        帮助信息;
--version      版本信息;

# 3、执行范例:让命令以新的优先级执行
[root@localhost ~]# $ nice -n 5 ls  # nice -n -20 命令

# 4、补充:ps -l 命令
其中的几个重要信息有:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值

PRI即进程的优先级,此值越小进程的优先级别越高。而NI,也就是我们所要说的nice值(通过nice命令设置),其表示进程可被执行的优先级的修正数值。如前面所说,PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。
所以,nice命令设置的优先级不是程序最终的优先级,而只是优先级的修正数值。
renice命令允许用户修改一个正在运行的进程的优先权。

重新设置nice值
[root@localhost ~]# renice -20 pid号

给进程发信号(kill命令)

[root@localhost ~]# kill -l # 列出所有支持的信号

=====================解释==========================

# HUP(1): 该信号

暂时忽略,后续作为重点详细介绍

# INT(2): 中断, 通常因为按下ctrl+c而产生的信号,用于通知前台进程组终止进程。

# QUIT(3): 退出,和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。

# KILL (9):杀死进程,不可以被捕获、忽略和阻塞,如果管理员发现某个进程终止不了,可尝试发送这个信号。

# TERM(15): 杀死进程,可以被捕获、忽略和阻塞,通常用TERM信号来要求程序自己正常退出,如果进程终止不了,我们才会尝试SIGKILL。程序可以定制化处理该信号。

# CONT(18) 被暂停的进程将继续恢复运行

# SIGSTOP(19) 暂停进程,该信号不能被捕获、忽略和阻塞

# TSTP(20): 也是暂停进程,也可以用键盘按下ctrl+z产生该信号。与SIGSTOP不同的是,该信号可以被捕获、忽略和阻塞

# SIGCHLD

子进程结束时, 父进程会收到这个信号。

如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸 进程。

这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,

这时子进程的终止自动由init进程 来接管)。

# 更多详见:man 7 signal

补充1:用进程名杀进程

[root@egon ~]# vim egon.txt &

[root@egon ~]# vim egon.txt &

[root@egon ~]# vim egon.txt &

[root@egon ~]# killall -9 vim

[root@egon ~]# pkill -9 vim

补充2:查看某个用户开启的进程

pgrep -l -u 用户名

管理后台进程

# 准备脚本文件
#!/bin/bash
 
for i in `seq 1 1000000`;
do
    sleep 1
    echo $i >> /tmp/run.log
done
 
[root@localhost ~]# sh a.sh  # ^z,将前台的程序挂起(暂停)到后台
[root@localhost ~]# jobs  # 查看后台的进程,中括号内的编号就是作业编号,%1代表作业1
[1]+  Stopped                 sh a.sh
 
[root@localhost ~]# bg %1  # 然后后台的作业,直接在后台运行
[root@localhost ~]# fg %1  # 将作业1调回到前台
[root@localhost ~]# sh a.sh & # 让任务在后台运行
[1] 61486
[root@localhost ~]# sh a.sh & # 让任务在后台运行
[2] 61489
[root@localhost ~]# jobs     # 查看后台的任务
[1]- Running sh a.sh &
[2]+ Running sh a.sh &
[root@localhost ~]# kill %1
[root@localhost ~]# kill %2

hup信号

HUP信号,主要涉及两种应用场景:

1、终端关闭时,系统会向与该终端相连的所有进程发送SIGHUP信号,这会导致这些进程被关闭(所有才有了脱离终端运行的需求)

2、用killl -1 命令给进程发送SIGHUP信号实现该进程的平滑重启

需要注意的是,并非所有的进程或应用都能处理SIGHUP信号,只有程序内写过专门的捕获并处理该信号的代码才行,nginx的源代码里就写了,并且会响应该信号来实现平滑重启

脱离终端运行来规避hup信号影响

1 nohup命令

比如:ping baidu.com & 这里加and符号后台运行,输出到nohup.out

2 setsid命令

针对方案1,我们还可以用setsid命令实现,原理与3.1是一样的,Setid是直接将进程的父pid设置成1,即让运行的进程归属于init的子进程,那么除非init结束,该子进程才会结束,当前进程所在的终端结束后并不会影响进程的运行

# 1、在终端2中执行命令
[root@egon ~]# setsid ping www.baidu.com  # 也可以在后面加&符号
 
# 2、关闭终端2
 
# 3、在终端1中查看
[root@egon ~]# ps -ef |grep [p]ing
root     102335      1  0 17:53 ?        00:00:00 ping www.baidu.com

3 在子shell中提交任务

# 1、在终端2中执行命令

[root@egon ~]# (ping www.baidu.com &)  # 提交的作业并不在作业列表中
 
# 2、关闭终端2
 
# 3、在终端1中查看
[root@egon ~]# ps -ef |grep [p]ing
root     102361      1  0 17:55 ?        00:00:00 ping www.baidu.com
 
可以看到新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的Linux HUP信号的影响了。

用hup信号实现平滑重启

1、什么是平滑重启:平滑重启是一种特殊的重启方式,它允许系统或服务在不中断对现有用户服务的前提下,实现新的代码部署或配置的更新。

2、为何要平滑重启:在日常的系统维护和开发中,我们需要更新代码或者修改系统配置,这就需要重启服务。但是,如果我们直接重启,当前的用户请求就会被中断。平滑重启可以做到在不影响用户的情况下,完成这些工作。这对于需要长期稳定运行的系统尤其重要。

3、如何实现平滑重启:在Linux系统上实现平滑重启,首先需要开发者在程序代码中处理好信号的接收和对应操作,例如nginx做了处理

再次强调,平滑重启并不是所有应用都可以的,它需要应用支持对应的信号处理和新旧进程的管理。

一般的步骤如下:

1、服务主进程收到重启信号,如SIGHUP。

2、主进程启动新的子进程加载更新后的程序或者配置,新的子进程开始接受新的请求。

3、主进程关闭旧的子进程的新连接接收,让它们仅处理完已存在的请求。

4、旧的子进程处理完请求后退出,实现了平滑重启。

例如,在Nginx中,可以通过kill-HUP命令实现平滑重启,其中是Nginx主进程的进程ID。当Nginx主进程接收到HuP信号后,它会平滑地关闭旧的worker进程并启动新的worker进程。

查看网络状态

网络访问多,高并发的时候,cpu,内存,硬盘都会被占用大量资源。客户端进来会占用进程,占用文件描述符。然后文件读数据需要消耗硬盘,加载到内存需要消耗内存,运行程序需要消耗cpu。

[root@tianyun ~]# netstat -tunalp # 查看正在监听的,且使用tcp和udp协议的进程
-t tcp协议
-u udp协议
-l listen
-p PID/Program name
-n 不反解,不将IP地址解析为主机名,不将端口号解析成协议名(80 —> http)

-a 全部信息

[root@egon ~]# netstat -an |grep :22

[root@egon ~]# netstat -an |grep :80

[root@egon ~]# lsof -i:22

proc文件系统

top命令统计的cpu状态信息就是从/proc/stat中取,系统所有进程的运行状态都在/proc下

看cpu

[root@localhost ~]# grep "processor" /proc/cpuinfo   # 逻辑cpu个数
processor   : 0
[root@localhost ~]# grep "physical id" /proc/cpuinfo # 物理cpu个数
[root@localhost ~]# grep "cpu cores" /proc/cpuinfo  # 每个cpu的核数
cpu cores   : 1
 
[root@localhost ~]# cat /proc/cpuinfo 
==flags
lm(64位)
vmx 支持虚拟化 Intel
svm 支持虚拟化 AMD
[root@localhost ~]# egrep --color 'lm|vmx|svm' /proc/cpuinfo 
[root@localhost ~]# lscpu

看内存

# 查看内存
[root@egon ~]# less /proc/meminfo
[root@egon ~]# free -wm
              total        used        free      shared     buffers       cache   available
Mem:           1980         192        1713           9           0          74        1671
Swap:          1023           0        1023
[root@egon ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:           1980         192        1713           9          74        1672
Swap:          1023           0        1023
 
需要注意的是
free表示的是当前完全没有被程序使用的内存;
而cache在有需要时,是可以被释放出来以供其它进程使用的(当然,并不是所有cache都可以释放,比如当前被用作ramfs的内存)。
而available才是真正表明系统目前可以提供给新启动的应用程序使用的内存。
/proc/meminfo从3.14内核版本开始提供MemAvailable的值;在2.6.27~3.14版本之间,是free程序自己计算available的值;早于2.6.27版本,available的值则同free一样。
[root@egon ~]# man free # 看一眼写的清清楚楚
 
# 释放内存举例:    
[root@egon ~]# free 
              total        used        free      shared  buff/cache   available
Mem:        2027876      208392      870612       10236      948872     1649684
Swap:       1048572         264     1048308
[root@egon ~]# echo 3 > /proc/sys/vm/drop_caches 
[root@egon ~]# free 
              total        used        free      shared  buff/cache   available
Mem:        2027876      194788     1765520       10236       67568     1718768
Swap:       1048572         264     1048308
[root@egon ~]# 

看内核启动参数

[root@localhost ~]# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.10.0-1127.13.1.el7.x86_64 root=UUID=84b5cfa6-b0dc-4d7a-a8fd-0302f0eb2f04 ro rhgb quiet LANG=zh_CN.UTF-8
 
[root@localhost ~]# uptime
 17:42:40 up 1 day,  1:33,  2 users,  load average: 0.00, 0.01, 0.05

卸载 /proc

[root@localhost ~]# umount /proc -l
 
# 下述命令都不可用
free -m
uptime
lscpu
top
 
# 重新挂载
[root@localhost ~]# mount -t proc proc /proc/
-t proc     指定文件系统的类型
proc        文件系统,虚拟文件系统
/proc       挂载点

管道

进程间的通信需要用管道 ‘xxx | xxx’, 主要用来连接左右两个命令,将左侧的命令的标准输出,交给右侧命令的标准输入

几个常用方法

统计当前/etc/passwd中用户使用的shell类型

[root@localhost ~]# awk -F: ‘{print $7}’ /etc/passwd | sort |uniq -c

2 /bin/bash

1 /bin/sync

1 /sbin/halt

40 /sbin/nologin

1 /sbin/shutdown

统计网站的访问情况

[root@localhost ~]# netstat -an |grep :80 |awk -F”:” ‘{print $8}’|sort |uniq -c

打印当前所有IP

[root@localhost ~]# ip addr |grep ‘inet ‘ |awk ‘{print $2}’ |awk -F”/” ‘{print $1}’

127.0.0.1

192.168.12.21

192.168.122.1

打印根分区已用空间的百分比(仅打印数字)

[root@localhost ~]# df -P|grep ‘/$’ |awk ‘{print $5}’|awk -F”%” ‘{print $1}’

50

统计网站的访问最多的iptop10

#思路: 打印所有访问的过来的ip | 排序 | 去重 | 倒序排序 | 取前10

[root@egon ~]# awk ‘{print $1}’ access.log |sort |uniq -c |sort -rn|head

12049 58.220.223.62

10856 112.64.171.98

1982 114.83.184.139

1662 117.136.66.10

1318 115.29.245.13

961 223.104.5.197

957 116.216.0.60

939 180.111.48.14

871 223.104.5.202

869 223.104.4.139

tee 命令,追加进一个文件

[root@egon ~]# ip address |grep ‘inet ‘ |awk -F”/” ‘{print $1}’ |awk ‘{print $2}’ |tee ip.txt

127.0.0.1

10.0.0.41

172.16.1.41

10.8.0.1

[root@egon ~]# cat ip.txt

127.0.0.1

10.0.0.41

172.16.1.41

10.8.0.1

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇