进程知识详解

进程

进程概念

进程的概念

进程的就是运行起来的程序,OS是根据进程来分配资源的。一个进程主要是由进程控制块(PCB)和程序代码构成的。
这里的PCB由操作系统创建并管理,PCB本质上就是一个结构体,Linux中的PCB叫做task_struct。
进程=进程控制块(task_struct)+程序代码和数据
下面我们就来深入探究一下进程。

我们接触电子设备这么久了,例如手机、电脑中的游戏,又或是qq、微信,大家想过这些程序是怎么运行起来并让我们使用的吗?首先我们从互联网上下载的程序装到手机或者电脑上,实际上是把这些程序的可执行程序装载到了我们的磁盘上,当我们启动某个已经装好的程序的时候,OS会把该可执行程序加载到我们的内存当中去并且生成该可执行程序的PCB,然后CPU就会对该进程来进行调度执行。可是我们为什么要有pcb啊?直接把程序数据和代码装载进去不就行了为什么需要多开辟空间来维护pcb呢?这不是消耗了我们的内存空间了吗?我们从生活的角度来思考这个问题,假设小明是一个刚考上大学的学生,开学那天小明兴高采烈的去往了学校,从踏进学校大门的那一刻起他就是一个大学生了,他也正常的上学放学,上了四年之后大家都去领毕业证了,结果小明从早等到晚一天都过去了,还是没有自己的毕业证!这个时候小明就去问了,怎么没有我的毕业证?问了之后才发现,原来小明根本就不在学校的学校系统里面!这个时候小明才反应过来,怪不得进学校这么自由没人管,原来是根本就没有进到学校的管理系统里面!这里的小明就是程序,学校管理系统里面小明的属性就是pcb,学校就是操作系统。正常情况下小明在被录取的时候就会加进学校的管理系统当中,从而生成小明的各个属性,这样小明才会被学校进行管理,学校就会分配他的辅导员会给他安排课程…这个时候小明就真正成为了学校的一名学生。
所以说,pcb其实就是维护了程序的各个属性,为什么要pcb呢!答案就是 先描述,再组织! OS把程序的信息用数据结构维护起来,把pcb以链表的方式连接起来,OS只需要对pcb进行管理就实现了对进程的管理,对代码和数据的管理!进程会被根据task_struct属性 被OS调度器调度,运行!

进程状态

并行和并发

  • cpu执行进程代码,不是把进程代码执行完毕,而是给每一个进程分配一个时间片,cpu基于时间片来进行轮转调度,这是并发
  • 多个进程在多个cpu下分别,同时进行运行,叫做并行

时间片

民用级操作系统例如windows,linux 一般采用分时操作系统 : 调度任务追求公平
系统为每一个进程分配一个时间片 每个进程执行完时间片就会调度到下一个进程

进程具有独立性

进程具有很强的独立性,每个进程间互不影响

等待的本质

进程创建到销毁的过程如下
进程被创建后 对应的pcb被链入到运行队列 这个时候就说明该进程已经准备好了处于就绪态 (有争议点 实际上运行态和就绪态是一个概念)
当cpu轮转调度到该进程的时候会把cpu资源给到该进程,该进程转换为运行态;
我们这里把就绪态和运行态统称为运行态
当进程处于运行态的时候 系统发生了某些事件如I/O请求 该进程会进入阻塞态 直到I/O完成才会被重新链入到运行队列
那么阻塞是如何实现的呢?
OS管理硬件资源的核心思想是 先描述,再组织 OS会为每个硬件资源创建一个结构体来描述该资源的信息,其中会创建一个wait等待队列
当进程申请该硬件资源的时候会被链入到该硬件资源中的wait队列中 当该硬件完成响应后OS会把该进程重新连到运行队列当中
例如一个进程需要申请键盘操作 输入一个字符 那么当运行到该操作的时候 该进程会被链到键盘的wait队列中,直到键盘输入一个字符
综上所述 运行和阻塞的本质是在不同的队列中进行等待
运行态就是在运行队列中等待
阻塞态就是在某个硬件资源的wait队列中进行等待

挂起

当进程处于阻塞态中的时候,如果内存资源出现严重不足,OS会首先确保自身的安全,会把阻塞队列当中的进程暂时换入到磁盘当中
等到内存资源足够后再换入到内存中 换入到磁盘中的区域叫做swap交换区
换入换出需要时间消耗 这是时间换空间的操作

R

运行状态 R+就是前台运行状态 R就是后台运行状态
前台运行状态可以用ctrl+c停止运行 也可以kill杀掉 后台运行只能被kill杀掉

S:

休眠状态(浅睡眠) 阻塞等待的状态 可以被杀掉
例如某个进程申请了键盘资源但迟迟未获得 就会进入s状态

