七、进程和线程

七、进程和线程

进程的定义

进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程。只有当一个程序被OS加载到内存中,cpu对其执行时,这个过程是动态的,称为进程。 

进程的组成

一个进程应该包括:

  • 程序的代码 
  • 程序处理的数据 
  • 程序计数器中的值,指示将运行的指令。 
  • 一组通用寄存器的当前值,堆,栈等; 
  •  一组系统资源,内存资源、文件系统,网络

总之,进程包含了正在运行的一个程序的所有状态信息 

进程与程序的联系 

  • 程序是进程的基础,代码控制操作,
  • 可以多次执行程序,每次构成不同的进程(处理的数据可以不一样,行为不一样)
  • 进程是程序功能的体现
  • 通过多次执行——某一个程序对应多个进程;调用关系——某一个进程可包括多个程序 。多对多的映射关系
进程与程序的区别 
  • 程序是静态,有序代码的集合;进程是动态的,是程序的执行,进程有核心态/用户态。,进程在执行过程中需要完成某些功能,比如读写文件只能由OS完成, 因此是处于核心态。
  • 进程是暂时的,程序永久的; 进程是一个状态变化的过程,程序可永久保存。
  • 组成不同,进程包括程序,数据(可能变化),进程控制块(进程状态信息)。
进程的特点

  • 动态性:可动态的创建、结束进程
  • 并发性:在一段时间内有多个程序在执行,不同于并行,是一个时间点有多个在跑,需多个CPU即多核,进程可以被独立调度并占用处理机运行 。
  • 独立性:不同进程的工作不相互影响 。(如何保障——页表,使不同程序访问不同的地址空间,不能越过页表)
  • 制约性:因访问共享数据/资源或进程间同步产生制约,要同步互斥;


进程的数据结构:进程控制块(process control block,PCB )。OS给每个进程都维护了一个PCB,保存与之有关的所有状态信息。(OS管理PCB,提供动态性、独立性)

进程控制结构

进程控制块(process control block,PCB ):操作系统管理控制进程运行所用的信息集合。OS用PCB描述进程的基本情况和运行变化的过程。PCB是进程存在的唯一标识。

使用进程控制块

  • 进程的创建:为该进程生成一个PCB
  • 进程的终止:回收它的PCB
  • 进程的组织管理:通过对PCB的组织管理来实现

PCB含有三大类信息: 
(1) 进程标识,哪个程序在执行,执行了几次(本进程的标识),产生者标识(父进程标识),用户标识 
(2) 处理机状态信息保存区,主要就是寄存器,保存进程的运行现场信息:

  • 用户可见寄存器,程序使用的数据,地址等寄存器
  • 控制和状态寄存器,程序计数器pc,程序状态字PSW
  • 栈指针,过程调用/系统调用/中断处理和返回时需要用到它

3) 进程控制信息

  • 调度和状态信息,用于操作系统调度进程并占用处理机使用。运行状态?等待?进程当前的执行现状
  • 进程间通信信息,支持进程间的与通信相关各种标识、信号、信件等,这些信息存在接收方的进程控制块中
  • 存储管理信息,包含有本进程映像存储空间的数据结构,内存信息,占了多少?要不要回收?
  • 进程所用资源,由进程打开、使用的系统资源,如打开的文件
  • 有关数据结构连接信息,进程可以连接到一个进程队列,或链接到相关的其他进程的PCB

PCB的组织方式: 

  • 链表(便于插删,用于通用的OS):同一状态的进程其PCB为一链表,多个状态对应多个不同的链表:就绪链表,阻塞链表 
  • 索引表(数组,不利于插删,使用于固定数目的进程,相对创建更快捷):同一状态的进程归入一个index表(由index指向PCB),多个状态对应多个不同的index表,各状态的进行形成不同的index表:就绪索引表、阻塞索引表

进程的状态

进程生命期管理:创建—>运行—>等待—>唤醒—>结束 

