Linux进程管理

Linux进程管理

一 进程介绍

程序:存放代码的文件=》静态
进程:程序的运行过程=》动态

同一个程序可能对应多个进程
父进程:程序运行时产生的第一个进程
子进程:由父进程衍生fork()出来的进程

注意:如果父进程终止,子进程也会随之被终止

[root@localhost yum.repos.d]# yum install nginx -y
[root@localhost yum.repos.d]# systemctl start nginx
[root@localhost yum.repos.d]# ps aux |grep nginx
root      27482  0.0  0.1 120896  2096 ?        Ss   16:50   0:00 nginx: master process /usr/sbin/nginx
nginx     27483  0.0  0.1 123364  3544 ?        S    16:50   0:00 nginx: worker process
root      27500  0.0  0.0 112724   988 pts/0    S+   16:50   0:00 grep --color=auto nginx

进程状态(R、S、D、T、Z、X)

#1、进程概念:
1)正在执行的程序
2)正在计算机上执行的程序实例
3)能分配处理器并由处理器执行的实体
进程的两个基本元素是程序代码和代码相关联的数据集。进程是一种动态描述,但并不代表所有的进程都在运行。这就可以引入‘进程状态’。
进程在内存中因策会略或调度需求,
会处于各种状态:

#2、Linux下的进程状态:
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
# R- -可执行状态(运行状态)
只有在运行状态的进程才有可能在CPU上运行,注意是可能,并不意味着进程一定在运行中。同一时刻可能有多个进程处在可执行状态,这些进程的PCB(进程控制块)被放入对应CPU的可执行队列中。然后进程调度器从各个可执行队列中分别选择一个进程在CPU上运行。另外如果计算机只有一个处理器,那么一次最多只有一个进程处于这种状态。

# S- -可中断睡眠状态(sleeping)
处在这个状态意味着进程在等待事件完成。这些进程的PCB(task_struct结构)被放入对应时间的等待队列中。然后等待的事件发生时,对应的进程将被唤醒。

# D- -不可中断睡眠(disk sleep)
在这个状态的进程通常会等待IO的结束。
这个状态与sleeping状态相似,处于睡眠状态,但是此刻进程是不可中断的,意思是不响应异步信号。
另外你会发现处在D状态的进程kill -9竟然也杀不死。这就相当于我们怎么也叫不醒一个装睡的人。

# T- -暂停状态
给进程发送一个SIGSTOP信号,进程就会响应信号进入T状态,除非该进程正处在D状态。
再通过发送SIGCONT信号让进程继续运行。
kill -SIGSTOP
kill -SIGCONT

# Z- -僵死状态
僵死状态是一个比较特殊的状态。进程在退出的过程中,处于TASK_DEAD状态。
在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸。
X- -死亡状态或退出状态(dead)
死亡状态是内核运⾏ kernel/exit.c ⾥的 do_exit() 函数返回的状态。这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态

进程状态切换

进程在运行中不断的改变运行状态;
1)就绪状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
2)执行(Running)状态
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态
3) 阻塞(Blocked)状态
正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。

就绪–>执行
处在就绪状态的进程,当调度器为其分配了处理机后,就变成了执行状态。

执行–>就绪
执行状态的进程在其执行过程中,时间片跑完了不得不让出处理机,于是从执行变成就绪状态。

执行–>阻塞
正在执行的进程等待某种事件而无法继续执行时,便从执行状态变成阻塞状态。

阻塞–>就绪
处在阻塞状态的进程,如果等待的时间发生,则从阻塞状态转变成就绪状态。

二 查看进程

  • ps aux是常用组合
-a         #显示一个终端的所有进程

-u         #选择有效的用户id或者是用户名

-x         #显示没有控制终端的进程,同时显示各个命令的具体路径。
  • 示例:
