操作系统 - 进程与线程、调度、进程间通信
本文为《现代操作系统:原理与实现》(陈海波著)的阅读笔记。
# 1. 进程
# 1.1 fock:进程创建
在linux中,fock会复制一个进程的全部信息,成为一个全新的进程,并返回其PID(两次)。这个过程,这事linux创建进程的唯一方式。其中写时拷贝会优化fock的过程,使其效率更高。
第一个进程怎么创建:
# 1.2 execve:进程的执行
# 1.3 进程树、进程间监控与僵尸进程
linux进程间监控用的是waitpid,由父进程调用waitpid对子进程进行监控。wait不仅有监控的作用,还有释放资源的作用。
如果父进程没有调用waitpid,子进程就结束了,此时资源没有完全释放,这个进程就变成了僵尸进程。
# 1.4 概念:进程组(process)与会话(session)
概念关系:
进程组是进程的集合
会话是进程组的集合
进程组存在的意义:主要是体现在信号的处理上,信号是由软件发出的类似中断的操作。当对一个进程组发出信号时,信号会发送给进程组的每个进程。
会话可以根据进程组的执行状态将其分为“前台进程组”和“后台进程组”。
控制终端进程是会话与外界进行交互的“窗口“,负责接收用户发来的输入。
在一个进程中会有三个标识:
pid:进程号
gid:进程组号
sid:会话号
# 2. 线程
线程出现的原因:
创建进程的开销大,需要创建独立的地址空间。(线程创建速度比进程快10-100倍)
进程间进行数据共享麻烦而且开销大
另外需要注意:线程是操作系统资源调度的基本单位。通常使用超线程技术的CPU可以一个核心同时处理两个线程,因此出现了4核8线程等CPU。
# 2.1 多线程的地址空间
多线程在一个进程内,他们的地址空间有两个重要的特征:
分离的内核栈和用户栈。
共享的其他区域(代码库,用户堆,数据,代码等)
# 2.2 用户态线程与内核态线程
根据由用户态应用还是内核创建,将线程分为用户态线程和内核态线程。
区别:
用户态线程是自己创建的,内核不可见,不接受操作系统调度器直接管理。
用户态线程比内核态线程更加轻量级,创建开销小。
用户态线程的功能受限。
但是有时候需要用户态线程与内核态线程相互协作,所以操作系统会建立两者之间的关系,有三种:
多对一模型
一对一模型(Linux和Windows所采用)
多对多模型(macOS和iOS的调度器GCD采用)
# 2.3 线程控制块与线程本地存储
线程控制块(Thread Control Block, TCB)主要用于保存线程自身的相关信息。
线程本地存储(Thread Local Storage, TLS)可以实现线程内的全局变量。
# 2.4 线程的基本接口:POSIX线程库
这部分参看书籍P95 5.3.4
# 2.5 线程的实现
这部分参照:Leetcode 硬核操作系统指南 线程实现 (opens new window)
主要分为两种,在用户空间实现和在内核中实现
# 3. 线程的调度
# 3.1 调度的目标
由于应用种类繁多,调度的目标也各不相同。根据调度任务的不同,总结如下:
# 3.2 调度任务总览
此部分见P116 图6-4,这里有图的过程以及各部分的解释。
# 3.3 调度方法
LeetBook中的分类是按照不同应用来分的,大体如下:
批处理任务调度:
FCFS,先到先服务
最短作业优先(最短任务优先, Shortest Job First, SJF)
最短剩余时间优先(2的抢占式版本,也叫最短完成时间任务优先, Shortest Time-to-Complete First, STCF)
交互式系统的调度:
- 轮询调度(时间片轮转, Round Robin, RR)
- 优先级调度
- 多级队列(MLQ)
- 多级反馈队列(MLFQ,早期的Linux,Windows,macOS)
- 公平共享调度策略
- 彩票调度
- 步幅调度
- 最短进程优先
- 保证调度
实时系统中的调度:
这里只介绍了一下,没有具体方案。
在书中还介绍了多核调度策略,主要内容有:
- 负载分担
- 协同调度
- 两级调度
- 负载追踪与负载均衡
- 能耗感知调度