面试准备之计算机操作系统——进程管理

文章目录

  • 进程与线程
    • 进程
    • 线程
    • 区别
  • 进程状态的切换
  • 进程调度算法
    • 1.批处理系统
      • 1.1先来先服务first-come first-serverd(FCFS)
      • 1.2短作业有限shortest job first(SJF)
      • 1.3 最短剩余时间优先shortest remining time next(SRTN)
    • 2.交互式系统
      • 2.1时间片轮转
      • 2.2优先级调度
      • 2.3多级反馈队列
    • 3.实时系统
  • 进程同步
    • 临界区
    • 同步与互斥
    • 信号量
        • 使用信号量实现生产者-消费者问题
    • 管程
  • 经典同步问题
    • 哲学家进餐问题
    • 读者-写者问题
  • 进程通信
    • 管道
    • FIFO
    • 消息队列
    • 信号量
    • 共享存储
    • 套接字

进程与线程

进程

  • 资源分配的基本单元,操作系统在程序运行时刻创建的实例。
  • 进程控制块描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对PCB的操作。

线程

  • 线程是独立调度的基本单位。
  • 一个进程中可以有多个线程,他们共享进程资源。

区别

  • 拥有资源:进程是资源分配的基本单位,但线程不拥有资源,线程可以访问隶属于进程的资源。
  • 调度:线程是独立调度的基本单位,同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程的线程是,会引起进程切换。
  • 系统开销:由于创建或撤销进程时,系统都为之分配或回收资源,如内存空间,I/O设备等,开销远大于创建或撤销线程时的开销。类似地,在进程切换时,涉及当前执行进程CPU环境的保存及新调度进程环境CPU环境的设置,而线程切换时只需要保存和设置少量寄存器内容,开销很小。
  • 通信:线程间可通过直接读写同一进程中的数据进行通信,但是进程通信需要借助IPC。

进程状态的切换

  • 状态:
    - 创建
    - 就绪状态(ready):等待被调度
    - 运行状态(running)
    - 阻塞状态(waiting):等待资源
    - 结束
  • 只有就绪态和运行态可以相互转换,其他的都是单向转换。就绪状态通过调度算法获得CPU时间,从而进入运行状态;而运行状态的进程,在分配给它的CPU时间片用完之后就会转为就绪状态,等待下一次调度。
  • 阻塞状态是缺少需要的资源从而由运行状态转来,但是该资源不包括缺少CPU时间,缺少CPU时间会从运行态转为就绪态。

进程调度算法

不同的环境调度算法不同,因此需要针对不同的环境来讨论调度算法。

1.批处理系统

批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(提交到终止)。

1.1先来先服务first-come first-serverd(FCFS)

  • 非抢占式调度,按照请求的先后顺序进行调度。
  • 有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成短作业等待时间过长。

1.2短作业有限shortest job first(SJF)

  • 非抢占式调度,按估计运行时间最短的顺序进行调度。
  • 长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远的不到调度。

1.3 最短剩余时间优先shortest remining time next(SRTN)

  • 是短作业有限的抢占版本。按剩余运行时间的顺序进行调度。当一个新的作业到达时,整个运行时间与当前进程的剩余时间做比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。

2.交互式系统

交互系统有大量的用户交互操作,在该系统中调度算法的目标时快速的响应。

2.1时间片轮转

  • 将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
  • 时间片轮转算法的效率和时间片的大小有很大关系:
    - 因为进程切换需要保存旧进程的信息并导入新进程的信息,如果时间片太小,会导致进程切换太频繁,在进程切换上花时间过多。
    - 如果时间片太长,那么实时性得不到保证。

2.2优先级调度

  • 为每个进程分配一个优先级,按优先级进行调度。
  • 为防止低优先级的进程永远等不到调度,可以随着时间的退役增加等待进程的优先级。

2.3多级反馈队列

  • 一个进程需要执行100个时间片,如果采用时间片轮转调度算法,那么需要切换100次。
  • 多级队列就是为这种需要连续执行多个时间片的进程考虑的,它设置了多个队列,每个队列时间片大小都不同,例如:1,2,4,8…。进程在一个队列中没有执行完,就会被移到下一个队列。这种方式下,原本需要时间片较多的进程就会减少切换次数。
  • 每个队列也有优先权,最上面的优先权最高。因此只有上一个队列没有进程在排队时,才能调度当前队列中的进程。可以将这种调度算法看成是时间片轮转调度和优先级调度算法的结合。

