进程概念总结

什么是进程?

在操作系统的书上是这样描述进程的:
一个运行起来(加载到内存)的程序就是进程,进程具有动态属性。

程序是什么?
程序的本质就是磁盘上的一个文件。

当程序被加载到内存之后,假设内存中有很多这样的进程,那么此时操作系统需要多这么多的进程进行管理,如何管理呢?先描述,在组织

程序在载入内存之后在内存中不仅有程序的代码和数据,还有一个对应的PCB。

PCB概念

PCB就是process contral block,叫做进程控制块。

PCB的作用

当程序被加载到内存之后,操作系统要对其进行管理,PCB相当于就是操作系统对进程的描述。PCB其实是一个结构体。在Linux下他的名字是task_struct。

struct task_struct
{
	//该进程的所有属性
    //该进程对应的代码和属性地址
};
//这个结构体被称为内核结构体
//创建的对象被叫做内核对象
//该结构将与你的代码数据关联

通过对进程的描述,创建出来了一个结构体对象,将内存中所有的进程都描述创建完对象之后,通过特定的数据结构进行组织,这就是操作系统管理进程的方法(先描述,后组织)
对进程的管理实际就变成了对PCB进行管理,最终其实是对数据结构的管理。

进程 = 内核数据结构 + 进程对应的磁盘代码
内核数据结构是操作系统对进程进行管理所需要的,磁盘代码是CPU执行该进程时需要的。

Linux中关于进程的指令

进程的查询 ps ajx

直接使用该命令会将所有的进程都打印出来,所以可以使用行过滤工具进行过滤。

ps ajx | head -1 && ps ajx | grep 'bash'

这里的&&作用就是将两次执行的指令和并到一起执行。head -1就是打印表头。
image.png

这里的PID是进程的编号,PPID是该进程的父进程的编号,STAT是该进程当前的状态。

kill指令

作用:用于向进程发送信号,实现对进程的管理。

kill -l 可以将kill能发送的所有信号打印出来。
image.png
这里第一个用的就是9信号,作用是将进程杀死。

image.png
test是我写的一个死循环计数的C语言程序,编译成了test
test的PID是32330,通过kill -9 32330就可以将该进程杀死。
image.png

获取PID的系统调用

getpid()

image.png
这个系统调用的返回值就是当前程序运行起来的时候的PID
image.png
image.png

getppid()

这个系统调用的作用是获取当前进程的父进程的PID
image.png
image.png
image.png

当前在运行中的这个进程的父进程的PID是29000,29000其实是bash也即是命令行解释器bash
image.png
所有命令行上跑的指令都是bash以派生子进程的方式执行的。在shell原理那一节讲过,如果用户输入的指令或者代码存在bug,bash自己去执行万一崩溃了那用户就不能和系统进行交互了。所以用派生子进程的方式去执行指令和代码就算子进程崩溃了也是不会影响自身的。

所以,除了特殊情况,基本所有在命令行上跑的程序它的父进程都是bash。

fork()

fork是一个创建子进程的系统调用。
image.png
image.png
需要特别注意的是他的返回值,当创建子进程成功的时候会将子进程的PID返回给父进程,将0返回给子进程。如果创建失败就会将-1返回给父进程并且设置错误码。

下面是演示代码
image.png
image.png
在fork函数执行之前,只有一个父进程,fork执行后:父进程+子进程

为什么同一个变量在后续不会被修改的情况下会有不同的值?—有关进程地址空间。

上面这种父子进程同时跑的情况就是多进程。
如果我们使用kill杀掉父进程是不会影响子进程的执行的,同样的杀掉子进程也不会影响到父进程。

fork后面的代码是被父子进程共享的,通过fork返回值的不同让父子进程执行共享代码的不同部分,这就是并发编程。

进程状态

进程有很多状态不同的书有不同的描述,比如:运行,新建,就绪,挂起,阻塞,等待,停止,挂机,死亡等等

进程有这么多状态主要是为了满足不同的运行场景。

运行状态

首先解释运行状态,在CPU运行时会在其内存中维护一个运行队列,用来描述所有在CPU上运行的进程。
image.png
这里面CPU所指向的是操作系统内存中的结构体是操作系统根据管理的进程描述出来的,结构体内的队列是CPU内存中维护的运行队列。队列内放的是需要cpu执行的进程。进程是否被放进运行队列是由操作系统决定的。

当进程处于运行队列中时就是运行状态,并不是非要进程在被CPU执行才叫运行状态,因为CPU运行速度时很快的,每个进程只在CPU内运行很短的时间。进程轮转的很快。

