本系列文章为OS课程期末复习,个人使用
什么是操作系统
一种扩展的机器(用户角度)
-
隐藏了了复杂的、多样化的硬件接口
-
取而代之,提供了一个统一的虚拟机器 (read, write…)
一种资源管理者(硬件角度)
- 时间复用
- 空间复用
- 例如:多进程、多线程等等
操作系统的基本类型
- 分时
考虑交互性和响应时间 - 实时
考虑实时性和可靠性 - 批处理
考虑周转时间和系统吞吐量
目前的操作系统,通常具有分时、实时和批处理功能,又称作通用操作系统。
操作系统的功能
- 处理机管理
- 存储器管理
- 设备管理
- 文件管理
处理机管理
进程
进程是程序执行时的一个实例
它包括:程序计数器(PC)、寄存器(Regieters)、相关变量(Variables)
所以看起来,每一个进程都拥有一个虚拟的CPU
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
系统利用进程控制块(PCB)来描述进程的基本情况和活动过程,进而控制和管理进程。
程序段、相关的数据段和PCB三部分构成了进程实体(也称进程映像)
进程控制
基本功能
- 创建新进程
- 终止进程
- 进程运行中的状态转换
一般是由OS内核中的原语来实现
进程创建的条件
- 系统初始化
- 正在运行的进程执行了创建进程的系统调用
- 用户请求创建一个新进程
- 一个批处理作业的初始化
进程终止的触发条件
- 正常退出(自愿的)
exit(0),ExitProcess(0) - 出错退出(自愿的)
exit(n) - 严重错误(非自愿)
- 被其他进程杀死(非自愿)
进程的状态 进程间的状态转换
运行态:该时刻进程实际占用CPU
就绪态:可运行,因为其他进程正在运行而暂时停止
阻塞态:除非某种外部事件发生,否则进程不能运行
操作系统中最底层的调度程序之上有许多进程,所以关于中断处理、启动进程和停止进程的具体细节都隐藏在调度程序中
原语(Primitive)
原语是由若干条指令组成的,用于完成一定功能的一个过程
与一般过程的区别在于:它们是**“原子操作”**(Action Oretation)
原子操作,是指一个操作中不可分割的基本单位,在执行过程中不允许被中断
线程
来源
每个进程控制着一块独立的地址空间,可不可能有多个“进程”(线程)来控制同一块地址空间?
需要线程的理由
- 有些是用多进程所无法描述的
- 更轻量级
创建、切换、撤销 - 运行效率
如何能使多个程序更好地并发执行同时又尽量减少系统的开销?
作为调度和分派的基本单位,不同时作为拥有资源的单位
对于拥有资源的基本单位,又不对之进行频繁的切换 - 多CPU并行的支持
引入进程
减少程序在并发执行时所付出的时空开销,使OS具有更好的并发性。
线程与进程
-
一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
-
进程拥有独立的虚拟地址空间,而同一个进程中的线程,共享内存空间与资源。
-
同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。
-
线程只需要保存PC、寄存器、堆栈等,无需保存大量的文件、进程管理信息。线程之间也是没有保护并且平等的,因为它们共享了同样的资源,包括打开的文件、子进程等。
-
系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多。
-
线程也被称为轻量级进程。
-
线程例子
字处理软件
带三个线程(人机交互、格式化计算、自动存盘)的字处理软件
一个线程可以因I/O而等待,另两个线程仍然能够运行。
web服务器
指派(dispatcher)线程从网络中读取请求, 选择一个空转(阻塞态)工作(worker)线程并提交给其请求,Dispatcher 唤醒该 worker 线程转为就绪态以完成工作。先查缓存(所有线程均可访问),若不命中,再读取服务器磁盘,这时,可能会被阻塞,则Dispatcher可能挑选或运行另一工作线程。
windows中的进程和线程
-
Windows具有内核级的线程模型
-
Windows中进程和线程有明确的界限。
进程是分配资源的单位,具有独立的数据结构
- 进程提供一个各种资源的容器,定义了一个地址空间作为基本的执行环境;
线程是CPU调度的基本单位,具有自己的数据结构。
- 线程是一个指令执行序列,可以直接访问进程中的资源
每个进程中至少有一个线程,线程在任一时刻必属于某个进程。
linux线程
线程创建
进程被创建时,系统会为其创建一个主线程,而要在进程中创建新的线程,则可以调用pthread_create:
pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void * (start_routine)(void*),
void *arg);
start_routine为新线程的入口函数,arg为传递给start_routine的参数.
线程退出
(1)执行完成后隐式退出;
(2)由线程本身显示调用pthread_exit 函数退出;
pthread_exit (void * retval) ;
(3)被其他线程用pthread_cance函数终止:
pthread_cance (pthread_t thread) ;
在某线程中调用此函数,可以终止由参数thread 指定的线程。
如果一个线程要等待另一个线程的终止,可以使用pthread_join函数,该函数的作用是调用pthread_join的线程将被挂起直到线程ID为参数thread的线程终止:
pthread_join (pthread_t thread, void** threadreturn);
进程同步
并发执行的进程之间存在着相互制约关系。
-
间接相互制约:共享资源
-
直接相互制约:数据提供
为了协调进程之间的直接制约关系,引入了进程同步
- 异步环境下的一组并发进程因制约而互相发送消息、进行互相合作、互相等待,使得各进程按一定的速度执行的过程
临界资源
一次(一段时间内)仅允许一个进程使用的资源
-
物理上的打印机
-
存在硬盘或内存中被多个进程所共享的一些变量和数据等
进程间应采取互斥方式,实现对临界资源的共享
进程互斥
当一个进程进入临界区使用临界资源时,另一个进程必须等待, 当占用临界资源的进程退出临界区后,另一进程才允许去访问此临界资源
-
一次只允许一个进程进入临界区
-
任何时候,处于临界区的进程不得多于一个
-
进入临界区的进程要在有限的时间内退出
-
如果不能进入自己的临界区,则应让出处理机资源
考虑采用P-V操作的方法解决临界区互斥问题
进程间通信
生产者-消费者问题
两个进程共享同一个缓存
-
生产者:一个进程生产数据,放入缓存中
-
消费者:一个进程消费数据,从缓存中取数据
要求
-
缓存满时,生产者不能再生产数据
-
缓存空时,消费者不能再消费数据
信号量(semaphore)
•down(s),or P(s) (Proberen) P操作
if s >0, then s=s-1;
if s==0, then sleep();
•up(s), or V(s) (Verhogen) V操作
–if s==0 && 有sleep进程 then 将其中一个进程唤醒
–if s==0 && 没有sleep 进程,then s=s+1
–if s>0, then s=s+1
P、V操作均为原子操作,不可分割
生产者-消费者代码
这个解决方案使用了三种信号量:full,用来记录充满的缓冲槽数目;empty,记录空的缓冲槽数目;mutex,用来确保生产者和消费者不会同时进入缓冲区。
Full 被初始化为 0 ,empty 初始化为缓冲区中插槽数,mutex 初始化为 1。信号量初始化为 1 并且由两个或多个进程使用,以确保它们中同时只有一个可以进入关键区域的信号被称为 二进制信号量(binary semaphores)。
如果每个进程都在进入关键区域之前执行 down 操作,而在离开关键区域之后执行 up 操作,则可以确保相互互斥。
define N 100 /* number of slots in the buffer */
typedef int semaphore; /* semaphores are a special kind of int */
semaphore mutex = 1; /* controls access to critical region */
semaphore empty = N; /* counts empty buffer slots */
semaphore full = 0; /* counts full buffer slots */
void producer(void)
{
int item;
while (TRUE) { /* TRUE is the constant 1 */
item = produce item( ); /* generate something to put in buffer */
down(&empty); /* decrement empty count */
down(&mutex); /* enter critical region */
insert_item(item); /* put new item in buffer */
up(&mutex); /* leave critical region */
up(&full); /* increment count of full slots */
}
}
void consumer(void)
{
int item;
while (TRUE) { /* infinite loop */
down(&full); /* decrement full count */
down(&mutex); /* enter critical region */
item = remove_item( ); /* take item from buffer */
up(&mutex); /* leave critical region */
up(&empty); /* increment count of empty slots */
consume item(item); /* do something with the item */
}
}
调度
当多个进程竞争使用CPU,怎么办?
– CPU使用不存在竞争条件问题
– 但有些进程对CPU使用会有特殊的要求
- 如批处理、或GUI等
– 进程切换也不能过于频繁
何时需要调度
-
创建进程
- 如:父进程和子进程中,哪个设为运行状态?
-
进程退出
-
进程阻塞
-
I/O中断
-
基于时钟的调度
-
非抢占式调度(nonpreemtive)
-
抢占式调度(preemtive)
-
调度算法的分类
-
批处理(Batch)
批处理系统广泛应用于商业领域,比如用来处理工资单、存货清单和其他周期性作业。在批处理系统中,一般会选择使用非抢占式算法或者周期性比较长的抢占式算法。这种方法可以减少线程切换因此能够提升性能。
-
交互式(Interactive)
在交互式用户环境中,为了避免一个进程霸占 CPU 拒绝为其他进程服务,所以需要抢占式算法。服务器属于此类别,因为它们通常为多个(远程)用户提供服务,而这些用户都非常着急。计算机用户总是很忙。
-
实时(Real time)
在实时系统中,抢占有时是不需要的,因为进程知道自己可能运行不了很长时间,通常很快的做完自己的工作并阻塞。实时系统与交互式系统的差别是,实时系统只运行那些用来推进现有应用的程序,而交互式系统是通用的,它可以运行任意的非协作甚至是有恶意的程序。
、存货清单和其他周期性作业。在批处理系统中,一般会选择使用非抢占式算法或者周期性比较长的抢占式算法。这种方法可以减少线程切换因此能够提升性能。 -
交互式(Interactive)
在交互式用户环境中,为了避免一个进程霸占 CPU 拒绝为其他进程服务,所以需要抢占式算法。服务器属于此类别,因为它们通常为多个(远程)用户提供服务,而这些用户都非常着急。计算机用户总是很忙。
-
实时(Real time)
在实时系统中,抢占有时是不需要的,因为进程知道自己可能运行不了很长时间,通常很快的做完自己的工作并阻塞。实时系统与交互式系统的差别是,实时系统只运行那些用来推进现有应用的程序,而交互式系统是通用的,它可以运行任意的非协作甚至是有恶意的程序。