[root@localhost ~]# ps aux |head -5
USER        PID %
CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.3 128400  7104 ?        Ss   812   0:05 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root          2  0.0  0.0      0     0 ?        S    812   0:00 [kthreadd]
root          4  0.0  0.0      0     0 ?        S<   812   0:00 [kworker/0:0H]
root          5  0.0  0.0      0     0 ?        S    812   0:01 [kworker/u256:0]
  • 显示结果解释:
USER: 	 运行进程的用户
    
PID: 	进程ID

%CPU: 	 CPU占用率
    
%MEM:    内存占用率
    
VSZ:	占用虚拟内存,单位:kb(killobytes)
         VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大小,产生这个的可能性很多 
         比如内存映射,共享的动态库,或者向系统申请了更多的堆,都会扩展线性空间大小。

RSS:  	占用实际内存,单位:kb(killobytes)
        RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小
    
TTY: 	进程运行的终端

STAT:	进程状态	 man ps (/STATE)			
      R 	运行
      S 	可中断睡眠 Sleep,即在睡眠的过程中可以接收信号唤醒=》执行的IO操作可以得到硬件设备的响应
      D		不可中断睡眠,即在睡眠的过程中不可以接收信号唤醒=》执行的IO操作得不到硬件设备的响应
      T 	停止的进程 
      Z 	僵尸进程
      X 	死掉的进程(几乎看不见,因为死了就立即回收了)
      
      < 	标注了<小于号代表优先级较高的进程
	  N  	N代表优先级较低的进程
	 
      s  	包含子进程
	  
	  +		+表示是前台的进程组
    
	  l	  	小写字母l,代表以线程的方式运行,即多线程
      | 	管道符号代表多进程
    
START:	  进程的启动时间
TIME:    进程占用CPU的总时间

COMMAND: 进程文件,进程名
		  带[]号的代表内核态进程
          不带[]号的代表用户态进程
  • Linux进程有两种睡眠状态
# 1、Interruptible Sleep(可中断睡眠,在ps命令中显示“S”)
处在这种睡眠状态的进程是可以通过给它发送signal来唤醒的,比如发HUP信号给nginx的master进程可以让nginx重新加载配置文件而不需要重新启动nginx进程;

# 2、Uninterruptible Sleep(不可中断睡眠,在ps命令中显示“D”)
处在这种状态的进程不接受外来的任何signal,这也是为什么之前我无法用kill杀掉这些处于D状态的进程,无论是“kill”、“kill -9”、“kill -15”还是按 Ctrl+C 、Ctrl+Z 都无济于,因为它们压根儿就不受这些信号的支配。

# 解释
进程为什么会被置于D状态呢?处于uninterruptible sleep状态的进程通常是在等待IO,比如磁盘IO,网络IO,其他外设IO,如果进程正在等待的IO在较长的时间内都没有响应,那么就很会不幸地被ps看到了,同时也就意味着很有可能有IO出了问题,可能是外设本身出了故障,也可能是比如NFS挂载的远程文件系统已经不可访问了。

正是因为得不到IO的响应,进程才进入了uninterruptible sleep状态,所以要想使进程从uninterruptible sleep状态恢复,就得使进程等待的IO恢复,比如如果是因为从远程挂载的NFS卷不可访问导致进程进入uninterruptible sleep状态的,那么可以通过恢复该NFS卷的连接来使进程的IO请求得到满足,除此之外,要想干掉处在D状态进程就只能重启整个Linux系统了(恐怖的D状态)。

看到有人说如果要想杀掉D状态的进程,通常可以去杀掉它的父进程(通常是shell,我理解的这种情况是在shell下直接运行的该进程,之后该进 程转入了D状态),于是我就照做了,之后就出现了上面的状态:他们的父进程被杀掉了,但是他们的父进程PID都变成了1,也就是init进程,这下可如何是好?此时我这些D状态的进程已经影响到其他一些进程的运行,而已经无法访问的NFS卷又在段时间内无法恢复,那么,只好重新启动了。
# 强调
D与Z状态的进程都无法用kill -9杀死
  • 示例1
