操作系统学习笔记2(进程与线程)
操作系统学习笔记2(进程与线程)
1.进程(Processes)
1.进程模型
- 计算机上所有可运行的软件,被组织成若干顺序进程,简称进程(process)
- 一个进程就是一个正在执行程序的实例
2.进程的创建
-
四种主要事件会导致进程的创建
1.系统初始化
2.正在运行的程序执行了创建进程的系统调用
3.用户请求创建一个新进程
4.一个批处理作业的初始化
-
启动操作系统时会创建前台进程(foreground)和守护(daemon)进程,守护进程就是在后台执行的进程
3.进程的终止
-
进程的终止通常由以下条件引起
1.正常退出(自愿的)
2.出错退出(自愿的)
3.严重错误(非自愿)
4.被其它进程杀死(非自愿)
4.进程的层次结构
- 进程和它的所有子进程已经后裔共同构成一个进程组
5.进程的状态
-
进程共有三种状态
1.运行态(Running)(该时刻进程实际占用CPU)
2.就绪态(Ready)(可运行,但因为其它进程正在运行而暂时停止)
3.阻塞态(Blocked)(除非某种外部事件发生,否则进程不能运行)
-
进程状态之间的转换是通过调度程序(scheduler)来完成的
6.进程的实现
- 为了实现进程模型,操作系统维护着一张表格,即进程表(process table)。每个进程占用一个进程表项,这些表项也成为进程控制块(process control block PCB)
2.线程
1.线程的使用
-
CPU的资源和空间以进程为单位分配
执行和调度以线程为单位分配
同一个进程里的所有线程共享这个线程的资源
-
每一个线程都有自己的栈,因为线程是调度的最基本单位
-
同一个进程里可以有多个线程,线程可以在同一个进程里切换
-
线程比进程更轻量级,所以线程之间的切换代价更小
-
TCB 线程控制块
2.常用的线程函数
- Pthread_create 创建一个新的线程
- Pthread_create 退出一个正在执行的线程
- Pthread_join 等待正在执行的线程
- Pthread_yield 释放CPU让其它的线程来执行
- Pthread_attr_init 创建和初始化线程的属性结构
- Pthread_attr_destory 移除线程的属性结构
3.线程的一些优点
- 线程的创建,退出和切换都是非常快的
- 线程之间通信也是非常容易的
- 线程可以更容易的进行资源共享
4.线程的三种实现方式
- 在用户空间中实现
- 在内核空间中实现
- 混合实现
1.在用户空间中实现
- 内核不知道用户空间的线程
- 线程的管理由用户空间的线程库管理
- 线程的切换不需要内核支持线程,用户的线程可以很快的创建和管理
- 每个线程有专用的线程表(thread table)
- 问题:如果内核是单线程的,那么任何用户线程发起的阻塞系统调用都会造成整个进程阻塞
2.在内核空间中实现
- 线程需要内核的支持,由内核来执行线程的创建,调度和管理
- 没有专门的线程库,由内核来提供API
- 由内核来管理线程的切换
3.线程 vs 进程
1.进程是资源分配的基本单位,所有与该进程有关的资源分配情况,均记录在进程控制块中,进程也是分配主存的基本单位而线程则与资源分配无关,它和该进程的其它线程一起共享进程的资源。
2.不同的进程拥有不同的虚拟地址空间,而同一进程中的多个线程共享同一地址空间
3.进程调度的切换涉及到有关资源指针的保存及进程地址空间的转换等问题。而线程的切换将不涉及资源指针的保存和地址空间的变化。所以线程切换的开销要比进程切换的开销小得多
4.进程的调度与切换都是由操作系统内核完成,而线程则既可由操作系统内核完成,也可由用户程序进行
5.进程可以创建进程,被进程创建的线程也可以创建其它进程
6.进程有创建,执行和消亡的生命周期,线程也有类似的生命周期
4.进程间通信(Inter-process Communication IPC)
-
竞争条件(race conditions):
多个进程访问共享数据,最后的结果取决于进程运行的精确时序
-
避免竞争条件的关键是防止超过一个进程同一时间读或写共享数据
-
互斥(mutual exclusion)
以某种手段确保当一个进程在使用共享变量和文件时,其它进程不能够做同样的操作
-
临界资源(critical resource)
一次只允许一个进程访问的资源称为临界资源
-
临界区域(critical region)
对共享内存进行访问的程序称为临界区
-
如果两个进程不能同时处于临界区中,就能够避免竞争条件
-
提供互斥的四个条件:
-
- 任何两个进程不能同时处于临界区
- 不应该对CPU的数量和速度做任何假设
- 临界区外运行的进程不能阻塞其它进程
- 不能使进程无限等待进入临界区
-
互斥的可能解决方案
-
- 屏蔽中断
- 锁变量
- 严格轮换
- 皮特森解法
- TSL
- 睡眠与唤醒
-
屏蔽中断(disabling interrupts)
在进程进入临界区后禁止所有的中断,在进程离开的时候恢复中断
问题:进程可能会忘记恢复中断,并且不适用于多进程
-
锁变量(lock variable)
创建一个共享变量,初始为0,当进程想进入临界区时,首先测试该锁,如果该锁的值为0,则进入临界区并将其设为1,如果锁为1,则等待至其值变为0
有一定的疏漏,违背了条件1,不能完全避免竞争条件
-
严格轮换法(strict alternation)
使用了轮换变量turn
使CPU忙等待(busy waiting),会浪费CPU的时间
一个进程会被另外一个进程阻塞,违背了条件3
-
Peterson解法
除了使用轮换变量turn以外,还增加了一个interested数组
-
TSL(测试并加锁 test and set lock)
tsl指令可以原子性的操作读和写
-
睡眠与唤醒(sleep and wait)
在前面几种可行的方法中,都存在着CPU忙等的问题,忙等也会造成优先级反转问题
当进程无法进入临界区时进入阻塞状态,而不是忙等
生产者消费者问题
- 两个进程共享一个公共的固定大小的缓冲区
- 生产者向缓冲区中添加信息
- 消费者向缓冲区中提取信息
信号量(semaphore)
-
信号量
信号量是一个结构体,结构体中包含一个整数计数器和一个队列
-
对信号量的操作
PV操作
P(down)操作是获取资源并减去计数器
V(up)操作是释放资源并增加计数器
-
一个信号量的操作是原子性的(atomically),是不可分的(indivisible)
-
如果信号量的计数器小于0,就让该进程阻塞
-
我们采用两种不同的方式使用信号量
1.互斥
2.进行进程间的同步
-
mutex(互斥量)是二进制的信号量
-
mutex是用来解决互斥的问题
-
这个变量只有两个状态:
锁(lock)和解锁(unlock)
0表示解锁,1表示加锁
-
信号量的物理含义
S.count>0表示有S.count个资源可用
S.count=0表示无资源可用
S.count<0则表示S等待队列中的进程个数
P(S):表示申请一个资源
V(S):表示释放一个资源
-
P,V操作必须成对出现,有一个P操作就一定有一个V操作
当为互斥操作时,它们同处于同一进程
当为同步操作时,则不在同一进程中出现
-
如果P(S1)和P(S2)两个操作在一起,那么P操作的顺序至关重要,一个同步P操作和一个互斥P操作在一起时同步P操作在互斥P操作前
-
两个V操作的顺序无关紧要
-
信号量同步的缺点:信号量分布在整个程序中,其正确性分析很困难
-
引入管程(monitor):把信号量和操作封装在一个对象内部
-
管程是由过程,变量及数据结构等组成的一个集合,在一段时间内只允许一个进程访问
-
为了解决阻塞问题,我们需要引入条件变量,针对条件变量的操作是wait和signal
-
wait(x)会导致进程自身阻塞,直到有进程调用signal
-
signal(x)会恢复正在等待的进程
消息传递(Message passing)
- send和receive一个发送消息,一个接收消息
屏障(barrier)
- 除非所有进程都准备进入下一个阶段,否则所有的进程都不能进入下一个阶段
经典的IPC问题
- 生产者消费者问题
- 哲学家进餐问题
- 读者写者问题
5.调度(scheduling)
1.基本的调度算法
1.批处理系统
- 先到先服务(first-come first-served)
- 短作业优先(shortest job first)
2.交互式系统
- 轮转调度(round-robin)
- 优先级调度(priority scheduling)
- 多级队列(multi queue)
- 保证调度,彩票调度和公平分享调度
2.什么是调度
-
调度就是选择接下来在CPU中运行哪个进程
-
抢占式(preemptive)和非抢占式(non-preemptive)
-
非抢占式调度算法等待进程自动结束或者自动阻塞,然后挑选另一个进程执行
-
抢占式调度算法在当前进程的时间片用完后就会被挂起,然后挑选另一个进程执行
-
进程行为
CPU密集型进程和I/O密集型进程
3.何时调度
- 新进程被创建
- 运行进程退出
- 运行进程被阻塞
- I/O操作中断
- 时钟中断
4.调度算法
1.先来先服务(FCFS)
-
也叫做FIFO
-
先请求CPU的会先被分配CPU
-
用于批处理系统中
-
性能标准(performance metric)是平均等待时间(Average Waiting Time)
-
车队效应:越短的进程放在前面性能就越好
-
FCFS的问题
是非抢占式的
没有理想的AWT
不能并行的利用资源
2.短作业优先(SJF)
-
有着最短执行时间的进程优先执行
-
是批处理系统中的调度
-
有两种类型:
抢占式和非抢占式
-
每个进程的运行时间必须提前知道
-
如果所有进程同时就绪,这样的处理就是最优的
-
但如果有些进程不是同时到达,这样的处理就可能不是最优的
抢占式SJF
-
也叫最短剩余时间优先(Shortest Remaining Time First)
-
每次选择剩余时间最短的作业来进行处理
-
会出现饥饿(starvation)现象
即有些作业会一直的等待而不能执行
5.交互式调度算法
1.通常是抢占式的
- 时间会被分为时间片
- 调度在每个时间片开始的时候进行
2.性能标准
- 最小响应时间
- 最优的比例
3.Round-Robin
- 轮转调度,最简单的时间片(quantum)轮转方法
- 如果时间片过大,就会退化成FCFS
- 如果时间片过小,就会有许多不必要的上下文切换的开销
- 一般时间片在10-100ms之间
4.优先调度
-
给每一个任务一个优先级
-
如果优先级相同,则进行FCFS
-
每次挑选一个优先级最高的任务来执行
-
问题:
1.或许没有最优的AWT
2。会造成无限等待和饥饿的问题
-
设置优先级:
静态分配和动态分配
优先级的分配基于用户成本,用户的重要性,进程的类型和对资源的需求等
5.多级队列调度
-
是优先级调度和轮转调度的组合
-
提前把进程放在一个队列里
-
队列之间的调度按照固定优先级和CPU在该队列花费的时间
多级反馈算法
- 每个队列都有一个优先级
- 进程可以在不同的队列之间移动
- 每个队列内的任务会在一个给定的时间片中执行
- 如果时间片到达还没有被执行完,这个进程会被移动到优先级更低的一个队列中,但是下次执行时分配的时间片是上次的两倍
6.其它调度算法
1.保证调度
- 如果有n个进程执行,每个进程都分配1/n的CPU时间
- 计算实际消耗的CPU时间与应得的CPU时间的比率,选择比例最低的一个
2.彩票调度
- 给每个进程一张彩票,每当需要调度的时候,一张彩票会被随机选中,拥有彩票的进程会被执行
- 有更多彩票的进行拥有更高的优先级
3.公平分享调度
- 给每个用户分配相同的时间,而不是按照进程分配
7.内核级线程调度和用户级线程调度
- 在用户级线程调度中,调度只在相同的进程中执行,在一个进程的时间片中,不会调度超出这个进程的线程
- 在内核级线程调度中,调度是由内核选择一个线程运行,而不必考虑线程在哪个进程中