Tips:并行是指在同一时刻多个事件一起执行,并发是指在同一时间间隔中多个事件一起执行。
进程控制块(PCB)、程序段和相关数据段构成了进程实体,也就是我们常说的进程。
通常我们所说的创建进程和撤销进程实质上是对进程实体中的进程控制块进行创建和撤销。
进程控制块PCB包含:
进程的特性:
Tips:只有就绪状态和执行状态可以互相转换。
后两个状态不一定加。
传统操作系统中,进程是独立调度和拥有资源的基本单位。在引入线程的操作系统中,线程是独立调度的基本单位,进程是资源分配的基本单位。
同一个进程中的线程切换不会引起进程切换,但是从一个进程中的线程切换到另外一个进程中的线程时,会引起进程切换。
ps:如果线程也可以拥有资源,那么线程切换的开销也就很大了,引入线程就没有太大的意义了。
进程调度又称低级调度,主要任务是按照某种方法和策略从就绪队列中选择一个进程分配处理机。
进程调度方式可以分为:
是一种非抢占式的调度算法,FCFS调度算法每次从就绪队列中选择最先进入队列的进程,并将处理机分配给它。
特点是算法简单,但是效率低,对长作业有优势,对短作业不友好,短作业需要等到长作业完全结束才能运行,造成短作业等待时间过长。因此实际使用的时候结合其他调度策略,比如优先级调度策略,对于优先级相同的进程采用先到先服务处理。
多道程序的情况下进程是并发执行的,为了协调进程间制约关系,引入进程同步概念。比如1+2*3,分为加法进程和乘法进程,如果不进行协调就可能发生加法发生在乘法之前。
一次仅允许一个进程访问的资源称为临界资源,打印机等物理设备是临界资源,还有些变量啥的也是临界资源。存放临界资源的代码称为临界区。
为了互斥访问临界资源,分为三部分
entry section //进入区,检查能否访问临界区,可以就设置正在访问的标志阻止其他进程访问
critical section //临界区
exit section //离开区 ,离开后清除正在访问标志,允许其他进程访问
信号量是一个整型变量,可以进行wait(),signal()操作(P,V或者down,up)操作。
如果信号量只能取0,1那么就是互斥量,0表示临界区加锁,1表示临界区解锁。
typedef int semaphore;
semaphore mutex = 1;
void P1() {
down(&mutex);
// 临界区
up(&mutex);
}
void P2() {
down(&mutex);
// 临界区
up(&mutex);
}
使用信号量访问资源的时候要进行同步操作,大量的同步操作就被分散在各个进程中,而且可能因为同步进程使用不当造成死锁,于是使用管程将控制管理的代码独立出来,不仅不容易出错,也更容易管理了。
管程:由一组数据以及定义在这组数据上的操作组成的一种软件模块,这组操作能够初始化管程中的数据和同步进程。
管程引入了 条件变量 以及相关的操作:wait() 和 signal() 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。
// 管程
monitor ProducerConsumer
condition full, empty;
integer count := 0;
condition c;
procedure insert(item: integer);
begin
if count = N then wait(full);
insert_item(item);
count := count + 1;
if count = 1 then signal(empty);
end;
function remove: integer;
begin
if count = 0 then wait(empty);
remove = remove_item;
count := count - 1;
if count = N -1 then signal(full);
end;
end monitor;
// 生产者客户端
procedure producer
begin
while true do
begin
item = produce_item;
ProducerConsumer.insert(item);
end
end;
// 消费者客户端
procedure consumer
begin
while true do
begin
item = ProducerConsumer.remove;
consume_item(item);
end
end;
问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。
因为缓冲区属于临界资源,因此需要使用一个互斥量 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);
}
}
int count=0; //计数器
semaphore mutex=1; //计数器互斥量
semaphore rw=1; //读写互斥量
writer(){
while(true){
P(rw);
write;
V(rw);
}
}
Reader(){
while(true){
P(mutex);
if(count==0)
P(rw);
count++;
V(mutex);
read;
P(mutex);
count--;
if(count==0)
V(rw);
V(mutex);
}
}
五个哲学家坐在桌子上,两人之间有一个筷子,要同时拥有两个筷子才能进餐,那么就有可能造成饥饿或者死锁。
解决办法:
#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]);
}
}
原文:https://www.cnblogs.com/k-will/p/12828621.html