五、深入理解进程概念
5.1 进程的分类
1、系统进程/用户进程
这里系统进程的优先级要高2、前台进程/后台进程
用户一般只和前台进程交互。3、
CPU
密集型进程/IO
密集型进程
5.2 进程层次结构
-
UNIX
中是一个进程家族树的概念:init
为根进程。于是如果某一个进程结束了,那么其子孙进程都必须结束。 -
Windows
中的所有进程的地位都是相同的。
5.3 进程和程序的区别
- 进程更能准确刻画并发,而程序不能
- 程序是静态的,进程是动态的
- 进程有生命周期的,有诞生有消亡,是短暂的;而程序是相对长久的
- 一个程序可对应多个进程
- 进程具有创建其他进程的功能
5.4 进程的地址空间
操作系统为每个进程分配了一个地址空间。这里我们看一个例子:
这个程序我们从命令行中输入数据,比如:
myval 7
myval 8
此时我们会发现虽然进程不同,但是打印出来的地址确实一样的。这里我们从进程地址空间来分析:
说明:上面的两个进程都有这样一个地址空间,也就是说这两个进程是在不同的地址空间上的相同的位置,所以虽然地址是一样的,但是实际上在实际内存中的地址是不一样的。
5.5 进程映像
对进程执行活动全过程的静态描述(快照)。由进程地址空间内容、硬件寄存器内容及与该进程相关的内核数据结构、内核栈组成
- 与用户相关:进程地址空间(代码段、数据段、堆和栈、共享栈等)
- 与寄存器相关:程序计数器、指令寄存器、程序状态寄存器、栈指针、通用寄存器等的值
- 与内核相关:
- 静态部分:
PCB
及各种资源数据结构 - 动态部分:内核栈(不同进程在进入内核后使用不同的内核栈)
- 静态部分:
5.6 上下文切换
- 将
CPU
硬件状态从一个进程换到另一个进程的过程称为上下文切换,其实就是运行环境的切换。 - 进程运行时,其硬件状态保存在
CPU
上的寄存器中。寄存器有:程序计数器、程序状态寄存器、栈指针、通用寄存器、其他控制寄存器的值 - 进程不运行时,这些寄存器的值保存在进程控制块中;当操作系统要运行一个新的进程时,将进程控制块中相关值送到对应的寄存器中。
六、线程
6.1 线程的引入
引入线程有三个理由:
- 应用的需要
- 开销的考虑
- 性能的考虑
6.1.1 应用的需要
我们看一个例子,一个web
服务器的工作方式:
- 从客户端接收网页请求
- 从磁盘上检索相关的网页,读入内存(此时进程是停止的,直到读取完毕)
- 将网页返回给对应的客户端
可以看到每次从磁盘读取的时候进程都是暂停的,这样会导致性能低下。那如何提高服务器的工作效率?通常情况下是使用网页缓存的方式解决。如果没有线程的情况下的两种解决方案:
一个服务进程
这种情况下也是一种顺序编程,虽然采用了缓存机制,但是性能同样不高。而如果设置多个进程,这多个进程之间又是相互独立的,有独立的地址空间,所以不能共享信息。有限状态机
这种方式编程模型复杂,采用非阻塞的I/O
。
多线程的解决方式
说明:这是一个多线程的
web
服务器的工作方式,首先读取客户端的请求,之后由分派线程将各个任务分派给工作线程,同样这里还是采用了网页缓存。于是我们可以看到一个web
服务器的实现有三种方式:6.1.2 开销的考虑
6.1.3 性能的考虑
如果有多个处理器的话,一个进程就会有多个线程同时在执行了,这样可以极大的提高运行 性能。
6.2 线程的基本概念
线程的属性
- 有标识符
ID
- 有状态及状态转换
-->
需要提供一些操作 - 不运行时需要保存的上下文(程序计数器等寄存器)
- 有自己的栈和栈指针
- 共享所在进程的地址空间和其他资源
- 创建、撤销另一个线程(程序开始是以一个单线程方式运行的)
6.3 线程机制的实现
一般有三种实现机制:
- 用户级线程
- 核心级线程
- 混合(两者结合)方法
6.3.1 用户级线程
说明:可以看到线程是有运行时系统管理的,在内核中只有进程表。典型例子就是
UNIX:
POSIX线程库--PTHREAD
小结:
有点:
- 线程切换快
- 调度算法是应用程序特定的
- 用户级线程可运行在任何操作系统上(只需要实现线程库)
缺点:
- 内核只将处理器分配给进程,同一进程中的两个线程不能同时运行于两个处理器上
- 大多数系统调用是阻塞的,因此,由于内核阻塞进程,故进程中所有线程也被阻塞。(可以在调用之前判断进行解决,如果是阻塞线程,那么就换其他线程)
6.3.2 核心级线程
6.3.3 混合模型
- 线程创建在用户空间完成
- 线程调度等在核心态完成
- 例子如
Solaris
操作系统