提交 3c3a4831 编写于 作者: 2 2293736867

线程与进程的通信方式+线程与协程的比较

上级 d627ef91
# 目录
* [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 线程与协程比较
协程是一种轻量级线程,又叫微线程,协程的调度完全由用户控制。特点包括:
- 执行效率高
- 不需要多线程的锁机制
与线程的主要区别在于:
- 线程是同步机制,而协程是异步机制
- 协程能保留上一次调用的状态,每次重入时,将相当于进入上一次调用的状态
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册