声明: 1. 本文为我的个人复习总结, 并非那种从零基础开始普及知识 内容详细全面, 言辞官方的文章
2. 由于是个人总结, 所以用最精简的话语来写文章
3. 若有错误不当之处, 请指出
介绍
程序是静态的, 进程是动态的; 运行的程序叫做进程
挂起:
比如read( )引发系统调用使进程阻塞, 此时进程依旧会占用内存空间, 于是操作系统会把阻塞的进程置换到磁盘
中
- 阻塞挂起: 进程在外存(硬盘) 并等待某个事件的出现
- 就绪挂起: 进程在外存(硬盘), 并没有在等待某个事件的发生, 只要被换入到内存,就可以立即运行
导致进程挂起的原因:
- 进程受到阻塞
- 用户希望挂起⼀个程序,比如在 Linux 中用 Ctrl+Z 挂起进程
PCB
PCB 是进程存在的唯一
标识,线程的话是TCB
,PCB里记录的信息包括:
-
进程描述信息
- 进程标识符:标识各个进程
- 用户标识符:进程所属的用户
-
进程控制和管理信息
- 进程当前状态
- 进程优先级
-
资源分配清单
- 内存地址空间或虚拟地址空间的信息
- 所打开文件的列表和所使用的 I/O 设备信息
-
CPU 相关信息
CPU 中各个寄存器的值,当进程被切换时CPU 的状态信息都会被保存在相应的 PCB 中,以便进程重新执行时能从断点处继续执行
PCB通过链表
进行组织,相同状态
的进程连在一起 组成各种队列; 也可以通过索引表进行组织连接
原语:即原子操作,使用中断指令
进程修改状态:
经过两步, 这两步得是原子操作不可被打断:
-
先把这个 PCB
移动到修改后状态所对应的链表
后面, -
再修改 PCB 的进程状态值
进程的控制
-
创建进程
-
为新进程分配PCB
-
为进程分配资源
如果资源不足,进程就会进入等待状态以等待资源
-
初始化 PCB
-
如果进程的调度队列能够接纳新进程,那就将进程插入到就绪队列 等待被调度运行
-
-
终止进程
进程可以有 3 种终止方式:正常结束、异常结束(抛出异常)、外界干预(信号 kill 掉)
- 找到进程的 PCB
- 如果处于执行状态,则立即终止此进程以及子进程,然后将 CPU 资源分配给其他进程
- 将其从 PCB 所在队列中删除
-
阻塞进程
- 找到进程的 PCB
- 如果该进程为运行状态,则保护其现场(保存上下文信息, 比如PC寄存器记录此时程序执行位置), 然后将其状态转为阻塞状态
- 将该 PCB 插入到阻塞队列中去
-
唤醒进程
进程⼀旦被阻塞,则它只能由另⼀个进程去唤醒
- 找到进程的 PCB
- 将其从阻塞队列中移出,并置其状态为就绪状态
- 把该 PCB 插入到就绪队列中,等待调度程序调度
总结: 找PCB + 将进程移动至所属队列
进程切换:
发生在何处?
进程是由内核管理和调度的,所以进程的切换只能发生在内核态
切换时发生了什么?
-
切换
虚拟内存
、栈
、全局变量
等用户空间的资源, -
切换
内核堆栈
、寄存器
等内核空间的资源
进程 与 线程 的区别
进程是
操作系统
分配资源与调度的基本单位, 线程是cpu
进行调度的基本单位进程有
独立的虚拟地址空间
, 而线程没有独立的虚拟地址空间进程之间的
内存都是独立
的, 而线程之间既有独立的栈空间, 又有共享的当前进程的内存空间一个进程内可以有
多个
线程进程
切换开销
大, 而线程切换开销小因为 进程切换时 需要
切换虚拟地址空间
, 而同进程的线程切换时 并不会切换虚拟地址空间虚拟地址空间一切换,
页表缓存
会失效, 从而虚拟地址转换时会变慢
线程的实现
-
用户线程
对应关系:
多对多, 多个用户线程对应一个内核线程
实现:
在用户空间实现的线程,由
用户级的线程库
来完成线程的管理, 操作系统不直接参与优点:
无需用户态与内核态的切换, 所以开销小速度快
缺点:
没法打断当前运行中的线程, 故没法实现多线程抢占式调用, 从而并发量低
-
内核线程
对应关系:
一对一, 一个用户线程对应一个内核线程
实现:
在内核中实现的线程,是由
操作系统内核
管理的线程优点:
可以打断当前运行中的线程, 故可以实现多线程抢占式调用, 从而并发量低
缺点:
经常需要用户态与内核态的切换, 所以开销大速度慢
-
轻量级进程
对应关系:
有 多对一,一对一,多对多
实现:
在内核中来支持用户线程
缺点:
经常发生系统调用 切换内核态 与 用户态, 故开销大效率低
通信:
每个进程的用户地址空间都是独立的 不能互相访问,但内核空间是每个进程都共享的; 所以进程之间要通信必须通过
内核
-
管道
管道传输数据是单向的, 数据先进先出
实现:
管道是内核里的一块缓存, 是特殊的文件,只存在于内存,不存于文件系统中
⼀个是管道的读取端描述符 fd[0] ,另⼀个是管道的写入端描述符 fd[1]
命名管道:
适用于任意两个进程之间的通信
匿名管道:
只适用于父子进程之间的通信
优点:
简单
缺点:
管道内的数据必须被读走才能进行继续写入, 通信方式效率低,不适合进程间频繁地交换数据
-
消息队列
管道的升级版, 适合进程间频繁地交换数据, 在消息队列塞满之前 可以不必管数据是否被读取 就可以持续写入数据
不足之处:
- 通信不及时
- 附件有大小限制
管道
的生命周期随进程消息队列
的生命周期随内核 -
信号
发送信号, 如 kill 命令 发送的 SIGKILL 信号
-
信号量
是⼀个整型的计数器,主要用于实现进程间的
互斥
与同步
实现过程:
有两种原子操作:
- P 操作(lock),这个操作会把信号量减去 1
- 相减后如果信号量 < 0 则表明该资源已被
占用
,进程需阻塞等待 - 如果信号量 >= 0,则表明该资源
空闲
,进程可正常继续执行。
- 相减后如果信号量 < 0 则表明该资源已被
- V 操作(unlock),这个操作会把信号量加上 1
- 相加后如果信号量 <= 0,则表明当前
有阻塞
中的进程,于是会将该进程唤醒运行 - 相加后如果信号量 > 0,则表明当前
没有阻塞
中的进程
- 相加后如果信号量 <= 0,则表明当前
- P 操作(lock),这个操作会把信号量减去 1
-
共享内存
避免了
用户态
与内核态
之间的消息拷贝过程缺点:
临界区发生竞态条件 写操作时, 有并发安全问题
-
Socket
跨机器的进程通信
通信方式:
- TCP字节流
- UDP数据报
- 本地进程(实现: 绑定一个本地文件)
线程:
线程间的通信可以通过全局变量来实现, 线程不关注通信方式, 而关注多线程间竞争共享资源的问题
为了解决多线程间竞争共享资源的问题, 引发了
同步
和互斥
互斥
保证任意时刻只有⼀个线程访问共享资源, 是多线程之间的竞争
同步
保证线程 A 应在线程 B 之前执行, 是多线程间的协调
死锁
-