# 1、在窗口1执行命令
[root@egon ~]# vim egon.txt

# 2、在窗口2查看vim的运行状态为:S+
[root@egon ~]# ps aux |grep [v]im
root     103231  1.5  0.2 149828  5460 pts/2    S+   17:48   0:00 vim egon.txt

# 3、在窗口1执行:ctrl+z,将进程放置到后台
[root@egon ~]# vim egon.txt

[4]+  已停止               vim egon.txt

# 4、在窗口2查看vim的运行状态为:T
[root@egon ~]# ps aux |grep [v]im
root     103231  0.3  0.2 149828  5460 pts/2    T    17:48   0:00 vim egon.txt
  • 示例2:查看S+、R+、D+
# 1、在窗口1执行命令
[root@egon ~]# tar cvzf egon.tar.gz /etc/ /usr/ /var/ /usr/

# 2、在窗口2查看tar的状态:S+、R+、D+
[root@egon ~]# while true;do ps aux |grep [t]ar;sleep 0.3;clear;done
  • 查看进程树
[root@egon ~]# rpm -qf `which pstree`
psmisc-22.20-16.el7.x86_64
[root@egon ~]# pstree
[root@egon ~]# pstree 104239
bash───bash───bash───bash───pstree
  • 查看ppid
[root@egon ~]# ps -ef | head -10
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 1101 ?      00:00:07 /usr/lib/systemd/systemd --system --deserialize 20
root          2      0  0 1101 ?      00:00:00 [kthreadd]
root          4      2  0 1101 ?      00:00:00 [kworker/0:0H]
root          6      2  0 1101 ?      00:00:06 [ksoftirqd/0]

  • 动态查看
# 一 基本用法
[root@localhost ~]# top
[root@localhost ~]# top -d 1  # 1秒刷新一次
[root@localhost ~]# top -d 1 -p 进程的pid
[root@localhost ~]# top -d 1 -p `pgrep nginx | head -1`
[root@localhost ~]# top -d 1 -p `pgrep sshd | head -1`,33  # 查看sshd以及pid为33的进程
[root@localhost ~]# top -d 1 -u nginx  # 查看指定用户进程
[root@localhost ~]# top -b -n 2 > top.txt  # 将2次top信息写入到文件

# 二 显示信息解释
第一部分:系统整体统计信息
up左边的代表当前的时间
up右边代表运行了多长时间
load average: 0.86, 0.56, 0.78	  CPU 1分钟,5分钟,15分钟平均负载
  
us 用户态进程占用cpu时间的百分比
sys 内核态进程占用cpu时间的百分比
ni 代表优先被调度的进程占cpu时间的百分比
id cpu空闲的百分比
wa cpu等待io的百分比
hi 硬件中断,处理硬件中断所占用CPU的时间
si 软件中断,处理软件中断所占用CPU的时间
st 被偷走的cpu