3.实时系统

  • 实时系统要求一个请求必须在一个确定时间内得到响应。
  • 分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。

进程同步

临界区

  • 对临界资源进行访问的那段代码称为临界区。
  • 为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。
//entry section
//critical section
//exit section

同步与互斥

  • 同步:多个进程按一定的顺序执行。
  • 互斥:多个进程在同一时刻只有一个进程进入临界区。

信号量

  • 信号量(Semaphore)是一个整型变量,可以对其进程down和up操作,也就是常见的P和V操作。
    - down:如果信号量大于0,执行-1操作;如果信号量等于0,进程睡眠,等待信号量大于0.
    - up:对信号量执行+1操作,唤醒睡眠的进程让其完成down操作。
  • down和up操作需要被设计成原语,不可分割,通常的做法实在执行这些操作的时候屏蔽中断。
  • 如果信号量只能为0或者1,那么就成为了互斥量(Mutex),0表示临界区已经被加锁,1表示临界区解锁。
typedef int semaphore;
semaphore mutex = 1;
void P1() {
		down(&mutex);
		// 临界区
		up(&mutex);
} 

void P2() {
		down(&mutex);
		// 临界区
		up(&mutex);
}

使用信号量实现生产者-消费者问题

问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。

因为缓冲区属于临界资源,因此需要使用一个互斥量 mutex 来控制对缓冲区的互斥访问。

为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。

注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行down(empty) 操作,发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,消费者就无法执行 up(empty) 操作,empty 永远都为 0,导致生产者永远等下,不会释放锁,消费者因此也会永远等待下去。

#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
void producer() {
		while(TRUE) {
				int item = produce_item();
				down(&empty);
				down(&mutex);
				insert_item(item);
				up(&mutex);
				up(&full);
		}
}

void consumer(){
		while(true){
				down(&full);
				down(&mutex);
				int item = remove_item();
				consume_item(item);
				up(&mutex);
				up(&empty);
		}
}

管程

经典同步问题

哲学家进餐问题

读者-写者问题

允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。

一个整型变量 count 记录在对数据进行读操作的进程数量,一个互斥量 count_mutex 用于对 count 加锁,一个互斥量data_mutex 用于对读写的数据加锁。

typedef int semaphore
semaphore count_mutex = 1;
semaphore data_mutex = 1;
int count = 0;

void reader(){
		while(true){
				down(&count_mutex);
				count++;
				if(count == 1)	down(&data_mutex);
				up(&count_mutex);
				reaad();
				down(%count_mutex);
				count--;
				if(count == 0)	up(&data_mutex);
				up(&count_mutex);
		}
}

void writer(){
		while(true){
				down(&data_mutex);
				write();
				up(&data_mutex);
		}
}

进程通信

进程同步与进程通信很容易混淆,区别如下:

  • 进程同步:控制多个进程按照一定顺序执行;
  • 进程通信:进程间传输信息。
    进程通信是一种手段,进程同步时一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程间同步所需要的信息。

管道

管道是通过调用pipe()函数创建的,fd[0]用于读,fd[1]用于写。

#include
int pipe(int fd[2]);

它有以下限制:

  • 只支持半双工通信,单向交替传输;
  • 只能在父子进程中使用。

FIFO

也称命名管道,去除了管道只能在父子进程中使用的限制。

#include 
int mkfifo(const char *path, mode_t mode);
int mkfifoat(int fd, const char *path, mode_t mode);

FIFO常用于客户-服务器应用程序中,FIFO用作汇聚点,在客户进程和服务器进程之间传递数据。

消息队列

相比于FIFO,消息队列具有以下特点:

  • 消息队列可以独立于读写进程存在,从而避免FIFO中同步管道的打开和关闭时可能产生的困难。
  • 避免了FIFO的同步阻塞问题,不需要进程自己提供同步方法。
  • 读写进程可以根据消息类型有选择地接收消息,而不像FIFO只能默认地接收。

信号量

它是一个计数器,用于多进程对共享数据对象的同步互斥访问。

共享存储

允许多个进程共享一个给定的存储区。因为数据不需要再进程之间复制,所以这是最快的一种IPC。
需要使用信号量用来同步对共享存储的访问。
多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外XSI共享内存不是使用文件,而是使用内存的匿名段。

套接字

与其他通信机制不同的是,它可用于再不同机器之间的进程通信。

你可能感兴趣的:(面试准备之计算机操作系统——进程管理)