Linux通过调度器的调度算法评估那个进程要先被执行,将先执行的进程先放进运行队列。
结论:

  1. 一个CPU只有一个运行队列
  2. 让进程进入队列,本质是让PCB也就是task_struct结构体对象放进runqueue中。
  3. 进程被放进runqueue就是R,并不是进程正在运行的时候才叫运行状态。
  4. 进程状态是进程内部的属性,所以是放在task_struct对象中的。

CPU的速度这么快,执行进程的时候都有进程需要等待,那么一个外设也可能被多个进程访问,硬件比如磁盘也是只有一个的,所以在磁盘没有完成当前进程的任务的时候是不能被下一个进程访问的。
所以描述外设的PCB也就是struct dev_disk结构体中也有一个队列,这是个等待队列,或者叫阻塞队列。

当进程对应的代码被CPU执行的时候要开始访问外设了,但是此时外设被占用,进程需要等待外设空闲后再继续执行代码,因为外设是很慢的,CPU不能等外设,所以操作系统会将该进程放进外设的wait_queue等待,让CPU继续运行下一个进程。此时进程的状态就从运行状态转变成了阻塞状态。

所以所谓的进程不同状态,本质是在不同的队列中等待不同的资源

阻塞与挂起状态

上一段说了,当进程在外设的wait_queue中等待此时的状态就是阻塞状态。

但是现在有这样一个情况,现在A进程在磁盘的等待队列中等待,这时候操作系统的内存因为进程过多内存不足了,此时OS发现A进程在长时间等待但是不运行,那么此时操作系统就将A进程对应的代码清理出内存,放到磁盘对应的某个区域,腾出内存给别的进程。此时A进程的状态就是挂起状态
image.png
当A进程等到磁盘IO空闲了,此时操作系统会将A进程的代码和数据重新载入内存,准备执行A进程,此时万一内存不够了该怎么办?操作系统会将其他进程挂起节省出内存,因为你让了别人,你需要的时候别人也会让你。

总结

  1. 进程对应的代码和数据被暂时置换到了硬盘上,此时进程控制块还在内存中就是挂起状态。
  2. 挂起状态是将内存节省出来给别人使用。
  3. 将进程的代码和数据保存到磁盘和加载到内存的过程就是,内存数据的换入换出。

在当前所说的这些状态中,阻塞不一定挂起,但是挂起一定是阻塞。
为什么说是当前状态呢?因为在某些操作系统中还会有:阻塞挂起状态,就绪挂起状态等等组合除了没有运行挂起之外。

Linux是如何实现这些状态的?

下面我们先通过Linux源码来看一下Linux都有那几个状态。
image.png
R状态就是运行状态
S状态是睡眠状态(浅度睡眠)又叫可中断睡眠
D状态是深度睡眠状态(磁盘休眠)又叫不可中断睡眠
T是暂停状态
t(tracing stop)追踪暂停状态
Z 僵尸状态
X 死亡状态
第141行的的状态可以看作是t,防止与140行的T状态混淆

R (running)

image.png
这段代码是死循环,会一直占用CPU进行运行。此时进程test的状态就是R+,加号的意思是这个进程是前台进程,意思是在前台进程运行的时候命令行会失效也就是输入什么都没用,但是可以用ctrl + c终止前台程序。
image.png

S (sleeping)

image.png
我们将代码中间之前是加法运算,现在改成了打印。
image.png
此时test的状态就变成了S,休眠状态,这种休眠状态也是阻塞状态的一种。
为什么test程序一直在运行却是休眠状态呢?

因为printf是将内容打印到显示器上,显示器是外设,所以速度比较慢,程序运行的%99的时间都是在等待IO就绪,只有%1的时间是在运行代码。所以我们每次查看基本都是休眠状态。

进程分为两种,一种是计算密集型进程,另一种是IO密集型,上个R状态所举例的进程就是计算密集型,这个S状态所举例的进程就是IO密集型

T (stopped)

image.png
kill -19是进程暂停信号,kill -18是进程继续信号。
我们将test进程运行起来后再发出暂停信号,此时进程就会被暂停了。
image.png
image.png
再次输入kill -18 29666进程就会继续执行。
image.png
此时从暂停中继续运行的程序它的+没有了,也就是从前台进程,变成了后台进程。

那么前台进程和后台进程有什么区别呢?
前台进程运行的时候命令行是失效的,但是可以通过ctrl + c强行终止掉进程
后台进程运行的时候命令行是可以生效的,但是Ctrl + c不能终止后台进程,只能通过kill -9来终止掉。

D (disk sleep)

深度睡眠状态,一般情况下我们是看不到这个状态的,只有当系统出现高IO和高并发的情况下才会出现深度睡眠状态。