D:disk-sleep

disk磁盘 深度睡眠,不可中断睡眠 例如一个银行一天产生了100w条交易信息 要存放到磁盘当中去 当内存资源严重不足的时候 os会杀掉一些进程 如果杀掉了网磁盘中存放数据的进程 那么就会造成重大失误 D状态可以直接避免被杀掉

T

进程做了非法但不致命的操作 就是T状态

t

进程被追踪了 也是t状态 例如gdb断点

X

死亡状态

Z

僵尸状态 进程结束到死亡的过程 : 用来维持退出信息,方便父进程和OS来查询

为什么要有X状态

为什么要有X状态 按理说进程死亡了不应该消失吗?为什么对应的信息还在?
因为每一个进程都是需要创建PCB的 pcb是一个结构体 各个进程pcb内部存储的数据类型都是一样的
而pcb的创建是需要不断的malloc free的这样会消耗资源
有了X状态 我们就可以在进程死亡的时候把状态设置为X 然后把该进程的pcb链入到一个空闲队列
等到下次要创建新的pcb的时候直接修改空闲队列pcb的属性然后链入到运行队列当中

进程切换

进程优先级

进程优先级:获得某种资源的先后顺序

为什么要有进程优先级?

本质是目标设备资源较少 例如(cpu,磁盘,I/O….)

区分优先级和权限:
优先级是已经获得了除cpu意外的其他资源,已经进入了运行队列进行等待,优先级决定的是谁先谁后的问题,而权限是决定能不能的问题

在linux当中 进程优先级在task_struct中用几个int类型的变量来表示进程优先级
数字越小,进程优先级越高
linux中进程优先级 = pri(旧优先级)(default:80)+nice值
linux中允许程序员进行修改的是nice值,从而来保证linux调度的公平性
pri是由操作系统自己根据进程的类型来给出的
nice值允许我们修改的范围是[-20,19],实际上nice值的范围是[60,99]从而确保我们修改后的进程优先级依然保持在可控范围内,不影响系统调度整体的公平性。

补充:uid —> 进程的启动者

linux会为没个用户名计算一个uid来记录当前进程的启动者
意义是什么?:
首先linux下一切皆文件
当我们用命令创建一个文件的时候,命令也相当于一个进程
那么该进程就会记录下当前创建文件的uid也就是启动该进程的用户
通过该uid就会记录下该文件拥有者,所属组,和相应的权限
访问某个文件的时候也同理,系统会比较启动访问命令的进程uid和文件权限用户
相同则允许访问 不同则拒绝访问

修改nice值的方式:

指令和代码 os禁止频繁修改并且也不建议修改的所以不做介绍

进程切换的过程

1.理解进程切换

前置知识:
在linux中 进程时间片跑完了就要被切换
linux是基于时间片来进行轮转调度的
一个进程在时间片到了的时候 并不一定是跑完了 可以在任何时候进行调度切换
(如何实现的?)
理解:进程在时间片跑完或者退出的时候 会进行进程的切换,在调度的时候当前未跑完的进程会保存当前cpu的上下文,上下文就是当前cpu中各个寄存器的值(ir,pc,eax,ebx……)保存到task_struct中的tss字段当中,当该进程被重新调度的时候会把这些上下文重新给cpu进行进一步的运行。实际上切换的主要工作就是:1.保存上下文数据 2.恢复上下文数据 。

2.切换过程

首先,当进程运行在cpu的时候会产生很多的临时数据存放在cpu的寄存器当中,这些寄存器的数据就是上下文。
常见的寄存器:pc :当前正在执行指令的下一条指令
ir : 指令寄存器,就是当前正在执行的指令。
并且 这些寄存器的数据 都是进程执行时的瞬时状态信息数据
cpu内部只有一套寄存器,但是寄存器 != 寄存器中的数据
进程切换的核心就是上下文的保存和恢复
切走:将寄存器中的内容保存起来
切回:将历史寄存器的数据恢复到寄存器当中来
保证每次切换时,每次保存完上下文数据的时候,cpu都是全新的

这些上下文保存在哪里呢?肯定是在内存,因为已经移出cpu内部了
由于每个进程都要保存自己的上下文数据,linux内核于是把上下文数据直接存放于pcb(task_struct)中的 struct tss_struct tss(任务状态段)中

linux真实的调度算法