linux中断:https://www.cnblogs.com/linhaifeng/articles/13916102.html
平均负载解释如下:
'''
===========>什么是平均负载?
平均负载是指,单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数

===========>平均负载多少合理? 
假设现在在4,2,1核的CPU上,如果平均负载为2时,意味着什么呢?
------------------------------------------------
核心数      平均负载        含义
4             2         有一半(50%)的CPU是空闲状态
2             2         CPU刚好完全被占用
1             2         至少一半的进程是抢不到CPU
-------------------------------------------------

===========>平均负载的三个数值我们该关注哪一个?
三个值相当于三个样本,我们应该统筹地看
如果1分钟,5分钟,15分钟的负载数值相差不大,代表系统的负载很'稳定'
如果1分钟的值,远小于15分钟的值,那么证明系统的平均负载逐渐降低,即我们的系统刚刚经历过大风浪,但目前已逐渐趋于平均。至于15分钟区间内,系统负载上升的原因,还需要我们认真查明
如果15分钟的值,远小于1分钟的值,那么证明系统的平均负载逐渐升高,有可能是临时的也有可能持续上升,需要观察
一旦1分钟的平均负载接近或超过了CPU的个数,就意味着,系统正在发生过载的问题,这时候就得分析问题了, 并且要想办法优化。

==========>平均负载实验:4个CPU跑满
[root@egon ~]# cat /proc/cpuinfo  | grep processor
processor	: 0
processor	: 1
processor	: 2
processor	: 3
打开窗口1:执行top命令,然后按1,观察四个核的id几乎为100%,然后在窗口2执行命令观察负载情况
[root@egon ~]# top
[root@egon ~]# 按1

打开窗口2:依次执行下述命令,然后在窗口来观察变化
[root@egon ~]# while true;do ((1+1));done &
[root@egon ~]# while true;do ((1+1));done &
[root@egon ~]# while true;do ((1+1));done &
[root@egon ~]# while true;do ((1+1));done &

用ps aux | grep bash会看到一系列R的bash进程,然后一个个杀掉,观察cpu的负载逐步恢复平静

思考:如果把测试命令换成下述命令,一直连续执行n次,cpu负载都不会很高,为什么??
[root@egon ~]# while true;do ((1+1));sleep 0.1;done &  
[root@egon ~]# while true;do ((1+1));sleep 0.1;done &  
[root@egon ~]# while true;do ((1+1));sleep 0.1;done &  
。。。执行好多次


补充1:也可以使用stress工具来取代上述的while命令
stress是Linux系统压力测试工具,可用作异常进程模拟平均负载升高的场景,需要安装yum install stress -y
[root@egon ~]# stress --cpu 4 --timeout 3000  # 3000代表持续执行3000秒

补充2:安装yum install sysstat -y会得到下述两个命令
mpstat 是多核CPU性能分析工具,用来实时检查每个CPU的性能指标,以及所有CPU的平均指标。
[root@egon ~]# mpstat -P ALL 3  # 3s输出一组所有指标

pidstat 是一个常用的进程性能分析工具,用来实时查看进程的CPU,内存,IO,以及上下文切换等性能指标。
[root@egon ~]# pidstat -u 1 5  # 1秒一次,总共输出5次
'''
    
第二部分:进程信息
top 命令 VSZ,RSS,TTY,STAT, VIRT,RES,SHR,DATA的含义
====================================================
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、真正的该程序要求的数据空间,是真正在运行中要使用的。

# 三 top 运行中可以通过 top 的内部命令对进程的显示方式进行控制。内部命令如下:
命令
M	按内存的使用排序
P	按CPU使用排序
N	以PID的大小排序
R	对排序进行反转
f	自定义显示字段
1	显示所有CPU的负载
s   改变画面更新频率

h|?帮助
<	向前
>	向后
z	彩色


# 四:调整进程的优先级:
1、r	调整进程的优先级(Nice Level) 
优先级的数值为-20~19,其中数值越小优先级越高,数值越大优先级越低,-20的优先级最高,19的优先级最低。
需要注意的是普通用户只能在019之间调整应用程序的优先权值,只有超级用户有权调整更高的优先权值(从-2019)。
    
2、k	给进程发送信号 1,2(^C),9,15,18,19(^Z)

# 五:更多内部命令
l – 关闭或开启第一部分第一行 top 信息的表示
t – 关闭或开启第一部分第二行 Tasks 和第三行 Cpus 信息的表示
m – 关闭或开启第一部分第四行 Mem 和 第五行 Swap 信息的表示
N – 以 PID 的大小的顺序排列表示进程列表
P – 以 CPU 占用率大小的顺序排列进程列表
M – 以内存占用率大小的顺序排列进程列表
h – 显示帮助
n – 设置在进程列表所显示进程的数量
q – 退出 top

