跳到主要内容

进程管理

老实讲,我不知道咱这个大专会出怎么样的期中题,但是按我上课和师兄的道听途说,就大概是理论+一些习题(比如说接下来的什么进程同步吧,但是感觉占比不高)

下面将会是很无聊的概念,也是我很讨厌的东西。

进程的引入

程序顺序执行的特征

  1. 顺序性:严格按照程序顺序执行
  2. 封闭性:在封闭环境下运行的,
  3. 可再现性

并发执行的概念

警告

并发性,是指多道程序在同一时间间隔同时发生。

程序并发执行的特征

  1. 间断性:程序在并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使这些并发执行的程序之间,形成了相互制约的关系。相互制约将导致并发程序具有“执行——暂停——执行”这种间断性的活动规律。
  2. 失去封闭性
  3. 不可再现性

用程序作为描述其执行过程以及共享资源的基本单位是不合适的。

这就需要一个既能描述程序的执行过程,又能用来共享资源的基本单位,这个基本单位被称为进程。 所以需要进程

进程

给你们看看PPT原文,真的逆天,大便中的大便,不是说他讲不讲的对,而是讲的很难理解,我认为,这玩意应该围绕着PCB(分配的资源等等)和调度来讲,用实际例子来讲通这些xx概念

进程是操作系统中最基本、最重要的概念之一。人们对进程下过许多定义。现列举其中的几种: (1)进程是程序的一次执行过程。 (2)进程是可以和别的进程并发执行的计算。 (3)进程就是一个程序在给定活动空间和初始条件下,在一个处理机上的执行过程。 (4)进程是程序在一个数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单位。 (5)进程是动态的,有生命周期的活动。内核可以创建一个进程,最终将由内核终止该进程使其消亡。 综上观点定义为:“并发执行的程序在一个数据集合上的执行过程” 【运行着的程序】

定义(摘自百度百科)

警告

进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程

image-20230502160847562

进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。

它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元

  • 进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
  • 进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。

特征

  1. 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的
  2. 并发性:任何进程都可以同其他进程一起并发执行
  3. 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
  4. 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
  5. 结构特征:进程由程序数据进程控制块三部分组成。

进程的状态以及切换

image-20230502155539939

一般来说,状态都差不多,课上讲的都是基本的,这边用FreeRTOS的图来讲

  • 创建态
  • 就绪/准备态
  • 运行态
  • 堵塞态
  • 暂停/挂起态
  • 结束进程

image-20230502160403982

进程创建,主要引起创建进程的事件

  • 系统初始化时(整点个性化功能)
  • 用户请求创建一个新进程
  • 正在运行的进程执行了创建的syscall

进程等待,在以下情况下堵塞

  • 请求并等待系统服务,(整点复制粘贴)无法马上完成
  • 启动某种操作,无法马上完成
  • 需要的数据没有到达

进程只能自己堵塞自己,因为只有进程自身才能知道什么时候需要等待某种事件的发生

进程唤醒,唤醒原因:

  • 被堵塞进程需要的资源可以被满足
  • 被堵塞进程等待的事件到达
  • 将该进程的PCB插入就绪队列

进程只能被操作系统或者正在执行的进程里唤醒

进程结束,情形

  • 正常退出
  • 错误退出
  • 致命错误
  • 被其他进程所杀

进程挂起

  • Block阻塞挂起

  • Ready就绪挂起

  • 运行到就绪挂起(被抢了)

  • 堵塞到就绪挂起(挂起)

警告

Linux上,有个僵尸状态,由于某些原因进程被终止,这个进程所占有的资源全部释放之后,还保存着PCB信息,这种占有PCB但已被撤消的进程就处于僵死状态。

进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止。如果子进程已经终止,那么,它的终止代号将告诉父进程这个任务是否已成功地完成。

为了遵循这些设计原则,不允许 Linux 内核在进程一终止后就丢弃包含在进程描述符字段中的数据。只有父进程发出了与被终止的进程相关的 wait() 类系统调用之后,才允许这样做。这就是引入僵死状态的原因:尽管从技术上来说进程已死,但必须保存它的描述符,直到父进程得到通知。

如果一个进程已经终止,但是它的父进程尚未调用 wait() 或 waitpid() 对它进行清理,这时的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程(zombie process)。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。

