diff --git a/OperatingSystem/Chapter2/README.md b/OperatingSystem/Chapter2/README.md index c9f0ab329731d50d2f8dd00c67bf234fbae18bb8..0ba2850c9e8575d7e10bfd951841b4ad52bbbf91 100755 --- a/OperatingSystem/Chapter2/README.md +++ b/OperatingSystem/Chapter2/README.md @@ -1,85 +1,5 @@ -# 目录 - -* [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-实现方式) -* [15 线程](#15-线程) - * [15.1 进程的基本属性](#151-进程的基本属性) - * [15.2 线程引入](#152-线程引入) - * [15.3 进程与线程比较](#153-进程与线程比较) - * [15.4 线程的三个状态](#154-线程的三个状态) - * [15.5 `TCB`](#155-tcb) - * [15.6 线程实现方式](#156-线程实现方式) - * [15.6.1 内核支持线程](#1561-内核支持线程) - * [15.6.2 用户级线程](#1562-用户级线程) - * [15.6.3 组合方式](#1563-组合方式) - * [15.7 线程实现](#157-线程实现) - * [15.7.1 `KTS`实现](#1571-kts实现) - * [15.7.2 `ULS`实现](#1572-uls实现) - * [15.7.2.1 运行时系统](#15721-运行时系统) - * [15.7.2.2 内核控制线程](#15722-内核控制线程) - * [15.8 线程创建与终止](#158-线程创建与终止) - * [15.8.1 线程创建](#1581-线程创建) - * [15.8.2 线程终止](#1582-线程终止) - - - -# 1 前趋图 + +# 1 前趋图 一个有向无循环图,用来描述程序段或进程之间执行的先后次序关系。每个结点表示一个程序段或一个进程,结点间的有向边用来表示两个节点之间存在的偏序或前趋关系。 基本术语: @@ -442,54 +362,142 @@ p3() - 信息掩蔽:管程中的数据只能由管程中的过程访问 -# 14 进程通信 -## 14.1 定义 -进程通信是指进程之间的信息交换。 +# 14 进程通信方式 -## 14.2 分类 -高级进程通信工具可以简单分为四类: +每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,一个进程把数据从用户空间拷到内核缓冲区,另一个进程再从内核缓冲区把数据读走,这种机制被称为进程间通信(`IPC`)。 -- 共享存储器系统:相互通信的进程共享某些数据结构或共享存储区,可以分为基于共享结构的通信方式以及基于共享存储区的通信方式 -- 管道通信系统:利用管道进行通信 -- 消息传递系统:将通信的数据封装在消息中,利用操作系统提供的一组通信命令在进程间进行消息传递,完成进程间的数据交换,可以分为直接通信方式和间接通信方式 -- 客户机-服务器系统:分为套接字、远程过程调用、远程方法调用三类 +常见的7种`IPC`方式: +- 管道/匿名管道 +- 有名管道 +- 信号量 +- 消息队列 +- 信号 +- 共享内存 +- 套接字 -## 14.3 实现方式 -主要分为两种: +## 14.1 管道/匿名管道 +特点: -- 直接消息传递系统 -- 信箱通信 +- 管道是半双工的,数据只能向一个方向流动,需要双工的时候只能建立两个半双工的管道 +- 只能用于父子进程或者兄弟进程之间 +- 一个进程向管道中写的内容被管道另一端的进程读出,写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据 +管道的实质: -# 15 线程 -## 15.1 进程的基本属性 -两个: +- 是一个内核缓冲区,进程以`FIFO`方式从缓冲区存取数据 +- 可以看做一个循环队列,读写位置都是自动增长的,不能随意改变 -- **进程是一个可拥有资源的独立单位** -- **进程是一个可独立调度和分派的基本单位** +管道局限: -## 15.2 线程引入 -在引入线程的`OS`中,**线程作为调度和分派的基本单位**(而不是进程作为调度和分派的基本单位)。 +- 只支持单向数据流 +- 只能用于父子进程或兄弟进程 +- 没有名字 +- 缓冲区有限 +- 传送的内容是无格式字节流,这意味着两个读写管道的进程必须事先约定好数据的格式 -## 15.3 进程与线程比较 -线程又叫轻型进程或进程元,传统进程叫重型进程,下面从几个方面对两者进行比较: +## 14.2 有名管道 +有名管道与匿名管道的区别在与提供了一个路径名与其关联,以有名管道的形式存在于文件系统中,这样就克服了匿名管道只能与父子进程或者兄弟进程通信的缺点。 -- 调度的基本单位:不引入线程的`OS`中,进程是调度和分派的基本单位,而引入后,线程是调度和分派的基本单位 -- 并发性:引入线程后,不同进程之间可以并发执行,同一进程的多个线程也可以并发执行,不同进程的不同线程也可以并发执行 -- 资源:进程可以拥有资源,是系统中拥有资源的一个基本单位,而线程本身并不拥有系统资源,只是仅有一点必不可少的线程运行所必须的资源,多个线程能够共享进程所拥有的资源 -- 独立性:不同进程的独立性比同一进程的不同线程的独立性要高 -- 系统开销:创建/撤销进程所需要的开销远大于创建/撤销线程所需要的开销 -- 支持多处理机:传统的单线程进程只能运行在一个处理机上,而多线程进程可以将多个线程分配到多个处理机上 +值得注意的是,无论是有名管道还是匿名管道都会存在阻塞问题: + +- 匿名管道:匿名管道无需打开,创建时直接返回文件描述符,如果写入匿名管道的数据超过最大值,会阻塞写操作,如果管道中没有数据,会阻塞读操作 +- 有名管道:有名管道打开的时候,需要确保另一个进程存在,不存在将会阻塞 + +## 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.4 线程的三个状态 +## 15.2 线程的三个状态 - 执行状态:获得处理机执行 - 就绪状态:线程已具备各种执行条件,获得`CPU`即可执行 - 阻塞状态:线程在执行中因某事件受阻而处于暂停状态 -## 15.5 `TCB` +## 15.3 `TCB` 类似`PCB`,系统为每个线程也分配了一个`TCB`,包含的信息有: - 线程标识符 @@ -500,8 +508,8 @@ p3() - 信号屏蔽 - 堆栈指针 -## 15.6 线程实现方式 -### 15.6.1 内核支持线程 +## 15.4 线程实现方式 +### 15.4.1 内核支持线程 `Kernel Supported Threads`,`KTS`,内核支持线程,在内核的支持下运行,创建、阻塞、撤销和切换都是在内核空间实现的,优点如下: - 多处理器系统中,能够调度同一进程中的多个线程并发执行 @@ -513,7 +521,7 @@ p3() - 切换开销大,对用户切换线程而言,需要从用户态切换到内核态,系统开销较大 -### 15.6.2 用户级线程 +### 15.4.2 用户级线程 `User Level Threads`,`ULT`,用户级线程,在用户空间内实现,对线程创建、撤销、同步与通信无需内核的支持,与内核无关。 优点: @@ -527,7 +535,7 @@ p3() - 系统调用阻塞:大多数的系统调用会将线程阻塞 - 不能最大化利用线程:单纯的用户级线程实现中,多线程应用不能利用多处理机进行多重处理的优点,也就是进程中只能有一个线程运行 -### 15.6.3 组合方式 +### 15.4.3 组合方式 基于上面的两种方式,提出了一种组合的方式,形成了三种不同的模型: - 多对一模型:将用户线程映射到一个内核线程 @@ -536,27 +544,61 @@ p3() -## 15.7 线程实现 -### 15.7.1 `KTS`实现 +## 15.5 线程实现 +### 15.5.1 `KTS`实现 - 创建进程时分配一个任务数据区(`Per Task Data Area`),其中包括若干`TCB`空间 - 当进程创建一个线程时,便分配一个`TCB`,并分配必须的资源 - 继续创建线程时继续分配`TCB` -### 15.7.2 `ULS`实现 +### 15.5.2 `ULS`实现 `ULS`在用户空间实现,所有用户级线程都具有相同的结构,运行在中间系统上,实现中间系统主要有以下两种方式: -#### 15.7.2.1 运行时系统 +#### 15.5.2.1 运行时系统 运行时系统实质是用于管理和控制线程的函数的集合,线程切换通过调用运行时系统的函数来完成,当线程需要资源时,将请求传递给运行时系统,由运行时系统通过相应的系统调用获取资源。 -#### 15.7.2.2 内核控制线程 +#### 15.5.2.2 内核控制线程 又叫轻型进程`LWP`(`Light Weight Process`),`LWP`可通过系统调用获得内核提供的服务,用户级线程运行时,只需将它连接到一个`LWP`上。 通常把`LWP`做成一个缓冲池,叫“线程池”,用户级线程连接到其中一个`LWP`上,而该`LWP`又与内核级线程连接,这样就可以实现用户级线程与内核无关。 -## 15.8 线程创建与终止 -### 15.8.1 线程创建 +## 15.6 线程创建与终止 + +线程创建: + - 创建新线程时,利用线程创建函数,并提供相应参数 - 创建完成后返回一个线程标识符 -### 15.8.2 线程终止 +线程终止: + - 通过调用相应函数或系统调用进行终止 + +## 15.7 线程通信方式 +线程之间通信主要是用于同步而不是数据交换,包括: + +- 锁机制:包括互斥锁、读写锁、条件变量以及自旋锁 +- 信号量机制:包括匿名线程信号量和有名线程信号量 +- 信号机制:类似进程中的信号机制 +- 使用全局变量 + + +# 16 进程与线程比较 +线程又叫轻型进程或进程元,传统进程叫重型进程,下面从几个方面对两者进行比较: + +- 调度的基本单位:不引入线程的`OS`中,进程是调度和分派的基本单位,而引入后,线程是调度和分派的基本单位 +- 并发性:引入线程后,不同进程之间可以并发执行,同一进程的多个线程也可以并发执行,不同进程的不同线程也可以并发执行 +- 资源:进程可以拥有资源,是系统中拥有资源的一个基本单位,而线程本身并不拥有系统资源,只是仅有一点必不可少的线程运行所必须的资源,多个线程能够共享进程所拥有的资源 +- 独立性:不同进程的独立性比同一进程的不同线程的独立性要高 +- 系统开销:创建/撤销进程所需要的开销远大于创建/撤销线程所需要的开销 +- 支持多处理机:传统的单线程进程只能运行在一个处理机上,而多线程进程可以将多个线程分配到多个处理机上 + +# 17 线程与协程比较 +协程是一种轻量级线程,又叫微线程,协程的调度完全由用户控制。特点包括: + +- 执行效率高 +- 不需要多线程的锁机制 + +与线程的主要区别在于: + +- 线程是同步机制,而协程是异步机制 +- 协程能保留上一次调用的状态,每次重入时,将相当于进入上一次调用的状态 + diff --git a/SoftwareTesting/Chapter3/png.odg b/SoftwareTesting/Chapter3/png.odg new file mode 100644 index 0000000000000000000000000000000000000000..cf93260b93cb66f52c6b9046a14f4be9534a6188 Binary files /dev/null and b/SoftwareTesting/Chapter3/png.odg differ