操作系统复习之进程管理

进程与线程

1.进程

定义:

进程是程序的一次执行。
进程是资源分配的基本单位。
进程控制块(PCB)描述进程的基本信息和运行状态,所谓的创建进程和撤销进程都是指对PCB的操作。系统主要利用PCB来描述进程的基本情况和活动过程。

特征:

动态性: 进程的实质是进程实体的执行过程。
并发性:是指多个进程实题同存在于内存中,且能在一段时间内同时运行。
独立性:是指进程实体是一个能独立运行,独立获得资源和独立接受调度的基本单位。
异步性:是指进程是按异步的方式运行的,即按各自独立,不可预知的速度向前推进。

进程的三种基本状态

1.就绪状态

指进程已处于准备好运行的状态,即已经分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行。

2.执行状态

指进程已获得CPU ,其程序正在执行的状态。

3.阻塞状态

这是指正在执行的进程由于发生某种时间暂时无法继续执行时的状态。

进程的创建状态和终止状态

1.创建状态

进程申请一个空白的PCB,并向PCB中填写用于控制和管理进程的信息,然后为该进程分配运行时所必须的资源

2.终止状态

进程终止通常需要经过两个步骤 等待操作系统进行善后处理,最后将PCB清零,并将PCB空间返还系统。当一个进程达到无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结,它将进入终止状态。之后操作系统依然保留一个记录。供其他进程收集。

进程控制块PCB的作用

PCB作为进程实体的一部分,记录了操作系统所需的,用于描述进程的当前情况以及管理进程运行的全部信息。
PCB的作用时使一个在多道程序环境下不能独立运行的程序成为一个能独立运行的基本单位,一个能与其他进程并发执行的程序。
能实现间断性运行方式。在多道程序环境下,程序是采用停停走走间断性的运行方式运行的。当进程因阻塞而暂停运行时,其将保留自己运行时的CPU现场信息,再次被调度运行时,还要恢复其CPU现场信息。
提供进程管理所需要的信息 提供进程调度所需要的信息
实现与其他进程的同步与通信

进程控制块中的信息

1进程标识符
进程标识符用于唯一地标识一个进程,一个进程通常有两种标识符:
(1)外部标识符 为了方便进程对进程的访问,须为每一个进程设置一个外部标识符。
(2)内部标识符 为了方便系统对进程的使用,在OS中又为进程设置了内部标识符
2.处理机状态
处理机状态信息也称为处理机的上下文,主要由处理机的各种寄存器中的内容组成。
3.进程调度信息
记录进程当前的状态及有关进程调度的信息。
4.进程控制信息
指用于进程控制所必须的信息包括程序和数据的地址 进程同步和通信机制
链接指针

进程控制快的组织方式

线性方式
将系统中所有的PCB都组织在一张线性表中,将该表的首地址存放在内存中 的一个专用区域中
链接方式
把具有相同状态进程的PCB分别通过PCB中的链接字链接成一个队列。
索引方式
根据系统所有进程状态的不同,建立几张索引表。

线程

线程是独立调度的基本单位。

扫描二维码关注公众号,回复: 11105260 查看本文章

一个进程中可以有多个线程,它们共享进程资源。

QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。

进程和线程的区别

Ⅰ 拥有资源

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

进程状态的切换

就绪状态(ready):等待被调度
运行状态(running)
阻塞状态(waiting):等待资源
应该注意以下内容:

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

进程同步

主要时指控制多个进程按一定顺序执行

进程同步的基本概念

两种制约关系
1.间接相互制约关系
多个程序在并发执行时,由于共享资源,如CPU,I/O设备等,导致在这些并发执行的程序之间形成相互制约的关系
2.直接相互制约关系
进程间的直接制约关系就是源于他们之间的相互合作,

临界区

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

同步与互斥

同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。
互斥:多个进程在同一时刻只有一个进程能进入临界区。

信号量

信号量(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);
    }
}

管程

使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制,而管程把控制的代码独立出来,不仅不容易出错,也使得客户端代码调用更容易。

c 语言不支持管程,下面的示例代码使用了类 Pascal 语言来描述管程。示例代码的管程提供了 insert() 和 remove() 方法,客户端代码通过调用这两个方法来解决生产者-消费者问题。

monitor ProducerConsumer
    integer i;
    condition c;

    procedure insert();
    begin
        // ...
    end;

    procedure remove();
    begin
        // ...
    end;
end monitor;

管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。

管程引入了 条件变量 以及相关的操作:wait() 和 signal() 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。

经典同步问题

. 哲学家进餐问题

五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。

下面是一种错误的解法,如果所有哲学家同时拿起左手边的筷子,那么所有哲学家都在等待其它哲学家吃完并释放自己手中的筷子,导致死锁。

#define N 5

void philosopher(int i) {
    while(TRUE) {
        think();
        take(i);       // 拿起左边的筷子
        take((i+1)%N); // 拿起右边的筷子
        eat();
        put(i);
        put((i+1)%N);
    }
}

为了防止死锁的发生,可以设置两个条件:

必须同时拿起左右两根筷子;
只有在两个邻居都没有进餐的情况下才允许进餐。

#define N 5
#define LEFT (i + N - 1) % N // 左邻居
#define RIGHT (i + 1) % N    // 右邻居
#define THINKING 0
#define HUNGRY   1
#define EATING   2
typedef int semaphore;
int state[N];                // 跟踪每个哲学家的状态
semaphore mutex = 1;         // 临界区的互斥,临界区是 state 数组,对其修改需要互斥
semaphore s[N];              // 每个哲学家一个信号量

void philosopher(int i) {
    while(TRUE) {
        think(i);
        take_two(i);
        eat(i);
        put_two(i);
    }
}

void take_two(int i) {
    down(&mutex);
    state[i] = HUNGRY;
    check(i);
    up(&mutex);
    down(&s[i]); // 只有收到通知之后才可以开始吃,否则会一直等下去
}

void put_two(i) {
    down(&mutex);
    state[i] = THINKING;
    check(LEFT); // 尝试通知左右邻居,自己吃完了,你们可以开始吃了
    check(RIGHT);
    up(&mutex);
}

void eat(int i) {
    down(&mutex);
    state[i] = EATING;
    up(&mutex);
}

// 检查两个邻居是否都没有用餐,如果是的话,就 up(&s[i]),使得 down(&s[i]) 能够得到通知并继续执行
void check(i) {         
    if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] !=EATING) {
        state[i] = EATING;
        up(&s[i]);
    }
}

进程间的通信

进程同步与进程通信很容易混淆,它们的区别在于:

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

管道

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

#include <unistd.h>
int pipe(int fd[2]);
它具有以下限制:

只支持半双工通信(单向交替传输);
只能在父子进程或者兄弟进程中使用。

有名管道 FIFO

也称为命名管道,去除了管道只能在父子进程中使用的限制。
FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。

消息队列

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

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

信号量

它是一个计数器,用于为多个进程提供对共享数据对象的访问。

共享存储

允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是最快的一种 IPC。

需要使用信号量用来同步对共享存储的访问。

多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用内存的匿名段。

套接字

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

发布了49 篇原创文章 · 获赞 2 · 访问量 3540

猜你喜欢

转载自blog.csdn.net/qq_40623603/article/details/105603102