(From https://cloud.tencent.com/developer/article/1722245)

PCB进程控制块

分为四个组成部分

  • 进程标识符PID 如本进程的标识,本进程的产生者标识(父进程标识);用户标识。(进程的Tag)
  • 处理机状态:保存进程的运行现场信息。也就是上下文信息
    • 用户可见寄存器。用户程序可以使用的数据,地址等寄存器.
    • 控制和状态寄存器。如程序计数器(PC),程序状态字(PSW)
    • 栈指针。过程调用/系统调用/中断处理和返回时需要用到它
  • 进程调度信息
    • 当前状态:作为进程调度和对换的依据
    • 进程优先级:一般来说是整数
    • 调度相关的其他信息
      • 进程已等待CPU的时间、进程已运行的时间等。
    • 事件或者堵塞原因
      • 指进程由执行状态转变为阻塞状态所需要等待发生的事件(难不成进延时后进入堵塞状态就在这?)
  • 进程控制信息
    • 调度和状态信息,用于操作系统调度进程并占用处理机使用。
    • 进程间通讯信息,为支持进程间的与通讯相关的各种标识、信号、信件等,这些信息存在接收方的PCB 中。
    • 存储管理信息,包含有指向本进程的映像存储空间的数据结构
    • 进程所用资源清单,说明有进程打开、使用的系统资源,如打开的文件等。
    • 有关数据结构连接信息,进程可以连接到一个进程队列中,或连接到相关的其他进程的PCB

总而言之,PCB是一个比较复杂的数据,下面是FreeRTOS的任务块

typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //任务堆栈栈顶

xMPU_SETTINGSxMPUSettings; // MPU 相关设置

ListItem_t xStateListItem; //状态列表项
ListItem_t xEventListItem; //事件列表项
UBaseType_t uxPriority; //任务优先级
StackType_t *pxStack; //任务堆栈起始地址
char pcTaskName[configMAX_TASK_NAME_LEN]; //任务名字

StackType_t *pxEndOfStack; //任务堆栈栈底

UBaseType_t uxCriticalNesting; //临界区嵌套深度

#if (configUSE_TRACE_FACILITY == 1) // trace 或到 debug 的时候用到
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif

UBaseType_t uxBasePriority; //任务基础优先级,优先级反转的时候用到
UBaseType_t uxMutexesHeld; //任务获取到的互斥信号量个数


#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0) //与本地存储有关
void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS];
#endif
#if (configGENERATE_RUN_TIME_STATS == 1)
uint32_t ulRunTimeCounter; //用来记录任务运行总时间
#endif
#if (configUSE_NEWLIB_REENTRANT == 1)
struct _reent xNewLib_reent; //定义一个 newlib 结构体变量
#endif
#if (configUSE_TASK_NOTIFICATIONS == 1) //任务通知相关变量
volatile uint32_t ulNotifiedValue; //任务通知值
volatile uint8_t ucNotifyState; //任务通知状态
#endif
#if (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0)
}tskTCB;
警告

关于进程管理的一些细节还有很多想说的,以后上LAB有深刻体验再补充

TODO:

进程和线程的区别和联系

联系

  1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
  2. 资源分配给进程同一进程的所有线程共享该进程的所有资源
  3. 处理机分给线程,即真正在处理机上运行的是线程
  4. 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.

区别

  1. 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位 ,同一进程内线程的切换不会引起进程切换,不同进程的线程切换会引起进程切换。
  2. 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
  3. 拥有资源进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
  4. 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

练习2-2 购物问题。某超级市场,可容纳100个人同时购物,入口处备有篮子,每个购物者可持一个篮子入内购物。出口处结账,并归还篮子(出、入口仅容纳一人通过)。请用P、V操作完成购物同步算法。

这里注意一下,可以设为有100个篮子,但是出入口只有一个人能通过。

所以有一个资源,一个通道的互斥锁

semaphore mutex = 1 
semaphore shoppingBag = 100

void input(){
//先取篮子
P(shoppingBag);//(或者shoppingBag--都行)
V(mutex);//先释放掉互斥锁,退出临界区

//buy

P(mutex);//进入通道临界区

V(shoppingBag);

}
int main(){

}

期中题目

3.在为多道程序所提供的可共享的系统资源不足时,可能出现死锁。但是,不适当的( )也可能产生死锁

A、进程优先权 B、资源的线性分配

C、进程推进顺序 D、分配队列优先权

4.产生死锁的四个必要条件是,互斥、( )、循环等待和不剥夺 A、请求与阻塞 B、请求与保持 C、请求与释放 D、释放与阻塞

  1. 试着比较进程和程序之间的区别。

我的回答:

从内存上看,程序是静态的,由顺序的代码集合组合而成,存储在外存中;而进程是动态的,由程序的实例化而来,其数据存储在内存中,动态可变,具有自己的系统资源和状态。

程序是由一个或者多个进程组合而成的。

chatgpt:

进程和程序是两个相关但不同的概念。

程序是一组指令,用于告诉计算机要执行哪些操作。它通常被保存在磁盘上,并在需要时被加载到内存中。

进程是程序的一个实例,它正在运行并具有自己的内存空间和系统资源。一个程序可以同时运行多个进程,每个进程都有自己的状态和执行上下文。

因此,程序是一个静态的概念,而进程是一个动态的概念。

  1. 什么是线程,试说明它与进程的主要区别。

线程是操作系统能够进行运算调度的最小单位,能够在同一个进程内并发执行多个线程。

与进程的区别:(没有联系)

  1. 调度上看,线程作为调度和分配的基本单位,进程作为拥有资源的基本单位,如果是内核线程,则线程由操作系统调度,如果是用户级线程,则线程在进程内由用户调度,进程的切换会导致线程的切换,线程的切换不会导致进程切换
  2. 拥有资源上看,进程拥有独立的内存资源,线程没有,而多个线程共享它们所属的进程的资源
  3. 开销上看,进程的切换大于线程的切换
  1. 试利用记录型信号写出一个不会出现死锁的哲学家进餐问题算法。(10分)