1、进程创建:引起进程的3个主要事件

  • 系统的初始化,创建INIT进程,INIT再负责创建其他进程
  • 用户请求创建一个新进程
  • 正在运行的进程执行了创建进程的系统调用

2、进程的运行

内核选择一个就绪的进程,让它占用处理机(cpu)并执行 

3.进程等待

在一下情况下进程等待(阻塞):

  • 请求并等待系统服务,无法马上完成;
  • 启动某种操作(和其他进程协调工作),无法马上完成;
  • 需要的数据没有到达 

进程只能自己阻塞自己,因为只有进程自身才能知道何时需要等待某种事件的发生。

4.进程唤醒

唤醒进程的原因

  • 被阻塞进程需要的资源可被满足
  • 被阻塞进程等待的事件到达
  • 该进程的PCB插入到就绪队列 
因为自身没有占用cpu执行,所以只能被OS或其他进程唤醒 

5.进程结束

进程结束的情形:

  • 正常退出(自愿的)
  • 错误退出(自愿的)
  • 致命错误(强制性的)
  • 被其他进程所杀(强制性的)
进程状态变化模型

进程的三种基本状态:进程在生命结束前处于且仅处于三种基本状态之一

不同系统设置得进程状态数目不同

  • 运行状态(running):当一个进程正在处理机上运行时
  • 就绪状态(ready ):一个进程获得了CPU即处理机之外的一切资源,一旦得到CPU就可以运行
  • 等待状态(阻塞态,blocked):一个进程正在等待某一时间而暂停运行时。如等待某些自愿,等待输入/输出

进程其它的基本状态

  • 创建状态(new) :一个进程正在被创建,还没有被转到就绪状态 之前的状态
  • 结束态(exit) :一个进程正在从系统中消失时的状态,这是因为进程结束或由于其它原因所导致。


可能的状态变化如下:

  • NULL-New:一个新进程被产生出来执行一个程序
  • New-Ready:当进程被创建完成并初始化后,一切就绪准备运行时,变为就绪状态。(该过程很快,完成PCB初始化)
  • Ready-Running:处于就绪状态的进程被进程调度程序选中,就分配到处理机上来运行。(调度器)
  • Running-Exit:当进程表示它已经完成或者因为出错,当前运行进程会由操作系统作结束处理
  • Running-Ready:处于运行状态的进程在其运行过程中,由于分配给它的处理时间片用完而让出处理机。(OS管理时钟)
  • Running-Blocked:当进程请求某样东西必须等待时
  • blocked-Running:当进程要等待某时间到来时,它从阻塞状态变到就绪状态。
进程挂起

进程挂起不同于进程阻塞。进程挂起时没有占用内存空间。处于挂起状态的进程映像在磁盘上。与内存管理有紧密联系,在虚存管理中,有的运行程序的一部分空间会被导到硬盘上,腾出空间给需要的程序。

进程挂起:把一个进程由内存转到外存

挂起状态:

  • 阻塞挂起(blocked suspend )状态:进程在外存并等待某事件的出现
  • 就绪挂起(ready suspend):进程在外存,但只要进入内存就可运行

与挂起相关的状态转换: 

  • 阻塞到阻塞挂起: 没有进程处于就绪状态或就绪进程要求更多内存资源时,就会进行这种转换,以提交新进程或运行就绪进程 。
  • 就绪到就绪挂起:一般就绪进程比阻塞进程优先级高,OS优先选择阻塞转态的进程挂起。但有高优先级阻塞(OS认为会很快就绪)进程和低优先就绪进程时,挂低优先的就绪进程 
  • 运行到就绪挂起: 对抢先式分时系统,正在运行的进程空间不够或者当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行的进程转到就绪挂起状态

在外存时的状态转换:

  • 阻塞挂起到就绪挂起:当有阻塞挂起进程因相关事件出现时,系统会把阻塞挂起进程转换为就绪挂起进程