举例来说明:
image.png现在内存中有一个进程A,A想要向磁盘中写入1w条用户数据,A开始执行后,磁盘接到数据开始写入,因为外设的速度是比较慢的,所以A变成了阻塞状态,等待磁盘写完数据后再变成运行状态继续执行后面的代码。此时内存告急,操作系统发现即使将所有暂时不会运行的进程挂起,内存也不够使用了。然后操作系统突然发现A进程正在悠闲的等着磁盘写数据,操作系统直接将这个啥事都不干的A进程干掉了。等磁盘写完数据,磁盘发现有些数据写入失败了,磁盘带着数据回来想找A进程询问一下,是重新写呢还是将数据扔了呢?但是此时磁盘找不到A进程了。所以磁盘就将数据丢掉了继续执行下一个进程的IO需求。

到现在就出现了问题,用户的1w条数据没了,那么此时是谁的问题呢?A进程是执行用户的需求去写数据,磁盘也是谁让他写他就写,数据写入失败也和磁盘没有什么关系。此时矛头直指操作系统,但是操作系统的本职就是要管理好进程,内存不足了就需要杀死进程腾出内存。所以其实谁都没有错误,但是结果出现了问题。

由此用户说,行了行了,我给A进程一个保护令,只要A进程有保护令操作系统就不要杀死这个进程。这个保护令就是D(disk sleep)状态。

当进程处于D状态的时候,是无法被操作系统杀掉的,只能通过断电或者是等进程自己醒过来。
所以这种情况只会在高IO的情况下出现,如果机群内存中挂满了D状态的进程,那么此时内存还需要加载程序进入内存,这时候操作系统无法杀死D状态的进程,内存空间越来越小,最后很可能就导致服务器集群崩溃了。
解决方法只能是,转移机群服务压力慢慢的等着D状态进程醒过来,或者是系统崩溃,断电重启。

T (tracing stop)

这个T在Linux操作系统中会显示成t
这个状态标识该进程正在被追踪(调试)
image.png

Z (zombie)

僵尸状态,当子进程运行结束后,会有一个退出状态代码,如果父进程一直不读取这个退出状态代码那么此时子进程就一直处于僵尸状态,这个子进程就变成了僵尸进程。

为什么会有僵尸进程?当进程被创建出来的时候都是为了执行某个任务的,进程结束后操作系统需要知道任务完成的结果怎么样,所以进程退出时不能立即释放进程对应的资源,应该保留一段时间让父进程或者操作系统来读取。

例如,有个人在街上突然昏倒了,你路过发现人已经没有呼吸了,所以拨打了110和120,120过来检查了一下确实人没了,110此时不会说是直接就收队留你和尸体在风中凌乱,110要检测调查这个人为什么没了,检查完之后确认是正常死亡才会通知家属收拾现场。
从这个人倒下到检查完毕这段时间这个进程一直是僵尸状态,110相当于是操作系统,家属相当于是父进程,检查完成后进程从Z状态变成了X状态就可以被回收了。

下面通过一个监控脚本来看进程状态的变化。

while :; do ps ajx | head -1 && ps ajx | grep test | grep -v grep; sleep 1; done
//监控脚本

image.png
这是测试程序test的源代码,子进程延时2s后结束父进程继续运行但是不对子进程进行回收。
image.png
一开始子进程是S状态当子进程结束的时候就变成了Z状态。此时子进程就叫做僵尸进程。

孤儿进程

僵尸进程是子进程先结束但是父进程继续运行,那么还有一种情况就是父进程先结束了但是子进程还在继续运行,此时的子进程就是孤儿进程。
孤儿进程会被一号进程领养,也就是父进程退出后子进程的PPID就变成了1。
一号进程就是操作系统,一般叫做Init或者是systemed

孤儿进程被领养可以防止子进程结束后没有人回收子进程,防止造成内存泄漏。如果子进程僵尸了没有人回收那么此时进程虽然结束了,进程对应的代码和数据可以被释放了,但是进程的PCB需要保存进程的状态所以不能被释放,于是就造成了内存泄漏。

进程状态就是一个数字被保存在PCB中。
image.png
image.png
父进程是979,子进程是980,当父进程退出后子进程的PPID就变成了1.
上图中的+号没了,父子进程在创建的时候是前台进程,当子进程变成孤儿进程后会从前台进程变为后台进程。
除了僵尸进程和孤儿进程在服务器设计中还有精灵进程(守护进程)。

进程优先级

  1. 什么是进程优先级?

优先级不是权限,权限决定的是能不能的问题,而优先级是已经能了,是先做还是后做的问题。

  1. 为什么会存在优先级?

