# Table of Contents * [1 前趋图](#1-前趋图) * [2 程序执行特征](#2-程序执行特征) * [2.1 顺序执行的特征](#21-顺序执行的特征) * [2.2 并发执行的特征](#22-并发执行的特征) * [3 进程](#3-进程) * [3.1 定义](#31-定义) * [3.2 组成](#32-组成) * [3.3 特征](#33-特征) * [3.4 基本状态](#34-基本状态) * [3.5 基本状态的切换](#35-基本状态的切换) * [3.6 创建与终止状态](#36-创建与终止状态) * [3.7 挂起与激活](#37-挂起与激活) * [4 `PCB`](#4-pcb) * [4.1 `PCB`定义](#41-pcb定义) * [4.2 作用](#42-作用) * [4.3 组成](#43-组成) * [4.3.1 进程标识符](#431-进程标识符) * [4.3.2 处理机状态](#432-处理机状态) * [4.3.3 进程调度信息](#433-进程调度信息) * [4.3.4 进程控制信息](#434-进程控制信息) * [5 用户态与系统态](#5-用户态与系统态) * [6 `OS`内核功能](#6-os内核功能) * [6.1 支撑功能](#61-支撑功能) * [6.2 资源管理功能](#62-资源管理功能) * [7 进程创建](#7-进程创建) * [7.1 引起进程创建的事件](#71-引起进程创建的事件) * [7.2 创建过程](#72-创建过程) * [8 进程终止](#8-进程终止) * [8.1 引起终止的事件](#81-引起终止的事件) * [8.2 终止过程](#82-终止过程) * [9 阻塞/唤醒/挂起/激活](#9-阻塞唤醒挂起激活) * [9.1 阻塞与唤醒](#91-阻塞与唤醒) * [9.1.1 引起阻塞与唤醒的事件](#911-引起阻塞与唤醒的事件) * [9.1.2 阻塞过程](#912-阻塞过程) * [9.1.3 唤醒过程](#913-唤醒过程) * [9.2 挂起与激活](#92-挂起与激活) * [9.2.1 挂起过程](#921-挂起过程) * [9.2.2 激活过程](#922-激活过程) * [10 进程同步](#10-进程同步) * [10.1 制约关系](#101-制约关系) * [10.2 同步机制应该遵循的原则](#102-同步机制应该遵循的原则) * [11 实现互斥](#11-实现互斥) * [11.1 硬件同步机制](#111-硬件同步机制) * [11.2 信号量机制](#112-信号量机制) * [11.2.1 整型信号量](#1121-整型信号量) * [11.2.2 记录型信号量](#1122-记录型信号量) * [11.2.3 `AND`型信号量](#1123-and型信号量) * [11.2.4 信号量集](#1124-信号量集) * [12 信号量集应用](#12-信号量集应用) * [12.1 实现互斥操作](#121-实现互斥操作) * [12.2 实现前趋关系](#122-实现前趋关系) * [13 管程](#13-管程) * [13.1 定义](#131-定义) * [13.2 特性](#132-特性) * [14 进程通信方式](#14-进程通信方式) * [14.1 管道/匿名管道](#141-管道匿名管道) * [14.2 有名管道](#142-有名管道) * [14.3 信号](#143-信号) * [14.4 消息队列](#144-消息队列) * [14.5 共享内存](#145-共享内存) * [14.6 信号量](#146-信号量) * [14.7 套接字](#147-套接字) * [14.7.1 域](#1471-域) * [14.7.2 端口号](#1472-端口号) * [14.7.3 协议类型](#1473-协议类型) * [14.7.4 套接字建立过程](#1474-套接字建立过程) * [15 线程](#15-线程) * [15.1 线程引入](#151-线程引入) * [15.2 线程的三个状态](#152-线程的三个状态) * [15.3 `TCB`](#153-tcb) * [15.4 线程实现方式](#154-线程实现方式) * [15.4.1 内核支持线程](#1541-内核支持线程) * [15.4.2 用户级线程](#1542-用户级线程) * [15.4.3 组合方式](#1543-组合方式) * [15.5 线程实现](#155-线程实现) * [15.5.1 `KTS`实现](#1551-kts实现) * [15.5.2 `ULS`实现](#1552-uls实现) * [15.5.2.1 运行时系统](#15521-运行时系统) * [15.5.2.2 内核控制线程](#15522-内核控制线程) * [15.6 线程创建与终止](#156-线程创建与终止) * [15.7 线程通信方式](#157-线程通信方式) * [16 进程与线程比较](#16-进程与线程比较) * [17 线程与协程比较](#17-线程与协程比较) # 1 前趋图 一个有向无循环图,用来描述程序段或进程之间执行的先后次序关系。每个结点表示一个程序段或一个进程,结点间的有向边用来表示两个节点之间存在的偏序或前趋关系。 基本术语: - 初始结点:没有前趋结点的结点 - 终止结点:没有后继结点的结点 前趋图**不允许循环**存在。 # 2 程序执行特征 ## 2.1 顺序执行的特征 - **顺序性** - **封闭性** - **可再现性** ## 2.2 并发执行的特征 - **间断性** - **失去封闭性** - **不可再现性** # 3 进程 ## 3.1 定义 无统一定义,常见定义如下: - **进程是程序的一次执行** - 进程是一个程序及其数据在处理机上顺序执行时所发生的活动 - **进程是具有独立功能的程序在一个数据集合上运行的过程,是系统进行资源分配和调度的一个独立单位** ## 3.2 组成 **进程=程序段+数据段+PCB**。 ## 3.3 特征 - **动态性**:进程是程序的一次执行,动态的 - **并发性**:多个进程实体同存于内存中,且能在一段时间内同时执行 - **独立性**:进程实体是一个能够独立运行、独立分配资源和独立接受调度的基本单位 - **异步性**:进程可按各自独立、不可预知的速度向前推进 ## 3.4 基本状态 三种: - **就绪状态**:进程已获得除`CPU`外的所有资源,只要得到`CPU`,便可立即执行 - **执行状态**:进程已获得`CPU`,某程序正在`CPU`上执行 - **阻塞状态**:正在执行的进程因某种事件的发生而暂时无法继续执行 ## 3.5 基本状态的切换 如图所示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200917125452325.png) - 对单个进程而已任何时候只能处于某种状态之中 - `就绪->执行`:进程调度获得`CPU`后便由就绪转为执行状态 - `执行->就绪`:时间片完则由执行转为就绪状态 - `执行->阻塞`:等待某种事件(比如`I/O`)则由执行转为阻塞状态 - `阻塞->就绪`:等待的事件完成就由阻塞转为就绪状态 ## 3.6 创建与终止状态 - 创建状态:进程由创建而产生,引入创建状态是为了保证进程的调度在创建之后进行,创建的简要流程是首先申请一个空白`PCB`,接着向其中填写用于控制和管理进程的信息,然后为该进程分配运行时所必须的资源,最后把该进程转入就绪状态并插入就绪队列中 - 终止状态:首先等待操作系统善后处理,最后将`PCB`清零并归还`PCB` 加入后的关系转换图如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200917130701754.png) ## 3.7 挂起与激活 挂起的是指是进程不能被继续执行,被挂起的进程处于静止状态,没被挂起的叫活动状态。 处于静止状态的进程,只有通过激活操作才能变为活动状态。 转换图如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200917131506515.png) # 4 `PCB` ## 4.1 `PCB`定义 `Process Control Block`,`进程控制块`,是进程实体的一部分,在`PCB`中记录了系统所需的,用于描述进程的当前情况以及管理进程运行的全部信息。 ## 4.2 作用 - **作为独立运行基本单位的标志:换句话说,程序与进程的区别是`PCB`,`PCB`是进程存在的唯一标志** - 能实现间断性运行方式:供恢复现场使用 - 提供进程管理所需要的信息:根据`PCB`对进程的程序、数据以及所需要的资源进行管理 - 提供进程调度所需要的信息:`PCB`描述了进程的状态,处于就绪状态的才能被调度 - 实现与其他进程的同步与通信 ## 4.3 组成 由四部分组成: - 进程标识符 - 处理机状态 - 进程调度信息 - 进程控制信息 ### 4.3.1 进程标识符 进程标识符用于唯一标识一个进程,通常有两种标识符: - 外部标识符:由创建者提供,通常由字母+数字组成 - 内部标识符:为了方便系统调用的内部唯一数字标识符 ### 4.3.2 处理机状态 也叫处理机上下文,主要是由处理机的各种寄存器中的内容组成,其中寄存器包括: - 通用寄存器:用户程序可访问的暂存信息的寄存器 - 指令计数器:存放了要访问的下一条指令的地址 - `PSW`:程序状态字,包含状态信息,比如条件码、执行方式等 - 用户栈指针:存放过程和系统调用参数及调用地址 ### 4.3.3 进程调度信息 包括: - 进程状态:进程当前的状态 - 进程优先级:描述进程使用的处理机优先级的一个整数 - 进程调度的其他信息:比如进程已执行的时间总和等 - 事件:进程由执行状态转变为阻塞状态锁等待发生的事件,即阻塞原因 ### 4.3.4 进程控制信息 用于进程控制所必须的信息,包括: - 程序和数据的地址:`PCB`从这些地址找到要执行的程序和所需要的数据 - 进程同步和通信机制:比如消息队列指针和信号量等 - 资源清单:列出运行期间所需要的全部资源 - 链接指针:下一个进程`PCB`的首地址 # 5 用户态与系统态 处理机的执行状态分为系统态以及用户态: - **系统态**:又称为管态,也叫内核态,具有较高的特权,能执行一切指令,访问所有寄存器和存储区 - **用户态**:又叫目态,具有较低特权的执行状态,仅能执行规定的指令 # 6 `OS`内核功能 两大功能: - 支撑功能 - 资源管理功能 ## 6.1 支撑功能 包括: - 中断处理:内核最基本的功能,整个操作系统赖以活动的基础 - 时钟管理:比如在时间片轮转调度算法中使用到了时钟管理 - 原语作业:原语就是由若干条指令组成的,用于完成一定功能的一个过程,它们是原子操作,也就是一个操作中的所有动作要么全做要么全不做 ## 6.2 资源管理功能 包括: - 进程管理 - 存储器管理 - 设备管理 # 7 进程创建 ## 7.1 引起进程创建的事件 - 用户登录:分时系统中用户登录会为该用户创建一个进程 - 作业调度:多道批处理系统中将作业装入内存会为作业创建进程 - 提供服务:用户程序提出某个请求后系统自动为其创建一个进程 - 应用请求:由用户自己创建进程 ## 7.2 创建过程 - 申请空白`PCB`:为新进程申请获得唯一的数字标识符,并从`PCB`集合中索取一个空白`PCB` - 为新进程分配运行所需的资源:包括各种物理资源、逻辑资源、内存、文件等 - 初始化`PCB`:包括初始化标识信息、初始化处理机状态信息、初始化处理机控制信息 - 插入就绪队列:如果就绪队列能够接纳新进程则将新进程插入就绪队列 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200924085958320.png) # 8 进程终止 ## 8.1 引起终止的事件 引起终止的事件有三种:`正常结束`、`异常结束`以及`外界干预`,正常结束就是正常结束的流程,而异常结束就是出现了某种异常而结束,包括: - 越界错 - 保护错 - 非法指令 - 特权指令错 - 运行超时 - 等待超时 - 算术运算错 - `I/O`故障 等等。而外界干预就是受到外界的请求而终止,比如: - 操作员或操作系统干预 - 父进程请求 - 父进程终止 ## 8.2 终止过程 - 根据被终止的标识符,从`PCB`集合检索该进程的`PCB` - 若进程处于执行状态先停止执行 - 若进程还有子孙进程需要终止子孙进程 - 归还资源给父进程/系统 - 将`PCB`从队列中移出 # 9 阻塞/唤醒/挂起/激活 ## 9.1 阻塞与唤醒 ### 9.1.1 引起阻塞与唤醒的事件 - 向系统请求共享资源失败:请求失败会被阻塞,一直等到有资源时才被唤醒 - 等待某种操作完成:等待时阻塞,完成时唤醒 - 新数据尚未到达:到达前阻塞,到达后唤醒 - 等待新任务到达:到达前阻塞,到达后唤醒 ### 9.1.2 阻塞过程 阻塞是进程的一种自身主动的行为,过程如下: - 调用阻塞原语`block` - 停止进程运行 - 将`PCB`插入阻塞队列 - 调度,将处理机分配给另一进程 ### 9.1.3 唤醒过程 - 调用唤醒原语`wakeup` - 将进程移出阻塞队列 - 将`PCB`从阻塞改为就绪 - 再将`PCB`插入就绪队列 ## 9.2 挂起与激活 ### 9.2.1 挂起过程 - 调用原语`suspend` - 改为静止状态:也就是活动就绪变为静止就绪,活动阻塞变为静止阻塞 - 将`PCB`复制到某指定内存区域:方便查看进程状态 - 调度:若进程正在执行,进行调度 ### 9.2.2 激活过程 - 调用原语`active` - 将进程从外存调入内存 - 改为活动状态:若为静止就绪则改为活动就绪,若为静止阻塞则改为活动阻塞 - 优先级比较调度:若采用的是抢占式的调度策略,比较优先级并进行调度 # 10 进程同步 ## 10.1 制约关系 - 间接相互制约:比如共享设备的使用,两个进程不能同时使用等等 - 直接相互制约:一个进程等待另一个进程的计算结果等等 ## 10.2 同步机制应该遵循的原则 - **空闲让进**:无进程处于临界区时应该允许进入 - **忙则等待**:有进程处于临界区时应处于等待状态 - **有限等待**:不能陷入死等状态 - **让权等待**:进程不能进入自己的临界区时,应立即释放处理机 # 11 实现互斥 ## 11.1 硬件同步机制 包括: - 关中断 - `Test-and-Set`指令实现互斥 - `Swap`指令实现互斥 ## 11.2 信号量机制 包括: - 整型信号量 - 记录型信号量 - `AND`型信号量 - 信号量集 ### 11.2.1 整型信号量 - 信号量:用于表示资源数目或请求使用某一资源进程个数的数据结构,是一个被保护的变量,只能通过初始化、`P/V`操作去访问 - `P`操作:申请资源操作,实际上是原语`wait` - `V`操作:释放资源操作,实际上是原语`signal` 伪代码: ```c wait(S) { while(S <= 0); --S; } signal(S) { ++S; } ``` ### 11.2.2 记录型信号量 - 由于整型信号量未遵循让权等待的准则,进程会一直处于忙等状态 - 在整型信号量的基础上,加入了一个链表指针 伪代码: ```c wait(semaphore *S) { --S->value; if(S->value < 0) block(S->list); } signal(semaphore *S) { ++S->value; if(S->value <= 0) wakeup(S->list); } ``` ### 11.2.3 `AND`型信号量 将整个运行过程中需要的所有资源,一次性全部分配给进程,待进程使用完后再一起释放,只有尚有一个资源没有分配给进程,其他所有可能为之分配的资源也不分配。 也就是,对于一个进程需要的资源,要么全部分配,要么一个也不分配。 ### 11.2.4 信号量集 在`AND`型信号量的基础上,进行扩充,`AND`型每次只能对一个单位资源的申请或释放,而信号量集可以对`n`个单位资源一次性地进行申请或释放,而不是同一操作进行`n`次。 # 12 信号量集应用 ## 12.1 实现互斥操作 一般设置`mutex`为互斥信号量,初值为`-1`: - `mutex=1`表示两个进程都没有使用资源 - `mutex=0`表示已有一个进程使用资源但另一个进程没有使用 - `mutex=-1`表示已有进程使用资源另一进程因等待而阻塞 ## 12.2 实现前趋关系 可以利用信号量实现前趋关系。比如: ```bash p1() { s1; signal(a); signal(b); } p2() { wait(a); s2; } p3() { wait(b); s3; } ``` 在执行`p2`和`p3`之前必须先执行`p1`。 # 13 管程 ## 13.1 定义 一个管程定义了一个数据结果和能为并发进程所执行的一组操作,这组操作能同步进程和改变管程中的数据。 ## 13.2 特性 - 每次只有一个进程进入管程 - 管程由四部分组成:名称、局部于管程的共享数据结构说明、对该数据结构进行操作的一组过程、对局部于管程的共享数据设置初始值的语句 - 模块化:是一个基本程序单位 - 抽象数据类型:数据+对数据的操作 - 信息掩蔽:管程中的数据只能由管程中的过程访问 # 14 进程通信方式 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,一个进程把数据从用户空间拷到内核缓冲区,另一个进程再从内核缓冲区把数据读走,这种机制被称为进程间通信(`IPC`)。 常见的7种`IPC`方式: - 管道/匿名管道 - 有名管道 - 信号量 - 消息队列 - 信号 - 共享内存 - 套接字 ## 14.1 管道/匿名管道 特点: - 管道是半双工的,数据只能向一个方向流动,需要双工的时候只能建立两个半双工的管道 - 只能用于父子进程或者兄弟进程之间 - 一个进程向管道中写的内容被管道另一端的进程读出,写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据 管道的实质: - 是一个内核缓冲区,进程以`FIFO`方式从缓冲区存取数据 - 可以看做一个循环队列,读写位置都是自动增长的,不能随意改变 管道局限: - 只支持单向数据流 - 只能用于父子进程或兄弟进程 - 没有名字 - 缓冲区有限 - 传送的内容是无格式字节流,这意味着两个读写管道的进程必须事先约定好数据的格式 ## 14.2 有名管道 有名管道与匿名管道的区别在与提供了一个路径名与其关联,以有名管道的形式存在于文件系统中,这样就克服了匿名管道只能与父子进程或者兄弟进程通信的缺点。 值得注意的是,无论是有名管道还是匿名管道都会存在阻塞问题: - 匿名管道:匿名管道无需打开,创建时直接返回文件描述符,如果写入匿名管道的数据超过最大值,会阻塞写操作,如果管道中没有数据,会阻塞读操作 - 有名管道:有名管道打开的时候,需要确保另一个进程存在,不存在将会阻塞 ## 14.3 信号 特点: - 可以在任何时候发送给某一进程,无需知道进程的状态 - 如果进程未处于执行状态,那么内核会将该进程保存起来直到进程恢复执行并传递给它为止 - 如果进程阻塞,则信号的传递会被延迟,直到其取消阻塞才被传递给进程 信号的生命周期如下: - 信号被某个进程产生,并设置此信号传递的对象,然后传递给操作系统 - 操作系统根据进程是否阻塞,如果阻塞则先保存信号,直到不阻塞为止,如果没有阻塞则直接传递信号 - 目标进程收到信号后,根据信号进行处理,比如终止当前代码的执行等等 ## 14.4 消息队列 消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示。与管道(匿名管道存在于内存中,有名管道存在于实际文件中)不同的是,消息队列存放在内核中,只有内核重启或显示本地删除一个消息队列的时候,该消息队列才会被真正删除。 特点: - 允许一个或多个进程向它写入与读取消息 - 可以实现消息的随机查询,消息不一定要以先进先出的方式读取,也可以按照消息的类型读取 - 消息队列克服了信号承载信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点 目前主要有两种类型的消息队列:`POSIX`消息队列以及`System V`消息队列。 ## 14.5 共享内存 共享内存使得多个进程可以直接读写同一块内存空间,是最快的`IPC`方式,针对其他通信机制运行效率低效而设计的。 同时,由于多个进程共享一段内存,因此需要依靠某种同步机制(比如信号量)来达到进程间的同步和互斥。 ## 14.6 信号量 信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步,为了获得共享资源,需要执行以下操作: - 创建一个信号量:需要调用者指定初始值,通常是共享资源的数量 - 等待一个信号量:会测试这个信号量的值,如果小于0就阻塞,否则将其减1,也叫`P`操作 - 挂出一个信号量:将信号量的值加1,也叫`V`操作 为了保证信号量操作的原子性,通常在内核中实现,`Linux`环境中有三种类型: - `Posix`有名信号量 - `Posix`基于内存的信号量 - `System V`信号量 ## 14.7 套接字 套接字是一种通信机制,通过套接字,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行。 套接字的特性由三个属性确定: - 域 - 端口号 - 协议类型 ### 14.7.1 域 域是套接字通信中使用的网络介质,最常见的域有两种: - `AF_INET`:指网络,一般用于跨网络通信 - `AF_UNIX`:表示`UNIX`文件系统,就是文件的输入输出 ### 14.7.2 端口号 每一个基于`TCP/IP`网络通信的程序都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留`Socket`中的输入/输出信息。 ### 14.7.3 协议类型 三种: - 流套接字:在域中通过`TCP/IP`连接实现,同时也是`AF_UNIX`常用的套接字类型,流套接字提供一个有序的、可靠的、双向字节流的连接,因此发送的数据可以确保不丢失、重复或乱序到达,而且出错重传机制 - 数据报套接字:不需要建立连接和维持一个连接,在域中通常是通过`UDP/IP`实现,发送数据的长度有限制,数据包可能会出现丢失、复制或错乱到达 - 原始套接字:允许对较低层次的协议直接访问,比如`IP`、`ICMP`协议,换句话说,如果需要发送不是基于`TCP/UDP`协议的数据,必须使用原始套接字 ### 14.7.4 套接字建立过程 简单来说,套接字需要服务器先监听一个端口,并循环等待连接,接着客户端进行连接,具体来说,对于服务端: - 首先通过系统调用`socket`创建一个套接字 - 接着通过系统调用`bind`给套接字起名字 - 再接着系统调用`listen`设置一个监听队列 - 最后通过系统调用`accept`接受客户端的连接 而客户端的过程如下: - 通过`socket`创建一个未命名的套接字 - 然后将服务器的命名套接字作为一个地址来调用`connect`来与服务器建立连接 - 一旦连接建立就可以实现双向的数据流通信 # 15 线程 ## 15.1 线程引入 在引入线程的`OS`中,**线程作为调度和分派的基本单位**(而不是进程作为调度和分派的基本单位)。 ## 15.2 线程的三个状态 - 执行状态:获得处理机执行 - 就绪状态:线程已具备各种执行条件,获得`CPU`即可执行 - 阻塞状态:线程在执行中因某事件受阻而处于暂停状态 ## 15.3 `TCB` 类似`PCB`,系统为每个线程也分配了一个`TCB`,包含的信息有: - 线程标识符 - 一组寄存器 - 线程运行状态 - 优先级 - 线程专有存储区 - 信号屏蔽 - 堆栈指针 ## 15.4 线程实现方式 ### 15.4.1 内核支持线程 `Kernel Supported Threads`,`KTS`,内核支持线程,在内核的支持下运行,创建、阻塞、撤销和切换都是在内核空间实现的,优点如下: - 多处理器系统中,能够调度同一进程中的多个线程并发执行 - 进程中某一线程如果被阻塞,内核可以调度该进程的其他线程运行,也可以运行其他进程中的线程 - `KTS`具有很小的数据结构和堆栈,线程切换快,开销小 - 内核本身可以采用多线程技术,提高系统的执行速度和效率 缺点: - 切换开销大,对用户切换线程而言,需要从用户态切换到内核态,系统开销较大 ### 15.4.2 用户级线程 `User Level Threads`,`ULT`,用户级线程,在用户空间内实现,对线程创建、撤销、同步与通信无需内核的支持,与内核无关。 优点: - 线程切换不需要转换到内核空间 - 调度算法可以是进程专用的 - 实现与`OS`无关 缺点: - 系统调用阻塞:大多数的系统调用会将线程阻塞 - 不能最大化利用线程:单纯的用户级线程实现中,多线程应用不能利用多处理机进行多重处理的优点,也就是进程中只能有一个线程运行 ### 15.4.3 组合方式 基于上面的两种方式,提出了一种组合的方式,形成了三种不同的模型: - 多对一模型:将用户线程映射到一个内核线程 - 一对一模型:每一个用户级线程映射到一个内核线程 - 多对多模型:将许多用户线程映射到同样数量或数量更少的内核线程上 ## 15.5 线程实现 ### 15.5.1 `KTS`实现 - 创建进程时分配一个任务数据区(`Per Task Data Area`),其中包括若干`TCB`空间 - 当进程创建一个线程时,便分配一个`TCB`,并分配必须的资源 - 继续创建线程时继续分配`TCB` ### 15.5.2 `ULS`实现 `ULS`在用户空间实现,所有用户级线程都具有相同的结构,运行在中间系统上,实现中间系统主要有以下两种方式: #### 15.5.2.1 运行时系统 运行时系统实质是用于管理和控制线程的函数的集合,线程切换通过调用运行时系统的函数来完成,当线程需要资源时,将请求传递给运行时系统,由运行时系统通过相应的系统调用获取资源。 #### 15.5.2.2 内核控制线程 又叫轻型进程`LWP`(`Light Weight Process`),`LWP`可通过系统调用获得内核提供的服务,用户级线程运行时,只需将它连接到一个`LWP`上。 通常把`LWP`做成一个缓冲池,叫“线程池”,用户级线程连接到其中一个`LWP`上,而该`LWP`又与内核级线程连接,这样就可以实现用户级线程与内核无关。 ## 15.6 线程创建与终止 线程创建: - 创建新线程时,利用线程创建函数,并提供相应参数 - 创建完成后返回一个线程标识符 线程终止: - 通过调用相应函数或系统调用进行终止 ## 15.7 线程通信方式 线程之间通信主要是用于同步而不是数据交换,包括: - 锁机制:包括互斥锁、读写锁、条件变量以及自旋锁 - 信号量机制:包括匿名线程信号量和有名线程信号量 - 信号机制:类似进程中的信号机制 - 使用全局变量 # 16 进程与线程比较 线程又叫轻型进程或进程元,传统进程叫重型进程,下面从几个方面对两者进行比较: - 调度的基本单位:不引入线程的`OS`中,进程是调度和分派的基本单位,而引入后,线程是调度和分派的基本单位 - 并发性:引入线程后,不同进程之间可以并发执行,同一进程的多个线程也可以并发执行,不同进程的不同线程也可以并发执行 - 资源:进程可以拥有资源,是系统中拥有资源的一个基本单位,而线程本身并不拥有系统资源,只是仅有一点必不可少的线程运行所必须的资源,多个线程能够共享进程所拥有的资源 - 独立性:不同进程的独立性比同一进程的不同线程的独立性要高 - 系统开销:创建/撤销进程所需要的开销远大于创建/撤销线程所需要的开销 - 支持多处理机:传统的单线程进程只能运行在一个处理机上,而多线程进程可以将多个线程分配到多个处理机上 # 17 线程与协程比较 协程是一种轻量级线程,又叫微线程,协程的调度完全由用户控制。特点包括: - 执行效率高 - 不需要多线程的锁机制 与线程的主要区别在于: - 线程是同步机制,而协程是异步机制 - 协程能保留上一次调用的状态,每次重入时,将相当于进入上一次调用的状态