linux中进程的运行队列中存在这样的数据结构struct queue[140],我们这里简单称为优先级表,这样的表runqueue中有两个
两张表分别为active活跃表,expired过期表
cpu只会从active活跃表中进行进程调度,调度有三种情况
a.运行退出
b.不退出,但是时间片到了
c.有新的进程产生了
为什么要有这两张表呢?假设只有一张表来进行进程的调度,当一个高优先级的进程在执行完它的时间片后如果没有退出,那该进程就会重新进入高优先级的排队队列当中,直到进程执行完退出。并且在高优先级进程重复排队运行的过程中,可能会产生其他高优先级的进程加入到运行队列当中,那么就要等到这些进程全部都运行完退出后,那么低优先级的进程很有可能很久不被运行,一直处于排队的过程当中,出现饥饿现象。
linux的解决办法是用两张表,操作系统会在active表中进行调度,当一个进程的时间片结束后或者有新的进程产生,该进程不会被重新链入到active表中,而是会被链入到expired过期表中。这样的操作的意义是,随着os的调度,active表中的进程会越来越少,这样就给了低优先级进程调度机会,极大程度避免了饥饿现象。当active表中的进程=0时,就会交换两张表的数据
expired表和active表中的结构属性一摸一样,除了queue表纸玩还有nr_active和int bit_map[5]两个主要属性。nr_active记录当前进程数,=0时进行swap。bit_map[5]充当位图, bitmap实际上占了内存32*5=160位,这160位刚好覆盖了queue[140]个位置,cpu在active表中进行调度的时候,还是要遍历表中的一个一个数据,是On的算法。用bitmap充当位图,把每一位作为数组下表进行遍历。linux给出的方案是

for(int i=0;i<5;i++)
{
	for(int j=0;j<32;j++)
	{
	}
}

这样的方案可以一次性检测32个进程
这就是linux内核的O(1)调度算法

linux进程都是依靠链表连接的。
首先我们知道进程都会有独立的task_struct,所有的task _struct组成了一个全局的链表,但是这些进程又各自处于例如运行队列,阻塞队列……
实现:linux连接pcb的数据结构只有链接字段没有属性字段

struct node
{
	struct node* next;
	struct node* prev;
}
struct task_struct
{
	struct node listnode;
	struct node runqueue;
}

通过在pcb中添加属性,就可以链接到其他队列当中。这样就实现了一个进程既可以在全局的链表当中又可以在任意的一个数据结构当中,只需要添加字段即可。
我们知道struct的内存模型是从低到高的
例如struct A{int a;int b;}那么a是低地址并和A同地址,b是高地址
那么如何通过pcb中的一个一个链表地址来访问其他数据呢?
通过struct的地址模型可以得知,所有属性的地址都是想对于A的地址的,我们只要知道字段属性的偏移量就可以知道struct的首地址就可以访问其他属性。

命令行参数

int main(int argc,char argv[])
我们在linux命令行中命令时常会带选项 例如ls -a -l -n,这一串的命令加选项其实是字符串,首先这个字符串会给到shell进行处理,shell会按照空格打散,形成一张表argv和元素个数argc。那么同一个程序可以根据命令行参数,按照选项的不同,表现出不同的功能,比如指令的实现。
main中还有第三个参数char
env[]用来存储环境变量,环境变量是按照key,value形式存储的,具有全局属性的表量叫做全局变量。

进程退出

我们知道进程退出有两种情况,正常退出和非正常退出。正常退出的进程会给父进程返回退出码0,非正常退出会返回非0。系统中提供了一些退出码来告诉上层错误的信息。

进程正常退出

进程在执行完毕后,可以调用exit()函数来正常退出,exit()函数会执行一些清理操作,并把退出码返回给操作系统,通常返回0表示成功退出,非0即出现错误。

异常退出

进程在执行过程中,如果遇到无法继续运行的异常情况,就会异常退出。

错误码

错误码:错误码是用来给上层返回函数的调用情况,当函数调用出现问题的时候,会返回一个错误码。errno(全局变量)的含义是最近执行函数的一个错误。我们这边代码打开一个不存在的文件,按照预期来说会fopen内部会出现错误,当程序执行到错误代码的时候程序就会提取到错误码,通过strerror可以将错误码转换成错误信息字符串
在这里插入图片描述
在这里插入图片描述

退出码

进程具有正常退出和异常推出两种情况,退出码用来向父进程传递进程执行的结果。

正常退出:main函数执行完毕返回0,可以用exit()或是_exit()来进行退出。exit()是c语言库封装的函数接口,调用可以对程序进行一些清理工作例如清空缓冲区,_exit()是系统调用会直接将进程退出。

异常退出:进程收到某种信号退出。

退出码可以使用系统约定的错误码,也可以自己约定退出码。

进程等待

对于创建出来的子进程,父进程必须要负责回收子进程,父进程就要等待子进程,直到子进程结束。wait(int* status)函数用来等待任意一个子进程结束。

wait函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值