some trivial modifications.

This commit is contained in:
Shine wOng
2019-12-02 13:43:00 +08:00
parent 4c2a3c44bf
commit 0ff5971b49
2 changed files with 12 additions and 12 deletions

View File

@@ -19,7 +19,7 @@
信号量是操作系统提供的一种协调共享资源访问的方法。它采用一种简明的方法来协调多个资源的访问,即采用信号量来表示当前剩余的系统资源数量。如果有一个进程请求某个资源,则将该资源的信号量递减;反之,如果某个进程释放了资源,就将该资源的信号量递增。这里对信号量的操作都是被操作系统封装起来的,因此可以看到,信号量更多的是一种抽象数据类型,由一个整型变量和两个基本操作组成,通常用`P`操作和`V`操作来表示递减和递增,分别对应了荷兰语里面的`尝试减少`和`增加`。由于`PV`操作的对象,即`sem`变量实质上也是一个共享变量,对它的访问是需要互斥进行的,因此这里的`PV`操作都需要是原子操作。
```c
```cpp
class Semaphore{
private:
int sem;
@@ -38,9 +38,9 @@ public:
当一个进程释放了它占用的资源时,首先应该执行`++sem`表示将一个资源归还给了操作系统。此时如果`sem <= 0`,则表示还有其他进程在等待这个资源,所以应该从等待队列中挑选一个进程,将其唤醒;否则,如果`sem > 0`,表示当前已经有空闲的资源了,所以没有进程在等待队列中,直接退出就可以了。
根据这里的分析,给出上面`Semaphone`类的伪代码实现:
根据这里的分析,给出上面`Semaphore`类的伪代码实现:
```c
```cpp
class Semaphore{
private:
int sem;
@@ -78,7 +78,7 @@ void Semaphore::up(){
进程在进入临界区之间,首先需要获得锁,即对应了信号量的`P`操作;进程退出临界区之后,需要释放锁,让其他进程也可以进入,这对应了信号量的`V`操作。因此,只需要成对地使用`P`操作和`V`操作,就实现了利用信号量对临界区的互斥访问。其代码如下:
```c
```cpp
mutex = new Semaphore(1);
mutex->down();
@@ -94,7 +94,7 @@ mutex->up();
从信号量的观点来看,进程2等待的`prev`函数执行,实际上也是等待某种资源,而这种资源只有通过进程1才可以释放,在此之前这种资源的数量都是零。于是可以形成下面的代码:
```c
```cpp
condition = new semaphore(0);
//for process 1
@@ -124,7 +124,7 @@ process2(){
可见,问题的关键,就在于利用信号量实现上面三组同步互斥关系。首先,为了实现对缓冲区的互斥访问,需要设置一个二进制信号量`mutex`;为了指示生产者等待消费者的关系,设置二进制信号量`emptybuffer`,表示当前空闲缓冲区的大小;相应的,也设置信号量`fullbuffer`表示当前已被占用的缓冲区的大小。这样,根据上面叙述的利用信号量实现同步互斥的模式,可以形成下面的代码:
```c
```cpp
mutex = new semaphore(1);
emptybuffer = new semaphore(n); //buffers are all empty in the begining
fullbuffer = new semaphore(0);
@@ -163,7 +163,7 @@ consume(){
和信号量一样,为了使得进程可以等待某一个条件变量,每个条件变量也都设置了一个等待队列。但是和信号量不一样的是,条件变量并不管理空闲资源的数量,而只是维护了等待当前条件变量的进程个数,这样,可以给出条件变量的类定义:
```c
```cpp
class condvar{
private:
int num_waiting;
@@ -176,7 +176,7 @@ public:
这里的条件变量,本质上也是属于进程之间的共享资源,对它们的访问也需要互斥地进行。因此,任一时刻至多只能有一个进程可以进入管程执行管程代码,为此,管程还需要一个互斥访问锁。这样,就可以给出管程的类定义了:
```c
```cpp
class monitor{
private:
lock mutex;
@@ -192,7 +192,7 @@ public:
这样,当一个进程想要访问管程代码时,首先需要请求管程的互斥访问锁,获得了这个锁后方可进入管程,并且阻止其他进程的进入。一旦进程请求某个资源失败,它将进入等待状态并且放弃CPU的使用权,但是除此以外,它还需要释放管程锁以使其他进程也可以进入管程;当该进程终于获得这个资源时,它将从等待状态进入运行态,并且重新请求管程锁以继续执行。下面给出`wait`和`signal`的伪代码实现:
```c
```cpp
void condvar::wait(){
num_waiting++;
q.enqueue(curr_proc);
@@ -217,7 +217,7 @@ void condvar::signal(){
根据前面对生产者-消费者问题的分析,为了实现缓冲区的互斥访问,并不需要额外的变量,使用管程的互斥访问锁就可以了。对于生产者而言,如果缓冲区满了,则需要等待`缓冲区有空`条件,为此设置`notFull`条件变量;对于消费者而言,如果缓冲区为空,则需要等待`缓冲区非空`条件,为此设置`notEmpty`条件变量。此外,还需要一个额外的变量`count`来表示当前缓冲区被占用的个数。具体的代码如下:
```c
```cpp
void monitor_init(){
int count = 0; // buffers are empty in the beginning
lock mutex;
@@ -269,7 +269,7 @@ while(count == n)
......
```
这是因为,在`Hansen`管程的语意下,被阻塞的进程需要和其他尚未进入管程的进程,同时竞争管程访问的权限`mutex`。因此,当被阻塞管程被唤醒后,也许有一个另外的生产者进程已经得到执行,使得当前缓冲区再次满了,被唤醒的进程需要再次判断它请求的资源是否为空闲。
这是因为,在`Hansen`管程的语意下,被阻塞的进程需要和其他尚未进入管程的进程,同时竞争管程访问的权限`mutex`。因此,当被阻塞进程被唤醒后,也许有一个另外的生产者进程已经得到执行,使得当前缓冲区再次满了,被唤醒的进程需要再次判断它请求的资源是否为空闲。
而在`Hoare`管程的语意下,第二个进程释放第一个进程的资源后,将第一个进程唤醒,然后自己立刻进入阻塞状态,此时第一个进程将得到调度执行。该语意的实质是保证已经进入管程的阻塞进程优先于尚未进入管程的进程执行。因此,当第一个进程执行完毕后,它不会释放锁,而是直接将CPU的控制权转交给第二个进程。`Hoare`管程的执行流程如下图:

View File

@@ -44,7 +44,7 @@ lab7的主要任务其实就只有两个,即在ucore中实现信号量机制
首先将信号量`P`和`V`操作的伪代码贴在下面:
```c
```cpp
class Semaphore{
private:
int sem;