😇第一座大山:进程
00 min
2024-10-28
2024-11-24
type
status
summary
date
slug
tags
category
icon
password
 
💡
前言:在操作系统的管理工作中其实所有的管理操作都能转换为对某种数据类型的增删改查
所以到底什么是进程:所谓进程就是一个已经加载到内存中的程序就叫做进程,正在运行的程序就叫进程。
 
那对于操作系统1怎么去管理多个进程呢?
同样的还是先描述,在进行管理,就是首先先创建一个结构体用于描述进程—PCB,Process ctrl block,进程控制块,创建过后,将该程序加载到内存中
所以进程就是代码和进程控制块PCB的结合体
将进程的属性转化为结构体,然后形成一个链表对链表进行增删查改其实就是一种简单的进程管理了
 
 

我们来介绍一下什么是PCB和他的实现方式

PCB这个概念是基于操作系统上的,每个操作系统都存在PCB,但是每一个操作系统的PCB实现方式都不太一样
在linux中的PCB叫做task struct
接下来我们在讲讲linux中具体是这么做的
task struct是PCB的一种,是linux中的PCB,task struct是linux内核中的一种数据结构,他会被加载到RAM中并且包含着进程的信息
采用双向链表结构,来进行增删查改
然后这里就在引入一个新的命令就是ps,能打印出现在正在运行的进程的信息
 
 
获取pid::
ps axj | head -1 && psaxj | grep —
c语言中获取pid:getpid():这个其实就是c里面调用系统接口的一种情况
 
 
如何自己创建一个进程:
1.命令行层面上,./加载一个程序
2.在代码行层面使用fork函数创建
 
进程的父子关系:
父进程和子进程是共享代码的,也就是说如果执行fork函数之后,下方的代码会被执行两次,但是我们创建子进程的目的并不是这个而是想要这两个进程进行不同的工作父子协同,那怎么使得这两个代码协同呢?
答案是使用fork函数的返回值,而fork函数因为他创建了一个子进程的特殊性所以这个函数会返回两次所以函数: pid_t fork(void)
然后使用一个pid_t id这个变量来接收这两个返回值,但是一个变量怎么接收两个返回值呢?
💡
进程之间是不可以互相影响的,因为不允许两个进程相互篡改数据,所以虽然父子进程共享同一份代码但是不能共享同一份数据,所以共享代码不影响独立性
所以在父进程开辟子进程之后,他们共享同一份代码,但是数据共享相同的,但是相互独立,也就是如果子进程想要修改父进程的数据,操作系统会单独开辟一个空间让子进程操作,是的两个进程的不理性不受打扰,这个操作就叫做写时拷贝
所以代码不能被改变,但是数据能进行写时拷贝,同时fork函数又拥有了两个返回值所以这个接收返回值的id就被写入了两组数据
 
 

进程状态:

1.一般的操作系统进程状态的概念:
三种进程状态:运行,阻塞,挂起
当一个进程被创建后,不一定是立马运行,二十进入了一个队列等待运行,而这个排队的队列就是由各个进程的pcb组成的一个链表,凡是处于运行队列的进程。他们的状态被称为运行状态,也叫做R状态:其实也就是准备好了随时被调度器调用就叫做运行态
同时每一个进程1都拥有一个叫做时间片的东西定义在pcb中,定义的就是这个进程在cpu上最多呆上的时间
所以这里引入了一个概念:叫做并发执行:在一个时间段内所有的代码都会被执行
所以这里存在大量的进程从cpu上拿上拿下的动作,这个动作就叫做进程切换
 
 
阻塞状态
当有一个代码里面存在一个cin希望键盘输入的时候,这个进程就不能在运行队列上了因为他会阻塞后面的进程,所以我们将他挂在操作系统管理硬件的队列上等待,如果是希望键盘输入,那就挂在键盘的等待队列上,等待键盘的输入,这个队列叫做等待队列,当键盘输入数据之后,就把这个进程挂到运行队列中,我们就把这中进程称为阻塞状态
 
挂起状态
当进程过多,内存资源严重不足的时候,就需要将一些阻塞状态的进程改为挂起状态用于省下空间
在内存中保存PCB信息,但是将其他代码内容返回到磁盘空间等外设空间中,这就叫做挂起状态,在装操作系统的时候在磁盘分区的时候就能看见在磁盘空间中有一个swap分区,就是专门用于存储,这些我挂起状态的进程的
💡
电脑中有几个cpu就有几个运行队列
2.具体的linux下的状态时如何维护的
那上面说了三种进程状态,那linux中时如何实现这三种状态的呢
R状态(run):表示运行状态
S状态(sleep):表示阻塞状态(浅度睡眠)
D状态(disk sleep):深度睡眠状态,当一个进程是将一个很重要的数据写入到磁盘中时,操作系统不能将他杀死,避免数据丢失,所以这个进程的状态不能是最普通的sleep状态,所以诞生了一个D状态
T状态:暂停状态(stop)kill -18 PID能暂停一个进程
X状态(dead):中止态
Z状态(zombie)僵尸:在一个进程死亡的时候不会直接进入X状态,会先进入Z状态
在进程进入中止状态之前,os会先维持一段时间的Z状态,以便让关心这个进程的其他进程了解到情况再让这个进程进入到X状态(夫进程最关心这个子进程退出时的原因)
💡
如果父进程先退出了,子进程还在运行中,那么子进程的父进程就会被改为一号进程(操作系统),这种父进程是1号进程的我们将他称为孤儿进程
 
 
🔒
僵尸进程的危害:进程的退出状态必须被维持下去,因为他要告诉自己的父进程任务完成的怎么样了,但是如果父进程一直不读取,那么子进程就一直保持在Z状态
而且维护退出状态本身就是要用数据维护,也属于进程的基本信息,换句话说就是如果进程一直不退出,那他的PCb就要一直维护
所以僵尸进程一直不被父进程回收,那么就会浪费掉很多资源
会造成内存泄漏
那该如何解决呢?
后面会说到
 
 

进程优先级:

既然进程有父子关系,那PCb的链接形式到底是双向链表还是多叉树呢,有没有可能其实多叉树才更符合这样的父子关系呢?
其实这个很简单,这个节点技既属于多叉树也属于双向链表不就好了?
进程的优先级还是很重要的,操作系统中的调度器能尽可能公平的调用所有进程
 
notion image
这个是ps -l指令能查看优先级
我们很容易注意到其中的重要信息
UID:执行者身份
PID:执行进程编号
PPID:父进程的编号
PRI:代表优先级,数值越小越先被执行
NI:代表这个进程的nice值
nice值就是优先级的修正值,最终的pri值就是之前的pri加上nice值,当然nice值可以为负值
👋
linux不想让使用者过多的更改优先级,因为有调度器的存在,可以说进程的优先级是很公平的,但是又不能不让我们修改优先级所以有着这样的设定,nice值是有极限-20≤nice≤19
那到底怎么更改优先级呢?
top指令然后输入r,在输入进程的PID,然后就能输入nice值了,但是每次修改nice值的时候,PRI的初始值都是从八十开始算的,q退出
💝
位图:操作系统是如何根据优先级,来开展的调度呢?
在Linux的cpu中加载的进程中,linux会创建一个结构体,里面包含:task_struct* running[140],task_struct *waiting[140],其中[0,99]是不用的所有能用的加起来就是四十个而在pri值中他的范围是[60,99]正好也是四十个,然后在PCB链表中通过PRI值来排列顺序高低,以便PRI值低的能先被遍历到
上一篇
环境变量_进程地址空间
下一篇
The Annotated STL Source 观后感