进程在操作系统中是有不同的状态的,那么一个进程可以有多少种不同的状态的?
什么是运行状态呢?
每个CPU都会在系统层面上维护一个叫运行队列的东西,当我们的进程都准备好了,可以随时被调度,就会被放到运行队列中等待执行,所以只要在运行队列的进程都是运行状态。
什么是阻塞状态呢?
我们写代码是时候,一定会用到scanf等从键盘中读入的函数或者从硬盘中访问某些资源,但是如果我们一直不输入,或者要访问的该磁盘的资源的进程很多,我们的进程就不能就绪,既然我们需要的资源不能就绪,那么代码就不能接着往后执行,那么我们这种等待资源就绪的进程的状态,就是阻塞状态。并且OS一定是最先知道它所管理的设备的状态的变化的!!
当一个进程阻塞了,从我们小白的视角看就是程序卡住了,换个角度说,就是我们的进程没有在运行队列,CPU不调度我的进程了。
什么是挂起?
如果说一个进程被阻塞了,那么注定这个进程它所等待的资源是没有就绪的,该进程是无法被调度的,如果此时,OS的内存资源已经验证不足了,怎么办?OS会觉得反正你现在等待的资源没有就绪,也运行不了,所以它会把你加载到内存的代码数据,放到磁盘中,磁盘中有个swap分区,是专门存放被OS替换下去的数据的。所以当你的代码被OS放到磁盘的那一刻起,你的状态就是挂起状态,由于你是阻塞的,所以你就是阻塞挂起状态。但是如果你的进程出现了挂起状态,也就意味着你的OS离挂掉不远了。
将内存数据置换到外存中是针对所有阻塞进程的,不用担心慢的问题,这个是必然的,当出现挂起状态,就不是慢不慢的问题,是OS挂不挂的问题了。当进程再一次被调度是,该挂起的进程的数据就会被置换回来了,这些操作都是操作系统内部完成的。
PCB中的成员有很多,进程状态就在里面 status。进程状态说白了,就是PCB中的一个字段!而进程状态变化的本质,就是修改 status的值,然后把PCB链如不同的队列中去。
我们看一下kernel内核怎么说
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
S睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
t 停止状态 是debug程序在遇到断点时,进程暂停。
其中S D T t 都是阻塞状态,Linux在内存严重不足的时候,是会自己杀死进程来保证自身接着运行的,一般来说之计OS快要挂掉的时候才有可能能够看到D状态,并且它是不可被杀掉的,它是专门对磁盘设计的一种状态。
我们可以通过 ps axj 来查看进程状态。
我们的进程还分为前台进程和后台进程。
前台进程:是在终端中运行的命令,那么该终端就为进程的控制终端,一旦这个终端关闭,这个进程也随之消失。
后台进程:也叫守护进程(Daemon),是运行在后台的一种特殊进程,不受终端控制,它不需要终端的交互;Linux的大多数服务器就是使用守护进程实现的。
我们平时执行的可执行程序都是前台的,在Linux终端运行命令的时候,在命令末尾加上 & 符号,就可以让程序在后台运行。
我们创建一个进程一定是为了完成某种任务,但是这个任务完成的怎么样,所以在进程退出的时候是要有一些退出的信息,表示自己的任务完成的怎么样,我们平时写的main函数都有return ,这个就是返回信息,当一个进程在退出时退出信息会由OS写入到PCB中,可以允许代码和数据的空间立即被释放,但是PCB不能立即被释放,需要等待父进程来读取返回信息,然后才可以是释放,这样的进程就是僵尸进程。
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
僵尸进程的危害?
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
子进程退出了,父进程没有退出,该子进程就是僵尸状态,但是要是子进程没有退出,父进程退出来,子进程怎么办?
此时子进程就变成了孤儿进程,它会被1号进程领养,来回收它的PCB。
总结:
优先级是什么?
是得到某种资源的先后顺序,优先级存在的本质就是系统资源不足!优先级其实就是进程PCB中的一个字段,数字越小,优先级越高。
Linux中的优先级范围是 60 ~ 99,Linux中程序默认的优先级都是80。
Linux的优先级是支持动态调整的,Linux中有一个nice值,我们可以通过调整nice值来调整优先级:
优先级(新) = pri(每次都从80开始) + nice。
nice的调整大小是 -20 ~ 19 超过19 的按照19 计算,不高于 -20 的按照 -20计算。
那么为什么OS要把优先级控制在一定的范围内呢?
这是因为OS要保证每个进程在调度的时候,每个进程都要较为均衡的得到调度,如果不控制在一定的范围,容易到值一些优先级较低的进程长时间得不到CPU的调度,导致进程饥饿问题。
优先级VS权限
优先级是保证可以申请到某种资源,只不过需要等一等。
权限是能否得到某种资源。
每一个进程不是占有CPU就一直运行,而是每隔一段时间就会被从CPU上玻璃下来,这段时间就是时间片。Linux内核是支持进程之间进行CPU抢占的,基于时间片的轮转式抢占式内核。并发问题,就一定要考虑进程间的切换。
为什么我们函数内定义的临时变量,会被return返回给外部呢?
这是因为有eax寄存器,它充当了代码的临时空间。
为什么我们的进程知道我们的代码运行到哪里?又是如何做到函数之间的跳转呢?
这是因为CPU内有eip寄存器,它是程序计数器,会记录程序运行的位置。我们进程在运行的时候,是会使用这些寄存器的,我们的进程,会产生各种数据,在寄存器中临时保存。
如果我们有多个进程,各个进程产生的数据都是不一样的,但是寄存器只有一套,那怎么保证数据的独立性呢?
这是因为CPU寄存器的内容会保存到各个进程各自对应的PCB中,在调度的时候把内容放到CPU的寄存器中,在调度完后,再把自己对应的寄存器中的数据带走,这样就可以保证每个进程拥有自己独立的数据,内容保存在PCB中,本质是保存在内存中,所以说寄存器!=寄存器的内容。
那么今天的分享就到这里了,有什么不懂得可以私信博主,或者添加博主的微信,欢迎交流。