因为资源有限不能保证每个进程都能分到资源所以优先级决定了那个进程先分配到资源,也就是那个资源先执行。

  1. 进程优先级特点

优先级本质就是PCB里面的一个数字或者是几个数字

ps 指令

用于显示当前进程的状态,类似于 windows 的任务管理器。
a:显示一个终端的所有进程,除会话引线外。
l:长格式显示更加详细的信息;
image.png
图中的PRI这一栏就是进程的优先级。PRI全称是priority,NI全称是nice修改优先级就是通过修改NI来实现的。
进程最终的优先级 = 进程老的优先级 + nice值
nice支持在进程运行中,进行优先级的调整。
PRI越小,进程的优先级越高。

Linux优先级的计算法则:进程老的优先级每次设置都是80,nice的取值范围是[ -20 , 19 ]四十个值,所以PRI的取值范围也就是最终优先级的取值范围就是[ 60 , 99 ].

top指令

top命令是持续监听进程的运行状态。
使用top指令输入r,然后输入进程的PID,最后输入nice值完成对进程优先级的调整。
image.png
修改前父进程的PID是14612,优先级是80.
image.png
输入top点击回车后会进入这个界面。输入r会进入下一个界面。
image.png
意思是请你输入PID默认是1552,输入14612后点击enter
image.png
此时输入的值就是NI的值,要注意的是如果输入的值是NI范围之外的值比如100,那么最终NI就是19,如果输入-100那么最后NI就是-20.
我们输入19点击enter最后按q就是退出top。
image.png
此时再次查看进程的优先级就可以看到更改后的优先级了。

为什么NI有取值范围不允许你任意更改优先级呢?
因为操作系统不允许过度调整进程的优先级防止进程调度失衡,进程调度失衡就是一个进程的优先级太高,并且这个进程长时间占用CPU资源就会导致其他的进程卡死。

四个特性

  1. 竞争性:系统中进程的数目众多而CPU资源却是少量的,所以进程与进程之间存在竞争性,为了高效的完成任务更合理的竞争相关资源,就有了进程优先级。
  2. 独立性:进程都是各自独享资源,多进程运行的时候不会互相干扰,一个进程的崩溃不会影响到另一个进程。
  3. 并行:如果电脑有多个CPU那么此时就可以同时运行多个进程,这就是并行。
  4. 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间内让所有的进程都得以推进,这就是并发。这里的进程切换使用的就是时间片轮转策略。

进程切换

image.png
一个CPU内有很多的寄存器,其中有两种:一种是用户可见寄存器(通用寄存器等等),另一种就是用户不可见寄存器(一般是状态,权限等寄存器)。

其中有一个寄存器的名字叫做pc或eip,又叫做指令计数器,这个寄存器内保存的是当前正在执行的指令的下一条指令的地址。

当进程运行时,CPU一直都在做三个工作:1. 取指令,2. 分析指令,3. 执行指令。CPU执行进程的时候产生的临时数据都保存在寄存器中。CPU内只有一套寄存器,寄存器内保存的数据是当前进程的临时数据。

当一个进程还没有执行完,此时需要切换进程,这时候就需要保存寄存器里面的数据,这一步称为上下文保护

为什么一个进程没有执行完要切换进程呢?
因为CPU并不是将每个进程都一次型跑完再去跑下一个进程的,如果是这样的话那么一个进程运行的时候其他进程就会卡死特别是遇到死循环进程,因为Linux是一个分时操作系统分时的意思就是分时间片,操作系统给不同的进程分配不同的时间片,但是每个时间片对应的时间是相同的,可能是10ms,当该进程在CPU上运行了10ms之后操作系统就会将这个进程从CPU上剥离,让CPU继续执行下一个进程,并发就是CPU通过这种时间片轮转的方式在一段时间内推进多个进程。
比如:1秒内可以将5个进程各运行20次。

所以当一个进程时间片结束后,要切换到下一个进程,此时就要保存当前进程运行到此时CPU内寄存器上的数据,这一步称为上下文保护,保存的位置可以认为是在PCB中,但实际是操作系统有专门的段描述符表来负责这部分工作。
当下一次轮转到这个进程的时候,需要先将之前保存的上下文数据载入到CPU的寄存器中,这一步称为上下文恢复,载入完成后再继续上次执行到的位置接着运行。

注意:寄存器是被所有进程共享的,但是寄存器内的数据是每个进程私有的,这个私有的数据就是上下文数据

有一个问题,寄存器不是很小吗,那么寄存器能保存下代码里的数据嘛?
要注意寄存器保存的知识代码执行过程中的临时数据,下面执行需要用到的计算出来的数据,你的代码中开辟了很大的数组这些数组中的数据都是不会保存到寄存器内的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KissKernel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值