解挂/激活(Activate):把一个进程从外存转到内存,可能有一下几种情况

  • 就绪挂起到就绪:没有就绪进程或者就绪挂起进程优先级高于就绪进程时,会进行这种转换
  • 阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起(系统会认为很快出现所等待的事件)进程转换为阻塞进程。
状态队列

OS怎么通过PCB和定义的进程来管理PCB,帮助完成进程的调度?

 用进程的观点来看待OS的管理:用户进程、磁盘管理进程、终端进程.......


以进程为基本结构的OS:最底层为CPU调度程序(包括中断处理);上面一层为一组各式各样的进程

状态队列

  • OS要维护一组状态队列,表示系统中所有进程的当前状态。 
  • 不同的状态分别用不同的队列来表示(就绪队列,各种类型的阻塞队列,挂起队列 )
  • 每个进程的PCB根据它的状态加入到相应的队列中,当一个进程的状态变化,它的PCB从一个状态队列中脱离出来,加入到另一个队列。


就绪队列也分优先级。当事件1发生了,队列1可能整个都变为就绪转态,也可能只有一个进程变为就绪态。

线程(Thread)管理

更小的能独立运行的基本单位——线程 

为什么引入线程

【案例】编写一个MP3播放软件。核心功能模块有三个:

(1)从MP3音频文件当中读取数据

(2)对数据进行解压缩

(3)把解压缩后的音频数据播放出来



问题:进程之间如何通信,共享数据?另外,维护进程的系统开销较大;创建进程时,分配资源,建立PCB;撤销进程时,回收资源、撤销PCB;进程切换时,保存当前进程的状态信息。

怎么解决这些问题?

需要提出一种新的实体,满足一下特性:

  • 实体之间可以并发的执行
  • 实体之间共享相同的地址空间

这种实体就是:线程

线程:进程当中的一条执行流程

重新理解进程,由两部分功能组成: 

  • 从资源组合的角度:进程把一组相关资源组合起来,构成一个资源平台(环境),包括地址空间(代码段、数据段)、打开的文件等各种资源
  • 从运行的角度:代码在这个资源平台上的一条执行流程


线程有自己的TCB(hread control block) 只负责这条流程的信息,包括PC程序计数器,SP堆栈,State状态,和寄存器。有不同的控制流,需要不同的寄存器来表示控制流的执行状态,每个线程有独立的这些信息,但共享一些资源,包括代码段、数据。

线程=进程-共享资源 

  • 线程是控制流,一个进程中可以同时存在多个线程
  • 各个线程并发执行,
  • 各个线程共享地址空间和文件等资源。 

缺点:

  • 如果一个线程写错了,崩溃,如破坏了数据,会导致这个进程的所有线程都崩溃。

适用范围:高精计算中比如天气预报,代码相对统一,对性能要求高,用线程;网络有关如开一个浏览器的N个页面,如果用线程一个页面有问题所有的都关闭了,现在都改成了用进程。如Chrome,用的就是一个进程打开一个网页。

线程所需资源

  • 多线程,在进程空间内有多个控制流且执行流程不一样,有各自独立的寄存器和堆栈,但共享代码段,数据段,文件。 

线程与进程的比较

  • 进程是资源分配单位,线程是CPU调度单位

  • 进程拥有一个完整的资源平台,而线程只独享指令流执行的必要资源,如寄存器和栈

  • 线程具有就绪、等待和运行三种基本状态和状态间的转换关系

线程能减少并发执行的时间和空间开销

  1. 线程的创建时间比进程短(进程创建需要创建其他的管理,如内存管理、打开文件管理。而线程之接重用线程管理好的资源)

  2. 线程的终止时间比进程短(管理资源的释放)

  3. 同一进程内的线程切换时间比进程短(同一进程的线程具有同一个地址空间,即同一个页表。进程切换需要切换页表,开销很大)

  4. 由于同一进程的各线程间共享内存和文件资源,可不通过内核进行直接通信(线程之间的数据传递不需要通过内核,直接通过内存地址访问)