序号 列名 含义
a PID 进程id
b PPID 父进程id
c RUSER Real user name
d UID 进程所有者的用户id
e USER 进程所有者的用户名
f GROUP 进程所有者的组名
g TTY 启动进程的终端名。不是从终端启动的进程则显示为 ?
h PR 优先级
i NI nice值。负值表示高优先级,正值表示低优先级
j P 最后使用的CPU,仅在多CPU环境下有意义
k %CPU 上次更新到现在的CPU时间占用百分比
l TIME 进程使用的CPU时间总计,单位秒
m TIME+ 进程使用的CPU时间总计,单位1/100秒
n %MEM 进程使用的物理内存百分比
o VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
p SWAP 进程使用的虚拟内存中,被换出的大小,单位kb。
q RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
r CODE 可执行代码占用的物理内存大小,单位kb
s DATA 可执行代码以外的部分(数据段+)占用的物理内存大小,单位kb
t SHR 共享内存大小,单位kb
u nFLT 页面错误次数
v nDRT 最后一次写入到现在,被修改过的页面数。
w S 进程状态。(D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)
x COMMAND 命令名/命令行
y WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名
z Flags 任务标志,参考 sched.h

默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。

通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。
按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z 可以将相应的列向左移动。最后按回车键确定。
按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转。

# 六 拓展阅读swap分区:https://www.cnblogs.com/linhaifeng/articles/13915855.html

三 shell管理进程

3.1 优先级设置

可以在启动进程时用nice命令设置设置优先级,

# 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命令允许用户修改一个正在运行的进程的优先权。
  • 对已运行的进程设置新的优先级
[root@localhost ~]# renice -20 11111

3.2 给进程发送信号

[root@localhost ~]# kill -l  # 列出所有支持的信号
=====================解释==========================
# HUP(1):  1、挂起信号 2、往往可以让进程重新加载配置
本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。

登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都 属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进 程组和后台有终端输出的进程就会中止。不过,可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也 能继续下载。

此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。

# INT(2):  中断, 通常因为按下ctrl+c而产生的信号,用于通知前台进程组终止进程。
# QUIT(3): 退出,和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。

# TSTP(20): 停止进行运行,通常因为按下ctrl+z而产生的信号

# KILL (9)
用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
# TERM(15): 
终止,是不带参数时kill默认发送的信号,默认是杀死进程,与SIGKILL不同的是该信号可以被阻塞和处理。通常用TERM信号来要求程序自己正常退出,如果进程终止不了,我们才会尝试SIGKILL。

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

# SIGSTOP(19) 暂停进程

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

如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸 进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)# 更多详见:man 7 signal
  • 示例1:kill -1 重新加载配置文件
# 1、启动vsftpd服务
[root@egon ~]# systemctl start vsftpd  # 默认匿名用户共享根目录为/var/ftp
[root@egon ~]# ls /var/ftp/
centos7  pub


# 2、打开浏览器,输入ftp://192.168.12.42/,看到的是/var/ftp下的内容

# 3、修改vsftp的配置文件,默认匿名用户共享根目录改为/test
[root@egon ~]# sed -i '$a anon_root=/test' /etc/vsftpd/vsftpd.conf 

# 4、在不启动服务vsftpd的情况下让其加载配置文件
[root@egon ~]# ps aux |grep [v]sftpd
root      50695  0.0  0.0  53288   780 ?        Ss   1101   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
[root@egon ~]# kill -1 50695  # 这期间vsftpd的pid是始终不变的,因为从未关闭过进程
[root@egon ~]# ps aux |grep [v]sftpd
root      50695  0.0  0.0  53288   780 ?        Ss   1101   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf

#5、打开浏览器,重新输入ftp://192.168.12.42/,看到的是/test下的内容
  • 示例2:kill 杀死进程
# kill -15 不加-15,默认发出的就是-15的信号
[root@egon ~]# ps aux |grep [v]sftpd
root      50695  0.0  0.0  53288   780 ?        Ss   1101   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
[root@egon ~]# kill 50695
[root@egon ~]# ps aux |grep [v]sftpd

