一 进程介绍
程序:存放代码的文件 (静态)
进程:程序的运行过程 (动态)
同一个程序可能对应多个进程
父进程:程序运行时产生的第一个进程
子进程:由父进程衍生fork()出来的进程
注意:如果父进程终止,子进程也会随之被终止 (守护进程)
[root@arther-linux ~]# yum install nginx -y
[root@arther-linux ~]# systemctl start nginx
[root@arther-linux ~]# ps aux |grep nginx
root 2037 0.0 0.0 112824 980 pts/0 S+ 18:37 0:00 grep --color=auto ngin
1 进程-进程状态(R、S、D、T、Z、X)
进程概念
- 正在执行的程序
- 正在计算机执行的程序实例
- 能分配处理器并有处理器执行的实体
进程的两个基本元素是程序代码和代码想关联的数据集。进程是一种动态描述,但并不代表所有的进程都在执行。这就可以引入进程状态。
进程在内存中因策会略或调度需求,
会处于各种状态:
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上运行。 另外如果计算机只有一个处理器,那么一次最多只有一个进程处于这种状态。
(在此状态下等待被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() 函数返回的状态。这个状态只是⼀个返回状态, 你不会在任务列表⾥看到这个状态
(pid没有被回收,还占用着cpu资源)
进程状态切换
进程在运行中不断的改变运行状态;
-
就绪状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
-
执行(Running)状态
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态 。
-
阻塞(Blocked)状态
正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
就绪–>执行
处在就绪状态的进程,当调度器为其分配了处理机后,就变成了执行状态。
执行–>就绪
执行状态的进程在其执行过程中,时间片跑完了不得不让出处理机,于是从执行变成就绪状态。
执行–>阻塞
正在执行的进程等待某种事件而无法继续执行时,便从执行状态变成阻塞状态。
阻塞–>就绪
处在阻塞状态的进程,如果等待的时间发生,则从阻塞状态转变成就绪状态。
二 查看进程
ps aux是常用组合,查看进程用户、PID、占用CPU百分比、占用内存百分比、状态、执行的命令等。
-a # 显示一个终端的所有进程
-u # 选择有效的用户id或者是用户名
-x # 显示没有控制终端的进程,同时显示各个命令的具体路径
示例
[root@arther-linux ~]# ps aux |head -5
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 128816 7392 ? Ss 05:17 0:17 /usr/lib/systemd/systemd --system --deserialize 15
root 2 0.0 0.0 0 0 ? S 05:17 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? S< 05:17 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 05:17 0:00 [ksoftirqd/0]
查看结果显示
USER: 运行进程的用户
PID: 进程ID
%CPU: CPU占用率
%MEN: 内存占用率
VSZ: 占用虚拟内存,单位:kb
VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大 小,产生这个的可 能性很多 比如内存映射,共享的动态库,或者向系统申请了 更多的堆,都会扩展线性空间大小。
RSS: 占用实际内存,单位:kb
RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小
TTY: 进程运行的终端
STAT: 进程状态
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执行vim命令
[root@arther-linux /]# vim 1.txt
-2.在窗口2查看vim的运行状态为:S+
[root@arther-linux ~]# ps aux |grep [v]im
root 8065 0.1 0.2 149620 5188 pts/0 S+ 17:50 0:00 vim 1.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
查看进程树
[root@arther-linux /]# pstree
查看ppid
[root@arther-linux /]# ps -ef | head -10
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 05:17 ? 00:00:17 /usr/lib/systemd/systemd --system --deserialize 15
root 2 0 0 05:17 ? 00:00:00 [kthreadd]
root 4 2 0 05:17 ? 00:00:00 [kworker/0:0H]
root 6 2 0 05:17 ? 00:00:00 [ksoftirqd/0]
root 7 2 0 05:17 ? 00:00:01 [migration/0]
root 8 2 0 05:17 ? 00:00:00 [rcu_bh]
root 9 2 0 05:17 ? 00:00:20 [rcu_sched]
root 10 2 0 05:17 ? 00:00:00 [lru-add-drain]
root 11 2 0 05:17 ? 00:00:00 [watchdog/0]
动态查看
-1.基本用法
[root@arther-linux /]# top
[root@arther-linux /]# top -d 1 # 1秒查看一次
三 管理进程
1 关于HUP信号
要了解Linux的HUP信号,需要从hangup说起
在 Unix 的早期版本中,每个终端都会通过 modem 和系统通讯。
当用户 logout 时,modem 就会挂断(hang up)电话。
同理,当 modem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程。
综上,我们知道,当用户注销(logout)或者网络断开或者终端关闭(注意注意注意,一定是终端整体关闭,不是单纯的exit)时,终端都会收到Linux HUP信号(hangup)信号,然后终端在结束前会关闭其所有子进程。如果我们想让我们的进程在后台一直运行,不要因为用户注销(logout)或者网络断开或者终端关闭而一起被干掉,那么我们有两种解决方案。
-
方案1:让进程忽略Linux HUP信号
-
方案2:让进程运行在新的会话里,从而成为不属于此终端的子进程,就不会在当前终端挂掉的情况下一起被带走。
2 nohup命令
针对方案1,我们可以使用nohup命令,nohup 的用途就是让提交的命令忽略 hangup 信号,该命令通常与&符号一起使用。
nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,但是 nohup 命令会从终端解除进 程的关联,进程会丢掉STDOUT,STDERR的链接。标准输出和标准错误缺省会被重定向到 nohup.out 文件 中。一般我们可在结尾加上"&"来将命令同时放入后台运行,也可用">filename 2>&1"来更改缺省的重定向 文件名。
3 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 # 会继续运行
[root@egon ~]#
注意:如果我们刚开始已经用screen -S xxx指定了名字,那么我们其实可以直接 screen -r xxx ,就无须去找进程id了