线程的实现

线程的三种实现方式 

  • 用户线程:在用户空间实现,OS看不到,由应用程序的用户线程库来管理;POSIX Pthreads, Mach C-threads ,Solaris threads
  • 内核线程:在内核中实现,OS管理的;Windows ,Solaris, Linux
  • 轻量级进程(lightweight process):内核中实现,支持用户线程。Solaris, Linux

用户线程与内核线程的对应关系:

  • 多对一:n个用户线程对应一个内核线程
  • 一对一:一个用户线程对应一个内核线程
  • n对m:n个用户线程对应m个内核线程

用户线程


  • 在用户空间实现的线程机制,不依赖于OS内核,由一组用户级的线程库函数来完成线程的管理,包括线程创建、终止、同步和调度等。

优点:

  • 由于用户线程的维护由相应的进程来完成(通过线程库函数),不需要OS系统内核了解用户线程的存在,可用于不支持线程技术的多进程的OS
  • 每个进程都需要它自己私有的线程控制块(TCB)列表,用来记录它的各个线程的状态信息(PC,栈指针、寄存器),TCB由线程库函数来维护
  • 用户线程的切换也是由库函数来完成,无需用户态/核心态切换,所以速度特别快
  • 允许每个进程有自定义的线程调度算法

缺点

  • 一个线程发起系统调用而阻塞,那整个进程都在等待(OS只能看到进程,只能阻塞进程)
  • 一个线程开始运行后,除非主动交出CPU使用权,否则所在进程中的其他线程都无法运行;(只能通过时钟中断由OS完成切换)
  • 由于时间片分配给进程,固与其他进程相比,在多线程执行时,每个线程得到的时间片较少,执行会很慢

内核线程


是指在OS的内核当中实现的一种线程机制,由OS内核完成线程的创建、终止和管理

  • 在支持内核线程的OS中,由内核来维护进程和线程的上下文信息(PCB和TCB)
  • 线程的创建,终止和切换都是通过系统调用/内核函数的方式来进行,由内核完成,因此系统开销较大
  • 在一个进程中,如果某个内核线程发起系统调用而被阻塞,不影响其他线程运行
  • 时间片分配给线程,多线程的进程自动获得更多CPU时间
  • Windows NT/2000/XP支持内核线程

缺点:线程的切换要完成用户态到内核态的切换,开销大

优点:力度小

轻量级进程

它是内核支持的用户线程。一个进程有一个或多个轻量级进程,每个轻量级进程由一个单独的内核线程来支持。