# kill -9强制杀死
[root@localhost ~]# ps aux |grep nginx
root      29978  0.0  0.1 120896  2096 ?        Ss   17:28   0:00 nginx: master process /usr/sbin/nginx
nginx     29979  0.0  0.1 123364  3544 ?        S    17:28   0:00 nginx: worker process
root      29984  0.0  0.0 112724   988 pts/0    S+   17:28   0:00 grep --color=auto nginx
[root@localhost ~]# kill -9 29978
  • 暂停kill -19与恢复进程kill -18
[root@egon ~]# ps aux |grep [v]sftpd
root     106530  0.1  0.0  53288   572 ?        Ss   21:17   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
[root@egon ~]# kill -stop 106530
[root@egon ~]# ps aux |grep [v]sftpd
root     106530  0.0  0.0  53288   572 ?        Ts   21:17   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
[root@egon ~]# kill -cont 106530
[root@egon ~]# ps aux |grep [v]sftpd
root     106530  0.0  0.0  53288   572 ?        Ss   21:17   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
[root@egon ~]# 
[root@egon ~]# 
[root@egon ~]# # 也可以用数字
[root@egon ~]# kill -19 106530
[root@egon ~]# ps aux |grep [v]sftpd
root     106530  0.0  0.0  53288   572 ?        Ts   21:17   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
[root@egon ~]# kill -18 106530
[root@egon ~]# ps aux |grep [v]sftpd
root     106530  0.0  0.0  53288   572 ?        Ss   21:17   0:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
  • 杀死所有:根据进程名杀所有
