From 3c3a48318fc73a64048718259f163e4fcb780877 Mon Sep 17 00:00:00 2001 From: 2293736867 <2293736867@qq.com> Date: Fri, 9 Apr 2021 21:20:28 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E4=B8=8E=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E9=80=9A=E4=BF=A1=E6=96=B9=E5=BC=8F+=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=B8=8E=E5=8D=8F=E7=A8=8B=E7=9A=84=E6=AF=94=E8=BE=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OperatingSystem/Chapter2/README.md | 290 +++++++++++++++++------------ SoftwareTesting/Chapter3/png.odg | Bin 0 -> 14892 bytes 2 files changed, 166 insertions(+), 124 deletions(-) create mode 100644 SoftwareTesting/Chapter3/png.odg diff --git a/OperatingSystem/Chapter2/README.md b/OperatingSystem/Chapter2/README.md index c9f0ab3..0ba2850 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 GIT binary patch literal 14892 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 literal 0 HcmV?d00001 -- GitLab