进程上下文切换( context switch

停止当前进程(从运行状态改变成其他状态)并调度其他进程(转变成运行状态)切换叫做上下文切换。 

  • 必须切换前存储许多部分的进程上下文,
  • 必须能够在之后恢复它们,所以进程不能显示它曾经被暂停过
  • 必须快速(上下文切换频繁),
需要存储那些上下文?

  • 寄存器(pc,sp,…),CPU状态...
  • 有时候可能会费时,所以我们应该尽可能避免

进程执行中要关注寄存器,如PC(程序计数器,进程执行到了什么地方),栈指针(调用关系,相应的局部变量位置)等。 上下文切换时,这些信息要被保存到PCB中,运行另一个进程时需要把这个进程的PCB中的上下文恢复到寄存器中去,使接下来进程可以继续在CPU上执行。


上下文切换的开销越小越好,且所有信息都与硬件紧密相连,所以OS中实现是用汇编代码。

  • OS为活跃进程准备了进程控制块(PCB)
  • OS将进程控制块(PCB)放置在一个合适的队列里
  1. 就绪队列
  2. 等待I/O队列(每个设备的队列)
  3.  僵尸队列
进行控制

创建进程

  • windows进程创建API:CreateProcess(filename)
  • Unix进程创建系统调用:fork/exec

  1. fork()把一个进程复制成二个进程,parent(old PID),chile(new PID)
  2. exec()用新程序来重写当前进程,PID没有改变


fork() 创建一个继承的子进程

  • 复制父进程的所有变量和内存

  • 复制父进程的所有CPU寄存器(有一个寄存器childpid例外,下面讲到)

fork()的返回值

  • 子进程的fork()返回0

  • 父进程的fork()返回子进程标识符

  • 如果出现错误,fork返回一个负值;

  • fork() 返回值可方便后续使用,子进程可使用getpid()获取PID

fork()的地址空间复制

  • fork()执行过程对于子进程而言,是在调用时间对父进程地址空间的一次复制

  • 对于父进程fork()返回child PID, 对于子进程childPID返回值为0


fork()的使用示例



注意fork()的执行顺序

exec:系统调用exec( )加载新程序取代当前运行进程

在调用执行fork之后父进程与子进程一样(只有pid不一样),在系统调用exec()之后,会加载新程序calc,PCB信息会发生变化,用户态内存空间变化(代码变化)


fork()之后


exec()之后



  • exec()允许一个进程加载一个不同段程序并在main()执行
  • 它允许一个进程指定参数的数量argc和字符串参数数组argv
  • 允许进程指定不同的控制流,可指定不同的应用程序,如果调用成功它是相同进程,运行的是不同的程序!
  • 代码、stack,heap要重写

fork()的简单实现: 

  • 对子进程分配内存
  • 复制父进程的内存和CPU寄存器到子进程中
  • 开销大大大! 

99%的情况下,我们调用fork()之后调用exec()

  • 在fork()操作中内存赋值是没有作用的
  • 子进程将可能关闭打开的文件和连接
  • 开销高
  • 为什么不能结合它们在一个调用中

vfork()

  • 一个创建进程的系统调用,不用创建一个同样的内存映像
  • 一些时候称为轻量级fork,只是复制了一小部分父进程的内容
  • 子进程应该几乎立即调用exec
  • 现在不再使用,如果采用了copy on write(COW) 技术。(通过OS的虚存管理,只复制了父进程meta元数据即页表,指向的是同一地址空间,当对某个地址单元进行写操作时触发异常,使父子各把要用的页复制成两份,使父进程和子进程拥有不同的地址。按需,光读不用复制,写时才复制)
进程的等待和终止

wait()系统调用是被父进程用来等待子进程的结束

  • 一个子进程向父进程返回一个值,所以父进程必须接受这个值并处理。 
  • wait()系统调用担任这个要求
  • 为什么要让父进程等子进程?而不是直接结束? 当子进程执行完毕退出后,几乎所有用户态资源都回收。但有个资源很难回收,就是PCB,存在于内核空间,PCB是代表进程存在的唯一标识,OS要依据PCB执行回收。这个功能由父进程完成。

  1. 它使父进程睡眠来等待子进程的结果
  2. 当子进程调用exit()时,操作系统解锁父进程,并且将通过exit()传递得到的返回值作为wait调用的一个结果(连同子进程的pid一起)。如果这里没有子进程存活,wait()立刻返回
  3. 当然,如果这里有子进程的僵死等待,wait()立即返回其中一个值(并且解除僵死状态)

  • 进程结束执行之后,它调用exit()
  • 这个系统调用

  1. 将这个程序的“结果”作为一个参数
  2. 关闭所有打开的文件、连接等等
  3. 释放内存
  4. 释放大部分支持进程的OS结构
  5. 检查是否父进程是存活的,如果是的话,它保留结构的值直到父进程需要它;在这种情况下,进程没有真实死亡,而是进入了僵死(zombie)状态。如果没有,它释放所有的数据结构,进程结果
  6. 清理所有等待的僵死进程(由INIT进程定期扫描进程控制块,回收僵死进程)
  • 进程终止是最终的垃圾收集(资源回收)


执行exec()时,进程可以处于不同的状态。首先是runnig, 然后加载、运行,加载时间长就需要等待,会从running->blocked。 











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值