[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
  • 查看某个用户开启的进程
[root@egon ~]# pgrep -l -u egon

3.3 关于HUP信号

​ 要了解Linux的HUP信号,需要从hangup说起

在 Unix 的早期版本中,每个终端都会通过 modem 和系统通讯。
当用户 logout 时,modem 就会挂断(hang up)电话。 
同理,当 modem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程。 

​ 综上,我们知道,当用户注销(logout)或者网络断开或者终端关闭(注意注意注意,一定是终端整体关闭,不是单纯的exit)时,终端都会收到Linux HUP信号(hangup)信号,然后终端在结束前会关闭其所有子进程。

​ 如果我们想让我们的进程在后台一直运行,不要因为用户注销(logout)或者网络断开或者终端关闭而一起被干掉,那么我们有两种解决方案

  • 方案1:让进程忽略Linux HUP信号
  • 方案2:让进程运行在新的会话里,从而成为不属于此终端的子进程,就不会在当前终端挂掉的情况下一起被带走。
3.3.1 nohup命令

​ 针对方案1,可以使用nohup命令,nohup 的用途就是让提交的命令忽略 hangup 信号,该命令通常与&符号一起使用

nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,但是 nohup 命令会从终端解除进程的关联,进程会丢掉STDOUT,STDERR的链接。标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上"&"来将命令同时放入后台运行,也可用">filename 2>&1"来更改缺省的重定向文件名。
3.3.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.3.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信号的影响了。
3.3.4 screen命令
# 1、安装
[root@egon ~]# yum install screen -y

# 2、运行命令
方式一:
	  开启一个窗口并用-S指定窗口名,也可以不指定
[root@egon ~]# screen -S egon_dsb 

Screen将创建一个执行shell的全屏窗口。你可以执行任意shell程序,就像在ssh窗口中那样。
在该窗口中键入exit则退出该窗口,如果此时,这是该screen会话的唯一窗口,该screen会话退出,否则screen自动切换到前一个窗口。


方式二:
	  Screen命令后跟你要执行的程序
[root@egon ~]# screen vim test.txt

Screen创建一个执行vim test.txt的单窗口会话,退出vim将退出该窗口/会话。


# 3、原理分析
screen程序会帮我们管理运行的命令,退出screen,我们的命令还会继续运行,若关闭screen所在的终端,则screen程序的ppid变为1,所以screen不会死掉,对应着它帮我们管理的命令也不会退出
测试略

# 4:重新连接会话
在终端1中运行
[root@egon ~]# screen
[root@egon ~]# n=1;while true;do echo $n;sleep 1;((n++));done

[root@egon ~]# 按下ctrl+a,然后再按下ctrl+d,注意要连贯,手别哆嗦,

[root@egon ~]# 此时可以关闭整个终端,我们的程序并不会结束

打开一个新的终端
[root@egon ~]# screen -ls
There is a screen on:
	109125.pts-0.egon	(Detached)
1 Socket in /var/run/screen/S-root.

[root@egon ~]# screen -r 109125  # 会继续运行


注意:如果我们刚开始已经用screen -S xxx指定了名字,那么我们其实可以直接
screen -r xxx ,就无须去找进程id

远程演示

# 在终端1:
[root@egon ~]# screen -S egon_av

# 开启一个新的终端,在该终端执行的命令,终端1会同步显示
[root@egon ~]# screen -x egon_av

了解

你可能注意到给screen发送命令使用了特殊的键组合Ctrl-a,以后简称C-a。这是因为我们在键盘上键入的信息是直接发送给当前screen窗口,必须用其他方式向screen窗口管理器发出命令,默认情况下,screen接收以C-a开始的命令。这种命令形式在screen中叫做键绑定(key binding),C-a叫做命令字符(command character)。

可以通过C-a ?来查看所有的键绑定,常用的键绑定有:

C-a ?        显示所有键绑定信息

C-a w        显示所有窗口列表

C-a C-a      切换到之前显示的窗口

C-a c        创建一个新的运行shell的窗口并切换到该窗口

C-a n        切换到下一个窗口

C-a p        切换到前一个窗口(与C-a n相对)

C-a 0..9     切换到窗口0..9C-a a发送 C-a到当前窗口

C-a d        暂时断开screen会话

C-a k        杀掉当前窗口

C-a [        进入拷贝/回滚模式


四、Screen常用参数选项
常用的命令选项有:
-c file                  使用配置文件file,而不使用默认的$HOME/.screenrc
-d|-D [pid.tty.host]     不开启新的screen会话,而是断开其他正在运行的screen会话
-h num                   指定历史回滚缓冲区大小为num行
-list|-ls                列出现有screen会话,格式为pid.tty.host
-d -m                    启动一个开始就处于断开模式的会话
-r [pid.tty.host]        重新连接一个断开的会话。
-S sessionname           创建screen会话时为会话指定一个名字
-v                       显示screen版本信息
-wipe [match]-list,但删掉那些无法连接的会话
-x                       会话共享演示

四 查看网络状态

netstat命令

[root@tianyun ~]# netstat -tnlp # 查看正在监听的、且使用tcp协议的进程
-t   tcp协议
-u   udp协议
-l   listen
-p   PID/Program name
-n   不反解,不将IP地址解析为主机名,不将端口号解析成协议名(80 ---> http)
  • 示例
[root@egon ~]# netstat -an |grep :22
[root@egon ~]# netstat -an |grep :80
[root@egon ~]# lsof -i:22

五 proc文件系统

[root@localhost ~]# du -sh /proc
  • cpu:/proc/cpuinfo
[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
  • 内存:/proc/meminfo
# 查看内存
[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
  • 内核启动参数:/proc/cmdline
[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
toop

# 重新挂在
[root@localhost ~]# mount -t proc proc /proc/
-t proc		指定文件系统的类型
proc		文件系统,虚拟文件系统
/proc		挂载点

六 管理后台进程

[root@localhost ~]# sleep 5000 &  # 运行程序(时),让其在后台执行
[1] 31143
[root@localhost ~]# sleep 4000  # ^z,将前台的程序挂起(暂停)到后台
[root@localhost ~]# jobs  # 中括号内的编号就是作业编号,%1代表作业1
[1]-  运行中               sleep 5000 &
[2]+  已停止               sleep 4000

[root@localhost ~]# bg %2  # 让作业2在后台运行
[2]+ sleep 4000 &

[root@localhost ~]# fg %1  # 将作业1调回到前台

[root@localhost ~]# jobs
[2]+  运行中               sleep 4000 &
[root@localhost ~]# kill %2


nohup 
# 加在一个命令的最前面,表示不挂断的运行命令

七 管道

1. 什么是管道

管道用于进程间通信

[root@localhost ~]# ps aux |grep "httpd"
[root@localhost ~]# yum list |grep nginx

详细地说,管道操作符号 “|” ,主要用来连接左右两个命令, 将左侧的命令的标准输出, 交给右侧命令的标准输入

PS: 无法传递标准错误输出至后者命令

格式: cmd1 | cmd2 […|cmdn]

2. 管道应用示例

例1:统计当前/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

例2: 统计网站的访问情况

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

例3: 打印当前所有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

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

[root@localhost ~]# df -P|grep '/$' |awk '{print $5}'|awk -F"%" '{print $1}'
50

例5: 统计网站的访问最多的ip top 10

#思路: 打印所有访问的过来的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

4. 管道中的tee技术
-a #追加

[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
  • 重定向与 tee 他们在使用过程中有什么区别
[root@egon ~]# date > date.txt #直接将内容写入date.txt文件中

[root@egon ~]# date |tee date.txt #命令执行会输出至屏幕,但会同时保存一份至date.txt文件中

5. xargs参数传递

[root@egon ~]# which cat|xargs ls -l

[root@egon ~]# ls |xargs rm -fv

[root@egon ~]# ls |xargs cp -rvt /tmp/ -或-> ls | xargs -I {} cp -rv {} /tmp/

[root@egon ~]# ls |xargs mv -t /tmp/ -或-> ls | xargs -I {} mv {} /tmp

八 僵尸进程与孤儿进程

冤魂 Z 是杀不死的,是因为它已经死了,否则怎么叫 Zombie(僵尸)呢?冤魂不散,自然是生前有结未解之故。在UNIX/Linux中,每个进程都有一个父进程,进程号叫PID(Process ID),相应地,父进程号就叫PPID(Parent PID)。当进程死亡时,它会自动关闭已打开的文件,舍弃已占用的内存、交换空间等等系统资源,然后向其父进程返回一个退出状态值,报告死讯。如果程序有 bug,就会在这最后一步出问题。儿子说我死了,老子却没听见,没有及时收棺入殓,儿子便成了僵尸......
  • 僵尸进程
#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======================
[root@egon ~]# cat test.py 
#coding:utf-8
from multiprocessing import Process
import os
import time

def task():
    print("father--->%s son--->%s" %(os.getppid(),os.getpid()))

if __name__ == "__main__":
    p1=Process(target=task)
    p2=Process(target=task)
    p3=Process(target=task)
    p1.start()
    p2.start()
    p3.start()
    print("main--->%s" %os.getpid())
    time.sleep(10000)
[root@egon ~]# python test.py &
[5] 104481
[root@egon ~]# main--->104481
father--->104481 son--->104482
father--->104481 son--->104483
father--->104481 son--->104484

=====================窗口2======================
[root@egon ~]# ps aux |grep Z
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     104482  0.0  0.0      0     0 pts/2    Z    18:24   0:00 [python] <defunct>
root     104483  0.0  0.0      0     0 pts/2    Z    18:24   0:00 [python] <defunct>
root     104484  0.0  0.0      0     0 pts/2    Z    18:24   0:00 [python] <defunct>
root     104488  0.0  0.0 112828   960 pts/4    R+   18:24   0:00 grep --color=auto Z

孤儿进程

父进程先死掉,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被进程号为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的顶级进程接收了,所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已,孤儿进程声明周期结束自然会被顶级进程来销毁。

扩展阅读:

https://baike.baidu.com/item/僵尸进程/1036577?fr=aladdin

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值