操作系统04进程同步与通信

4.1 进程间的相互作用

4.1.1 进程间的联系
资源共享关系
相互合作关系

临界资源应互斥访问。
临界区:不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它们进行访问。
把在每个进程中访问临界资源的那段代码称为临界资源区。
显然,若能保证诸进程互斥地进入自己的临界区,便可实现它们对临界资源的互斥访问。

为此,每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看其是否正在被访问。
如果没被访问,则进入临界区,且设置正访问标志;如果正被访问,则不进入临界区。

同步机制应遵循的标准:
1、空闲让进
2、忙则等待
3、有限等待    对要求访问临界资源的进程,应保证该进程能在有效的时间进入自己的临界区,以免陷入“死等”状态
4、让权等待    当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”状态


—————————————————————————————————————————————————————————————————


4.1.2 利用软件方法解决进程互斥问题
全局共享变量,适用于两个进程,非重点,用于说明遇到的情况。现在很少采用。
算法1:
设置一个公用整形变量turn,用于指示被允许进入临界区的编号,即若turn=1,表示允许p1进入临界区。
main()
{
    cobegin{
        p1;
        p2;
    }
}
对p1:
while (1)
{
    while (turn != 1) no-op;
    critical section;
    turn = 2;
}

该算法可以确保每次只允许一个进程进入临界区。但是强制两个进程轮流进入临界区,资源利用不充分。
如p1退出临界区后将turn置2,如果此时p2不进入临界区,且p1又想再次访问临界区,违背“空闲让进”原则。


算法2:
算法1的问题在于:它采取了强制的方法让p1和p2轮流访问临界资源,完全不考虑它们的实际需要。
算法2的思想是对两个线程置两个标志位flag1和flag2。若flag1=1,表示p1正在执行;若flag2=1,表示p2正在执行。
int flag1=0;
int flag2=0;
对p1:
while (1)
{
    while (flag2 != 0) no-op;
    flag1=1;
    critical section;
    flag1=0;
}

如果两个进程在开始时几乎同时进入临界区,因而同时发现对方的访问标志是0,于是两个进程都先后进入临界区,此时违背了“忙则等待”原则。

算法3:
使要进入临界区的进程先设置其要求进入的标志,然后再去查看其它进程的标志。
对p1:
while (1)
{
    flag1=1;
    while (flag2 != 0) no-op;
    critical section;
    flag1=0;
}

此种算法导致死锁,违背了“有限等待”、“空闲让进”准则。

算法4:
组合了算法1和算法3的概念。
p1将flag1置1代表希望进入临界区,并将turn置2代表允许p2进入临界区;判断flag2 && turn=2为真,等待,否则,进入。
对p1:
while (1)
{
flag1=1;
turn=2;
while (flag2 && turn==2) no-op;
critical section;
flag1=0;
}
此算法即保证了“空闲让进”,又保证了“忙则等待”。

上述4种算法均为忙式等待,不满足“让权等待”。


—————————————————————————————————————————————————————————————————


4.1.3 利用硬件方法解决进程互斥问题
安全利用软件方法来解决诸进程互斥进入临界区的问题有一定难度且有很大局限性,因而现在已很少采用。
现在许多计算机已提供了一些特殊的硬件指令,这些指令允许对一个字中的内容进行检测和修正,或交换两个字的内容。

1、利用Test-and-Set指令实现互斥
int TS(static int lock)
{
    int TS=lock;
    lock=1;
    return(TS);
}
lock=0表示资源空闲。
为了实现诸进程对临界资源的互斥访问,可为每个临界资源设置一个全局变量lock并赋初值0,表示资源空闲。
用TS指令记录变量lock的状态,并将1赋予lock,这等效于关闭了临界区。
while (1)
{
    while(TS(lock))  no-op;
    critical section;
    lock=0;
}

2、利用Swap指令实现进程互斥
Swap指令称为交换指令。在微机中该指令又称为XCHG指令,用于交换两个字的内容。
void Swap(static int a,b)
{
    int temp;
    temp=a;
    a=b;
    b=temp;
}
可为临界资源设置一个全局变量lock。其初值为0,在每个进程中再利用一个局部变量key。
while (1)
{
    key=1;
    do{
        swap(lock, key);
    } while (key);
    critical section;
    lock=0;
}

利用硬件指令能有效实现进程互斥,但却不能满足“让权等待”准则,造成处理机时间的浪费,而且也很难将其用于解决较复杂的进程同步问题。

—————————————————————————————————————————————————————————————————

4.1.4 信号量机制
应用广泛:单处理机系统、多处理机系统、计算机网络

信号量的类型

·信号量分为: 互斥信号量 和 资源信号量

·互斥信号量用于申请或释放资源的使用权,常初始化为1

·资源信号量用于申请或归还资源,可以初始化为大于1的正整数,表示系统中某类资源的可用个数。

·wait操作用于申请资源(或使用权),进程执行wait原语时,可能会阻塞自己。

·signal操作用于释放资源(或归还资源使用权),进程执行signal原语时,有责任唤醒一个阻塞进程。

 

信号量的意义

·互斥信号量:申请/释放使用权,常被初始化为1

·资源信号量:申请归还资源,资源信号量可以初始化为一个正整数,表示系统中某类资源的可用个数。 S.count的意义为

>S.count >= 0 表示还可执行wait(S)而不会阻塞的进程数(可用资源)

>S.count < 0 表示S.queue队列中阻塞进程的个数(被阻塞进程数)

 

S.count的取值范围

·当仅有两个并发进程共享临界资源时,互斥信号量仅能取值 -1 0 1

其中S.count=1,表示无进程进入临界区

S.count=0,表示已有一个进程进入临界区

S.count=-1,表示已有一个进程正在等待进入临界区

·当用S来实现n个进程互斥时,S.count的取值范围为  1  -(n-1)

·操作系统内核以系统调用形式提供wait signal原语,应用程序通过系统调用实现进程间的互斥。

P(S)=wait(S)

V(S)=signal(S)

·工程实践证明,利用信号量方法实现进程互斥是高效的,一直被广泛采用。



1、记录型信号量机制
typedef struct{
    int value;    //整型值,代表资源数目
    list of process *L;  //链表,链接所有等待该信号量代表资源的进程
}semaphore;

两个标准原子操作:wait(s)和signal(s)    这两个操作长期以来被称为P、V操作。
原子性:执行过程不可中断,即当一个进程在修改某信号量时,没有其他进程可同时对该信号量进行修改。
void wait(static semaphore s)
{
    s.value--;
    if (s.value < 0)  block(s.L);
}

void signal(static semaphore s)
{
    s.value++;
    if (s.value <= 0)  wakeup(s.L);
}

s.value的初值表示系统中某类资源的数目,因为又称为资源信号量。
每次wait操作意味着请求一个单位的资源,描述为s.value--;当s.value<0时,表示资源已分配完毕,用block原语进行自我阻塞,放弃处理机并插入到信号量链表s.L中。遵循“让权等待”原则。此时,s.value的绝对值数代表在该信号量链表中。
每次signal操作,表示执行进程释放一个单位资源,故s.value++;加1后若s.value<=0,则表示在该信号量链表中仍有等待该资源的进程被阻塞,故还应调用wakeup原语,唤醒进程访问临界资源。

信号量实现互斥
semaphore mutex=1;
void procedure1()
{
while(1)
{
wait(mutex);
critical section;
signal(mutex);
}
}
void procedure2()
{
while(1)
{
wait(mutex);
critical section;
signal(mutex);
}
}
main()
{
cobegin{
procedure1();
procedure2();
}
}
注意:wait(mutex)和signal(mutex)必须成对出现。
缺少wait(mutex)将导致系统混乱,不能保证对临界资源的互斥访问;
缺少signal(mutex)将会使临界资源永远不被释放。

还可用信号量来描述程序或语句之间的前驱关系。
信号量初值为0
可利用信号量,按照语句的前趋关系,写出一个可并发执行的程序。

main(){
semaphore a=b=c=d=e=f=g=0;
cobegin{
{T1;  signal(a);    signal(b);}
{wait(a);  T2;  signal(c);  signal(d)}
{wait(b);  T3;  signal(e)}
{wait(c);  T(4);  signal(f)}
{wait(d);  T(5);  signal(g)}
{wait(e);  wait(f);  wait(g);  T6}
}

2、信号量集机制
(1)AND型信号量集机制
上述互斥针对进程之间要共享一个临界资源而言的。
在有些应用场合,一个进程需要先获得两个或更多的共享资源后方能执行任务。
process P:
wait(Amutex);
wait(Bmutex);
process Q:
wait(Bmutex);
wait(Amutex);
此种情况可能引起死锁。
AND同步机制的基本思想是,将所需的所有临界资源一次性全部分配给进程,该进程用完后一次性全部释放。
对若干临界资源的分配采取原子操作方式,要么全部分配,要么一个都不分配。
void Swait(s1, s2, ..., sn)
{
    if(s1>=1 && s2>=1 && ... && sn>=1)
        for(i=1; i<=n; ++i)    
            si.value--;
    else
        Place the process in the awaiting queue associated with the si found with si<1,and set the program count of this count of this process to the beinning of Swait operation.
}

void Ssignal(s1, s2, ..., sn)
{
for(i=1; i<=n; ++i)
{
si.value++;
Remove all the process waiting in the queue associated with si into the ready queue;
}

(2)一般信号量集
上述信号量机制,只能对信号量进行加一减一操作。
扩充:
可以加n减n;
在小于某个值时,不分配。
void Swait(s1, t1, d1, s2, t2, d2, ..., sn, tn, dn)
{
if(s1>=t1 && s2>=t2 && ... && sn>=tn)
for(i=1; i<=n; ++i)    
si.value-di;
else
Place the process in the awaiting queue associated with the si found with si<ti,and set the program count of this count of this process to the beinning of Swait operation.
}

void Ssignal(s1, t1, d1, s2, t2, d2, ..., sn, tn, dn)
{
for(i=1; i<=n; ++i)
{
si.value+=di;
Remove all the process waiting in the queue associated with si into the ready queue;
}

几种一般“信号量集”的特殊情况:
Swait(s,d,d)。信号量集中只有一个信号量,允许每次申请d个资源,当资源数少于d时,不予分配。
Swait(s,1,1)。退化为一般信号量(s>1)或互斥信号量(s==1)。
Swait(s,1,0)。开关型信号量。

———————————————————————————————————————————————————

4.1.5 管程机制
信号量机制对于每个要访问临界资源的进程都必须自备同步操作wait(s)和signal(s),这使得大量的同步操作分散在各个进程中。这不仅给系统的管理带来麻烦,还会因为同步操作的使用不当而导致死锁。

把所有进程对某一种临界资源的同步操作都集中起来,构成一个所谓的“秘书”进程。
凡要访问该临界资源的进程,都需要先报告“秘书”,有秘书来实现诸进程的同步。
把并发进程间的同步操作分别集中于相应的管程中。

系统中的各种硬件资源和软件资源,均可用数据结构加以抽象描述,即用少量信息和对该资源所执行的操作来表征该资源,而忽略了它们的内部结构和实现细节。

当共享资源用共享数据结构表示时,资源管理程序可用对该数据结构进行操作的一组过程来表示,如资源的请求和释放过程request和release。把这样一组相关的数据结构和过程一并称为管程。

一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据。

管程分3部分:
1、局部于管程的共享变量说明;
2、对该数据结构进行操作的一组过程;
3、对局部于管程数据的数据设置初值的语句。

语法描述:
monitor monitor-name{
variable declarations
{ initialization code}
entry p1(...){...}
entry p2(...){...}
... ...
entry pn(...){...}
}

局部于管程的数据,仅能被局部于管程的过程所访问,局部于管程的过程也仅能访问管程内的数据结构。
管程相当于围墙,每次只允许一个进程进入管程,从而实现进程互斥。
在利用管程实现同步时,必须设置两个同步操作原语wait和signal。
当某进程通过管程请求临界资源而未能满足时,管程便调用wait原语使该进程等待,并将其排在等待队列上。
仅当另一进程访问完并释放之后,管程调用signal原语唤醒等待队列中的队首进程。

通常,等待的原因可能有多个,为了进行区别,引入条件变量condition。
管程中对每个条件变量都需予以说明,其形式为:“condition x,y;”,该变量应置于wait和signal之前,可表示为x.wait和x.signal。
x.signal操作的作用是重新启动一个被阻塞的进程,但如果没有进程被阻塞,则x.signal操作不产生任何后果,这与信号量机制中的signal操作不同。

——————————————————————————————————————————————————

4.1.6 经典进程同步问题

1、生产者-消费者问题(producer-consumer)
有一群生产者进程在生产产品,并将此产品提供给消费者进程去消费。
并发进行,设置一个n个缓冲区的缓冲池。
不允许消费者到空缓冲区去取产品,也不允许生产者进程向一个已有产品,尚未取走的缓冲区投放产品。

in指示下一个可投放产品的缓冲区,生产者生产一个产品后,输入指针加1,即in=(in+1)mod n;
out指示下一个可获取产品的缓冲区,消费者消费一个产品后,输出指针加1,即out=(out+1)mod n。
(in+1)mod n == out 时表示缓冲池满;
in == out 时表示缓冲池空。
还引入了一个整型变量counter,初始值为0。生产者投放产品后,counter+1;消费者消费产品后,counter-1.

(1)利用记录型信号量解决生产者-消费者问题
互斥信号量mutex实现诸进程对缓冲池的互斥使用
资源信号量empty表示空缓冲区的数量
资源信号量full表示满缓冲区的数量

semaphore mutex=1, empty=n, full=0
item buffer[n];
int in=out=0;
void producer()
{
    while(1)
    {
        produce an item in nextp; //nextp代表产品
        wait(empty);
        wait(mutex);
        buffer[in]=nextp;
        in=(in+1)mod n;
        signal(mutex);
        signal(full);
    }
}
void consumer()
{
    while(1)
    {
        wait(full);
        wait(mutex);
        nextc=buffer[out];
        out=(out+1)mod n;
        signal(mutex);
        signal(empty);
        consume the item in nextc;
    }
}
main()
{
    cobegin{
        producer();
        consumer();
    }
}

注意以下几点:
1、在每个程序中实现互斥的wait(mutex)和signal(mutex)应该成对出现;
2、对资源信号量empty和full的wait和signal操作,同样需要成对出现,但它们分别处于不同的进程;
3、在每个进程中的wait操作不能颠倒。应先执行对资源信号量的wait操作,然后再执行对互斥信号量的wait操作,否则引起死锁。

生产者放产品和消费者取产品无需互斥进行。
semaphore mutex1=1, mutex2=1, empty=n, full=0
item buffer[n];
int in=out=0;
void producer()
{
    while(1)
    {
        produce an item in nextp; //nextp代表产品
        wait(empty);
        wait(mutex1);
        buffer[in]=nextp;
        in=(in+1)mod n;
        signal(mutex1);
        signal(full);
    }
}
void consumer()
{
    while(1)
    {
        wait(full);
        wait(mutex2);
        nextc=buffer[out];
        out=(out+1)mod n;
        signal(mutex2);
        signal(empty);
        consume the item in nextc;
    }
}
main()
{
    cobegin{
        producer();
        consumer();
    }
}

(2)利用AND信号量解决生产者-消费者问题
semaphore mutex1=1, mutex2=1, empty=n, full=0
item buffer[n];
int in=out=0;
void producer()
{
    while(1)
    {
        produce an item in nextp; //nextp代表产品
        Swait(empty, mutex1);
        wait(mutex1);
        buffer[in]=nextp;
        in=(in+1)mod n;
        Ssignal(mutex1, full);
    }
}
void consumer()
{
    while(1)
    {
        Swait(full, mutex2);
        nextc=buffer[out];
        out=(out+1)mod n;
        Ssignal(mutex2,empty);
        consume the item in nextc;
    }
}
main()
{
    cobegin{
        producer();
        consumer();
    }
}


(3)利用管程解决生产者-消费者问题

monitor producer-consumer
{
    int in,out,count;
    item buffer[n];
    condition notfull, notempty;
    {in=out=0; count=0;}
    entry put(item)
    {
        if(count>=n)  notfull.wait;
        buffer[in]=nextp;
        in=(in+1)mod n;
        count++;
        notempty.signal;
    }

    entry put(item)
    {
        if(count<=0)  notempty.wait;
        nextc=buffer[out];
        out=(out+1)mode n;
        count--;
        notfull.signal;
    }

}


void producer()
{
    while(1)
    {
        produce an item in nextp;
        producer-consumer.put(item);
    }
}

void consumer()
{
    while(1)
    {
        producer-consumer.get(item);
        consume the item in nextc;
    }
}

main()
{
    cobegin{
        producer();
        consumer();
    }
}


2、读者写者问题
对于数据对象
允许多个读者进程同时访问;只允许一个写者进程互斥访问。
即要么有多个读者,要么只有一个写者。

对于读者写者不同的优先权,可以有两种变形:
第一类读者写者问题,读者优先——读者在读,后续来的读者可直接进入。可能导致写者饿死。
第二类读者写者问题,写者优先——写者欲读,所有后续读者均等待。可能导致读者饿死。

(1)利用记录型信号量解决第一类读者-写者问题
semaphore rmutex=mutex=1;
int readcount=0;
void reader(int i)
{
    while(1)
    {
        wait(rmutex)
        if(readcount==0)
            wait(mutex);
        readcount++;
        signal(rmutex);

        perform read operation;
        
        wait(rmutex);
        readcount--;
        if(readcount==0)
            signal(mutex);
        signal(rmutex);
    }
}

void writer(int j)
{
    while(1)
    {
        wait(mutex);
        perform write operation;
        signal(mutex);
    }
}

mian()
{
    cobegin{
        reader(1);
        ...
        reader(n);
        writer(1);
        ...
        writer(n);
}

(2)利用记录型信号量集机制解决第一类读者-写者问题
增加一条限制,最多允许RN个读者同时读。
#define RN
semaphore L=RN, mx=1;
void reader(int i)
{
        while(1)
        {
                Swait(L,1,1)
                Swait(mx,1,0); //起开关作用,只要无写者进入(mx==1),读者就可进入。
                perform read operation;
                signal(L,1);
        }
}
void writer(int j)
{
    while(1)
    {
        Swait(mx,1,1,L,RN,0; //仅当无写者进程(mx==1),又无读者进程(L=RN)时,写者才进入 
        perform write operation;
        Signal(mx,1);
    }
}

mian()
{
cobegin{
reader(1);
...
reader(n);
writer(1);
...
writer(n);
}

(3)利用管程方法解决第二类读者-写者问题
monnitor reader-writer{
int rc, wc; //记录读进程和写进程数
condition R,W;
{ rc=wc=0}
entry start_read
{
if wc>0 R.wait;
rc++;
R.signal;
}
entry end_read
{
rc--;
if rc=0    W.signal;
}
entry start_write
{
wc++;
if( rc>0 || wc>1) W.wait;
}
entry end_write
{
wc--;
if(wc>0)    W.signal;
else   R.signal;
}
}

Reader(int i)
{
    while(1)
    {
        reader_writer.start_read;
        reading;
        reader_writer.end_read;
    }
}

Writer(int j)
{
    while(1)
    {
        reader_writer.start_write;
        reading;
        reader_writer.end_write;
    }
}

mian()
{
cobegin{
reader(1);
...
reader(n);
writer(1);
...
writer(n);
}


3、哲学家进餐问题
5位哲学家,交替思考和进餐。
一张圆桌,桌上有5支筷子。
平时思考,饥饿时同时拿起左右两只筷子即可进餐。
进餐完毕,放下筷子继续思考。

semophore chopstick[5]={1,1,1,1,1};
wodi process(int i)
{
while(1)
{
wait(chopstick[i]);
wait(chopstick[(i+1)mod 5]);
eat;
signal(chopstick[i]);
signal(chopstick[(i+1)mod 5]);
think;
}
}
上述过程可能引起死锁。

几种解决方案:
1、最多允许4位哲学家同时进餐;
2、仅当两支筷子都可用时,才允许拿起筷子进餐;
3、规定奇数哲学家先拿左边筷子,偶数哲学家先拿右边筷子。最后总有一个哲学家能获得两支筷子而进餐。

利用AND信号量机制解决哲学家进餐问题
semaphore chopstick[5]={1,1,1,1,1}
void process(int i)
{
while(1)
{
Swait(chopstick[(i+1)mod 5], chopstick[i]);
eat;
Ssignal(chopstic[(i+1)mod 5], chopstick[i]);
think;
}
}
main()
{
cobegin{
process(0);
process(1);
process(2);
process(3);
process(4);
process(5);
}
}

—————————————————————————————————————————————————————————

4.2 进程通信
进程通信是指进程之间的信息交换。少则是一个状态或数值,多则是成千上万个字节。
进程的互斥和同步时低级通信。
高级通信是指用户可直接利用操作系统所提供的一组通信命令高效地传送大量数据。
在高级通信方式中,操作系统隐藏了进程通信的实现细节,或者说通信过程对用户是透明的。

4.2.1 进程通信的类型
三大类:共享存储器系统、消息传递系统、管道通信系统

1、共享存储器系统
相互通信的进程共享某些数据结构或共享存储区。
(1)基于共享数据结构的通信方式。程序员负责公用数据结构设置和同步处理,低效,适合少量数据。
(2)基于共享存储区的通信方式。高级通信,进程在通信前,向系统申请共享存储区中的一个分区,并指定该分区的关键字;若系统已经给其它进程分配了这样的分区,则将该分区的描述符返回给申请者。接着申请者把获得的共享存储分区连接到本进程上。此后,便可像读、写普通存储器一样地读写公用存储区。

2、消息传递系统
进程间的数据交换以消息为单位。
直接利用系统提供的通信命令(原语)来实现通信。
操作系统隐藏了通信的实现细节,大大简化了通信程序编制的复杂性,因而应用广泛。
高级通信方式
(1)直接通信方式:
发送进程直接将消息发送给接受进程并将它挂在接受进程的消息缓冲队列上,接受进程从消息缓冲队列中取得消息。
(2)间接通信方式:
发送进程将消息发送到某种中间实体中,接受进程从中取得消息。这种中间实体称为信箱。这种通信方式称为信箱通信方式。

3、管道通信
管道是用于连接一个读进程和一个写进程以实现它们之间通信的共享文件,又称为pipe文件。
写进程以字符流的形式将大量的数据写入管道,读进程从管道读取信息。
管道通信机制必须提供以下3方面的协调能力。
互斥、同步、判断对方是否存在。

4.2.2 直接通信和间接通信
1、直接通信方式
直接通信方式是指发送进程利用操作系统所提供的发送命令直接把消息发送给目标进程。
此时,要求发送进程和接收进程都以显示的方式提供对方的标识符。
通常系统提供下述两条通信原语:
send(receiver, message);
receive(sender, message);

利用直接通信原语来解决生产者-消费者问题:
void producer()
{
while(1)
{
produce an item in nextp;
send(consumer, nextp);
}
}
void consumer()
{
    while(1)
    {
        receive(producer,nextc);
        consume the item in nextc;
    }
}
main(){
    producer();
    consumer();
}

2、间接通信方式
进程之间的通信需要通过作为某种共享数据结构的实体,该实体用来暂存发送进程发给目标进程的消息;接收进程从该实体中取出对方发送给自己的消息。通常把这种中间实体称为信箱。
在逻辑上,信箱由信箱头和包括若干信格的信箱体所组成,每个信箱必须有自己的唯一标识符。
利用信箱进行通信,用户可以不必写出接受进程标识符就可以向不知名的进程发送消息,且信息可以安全的保存在信箱中,允许目标用户随时读取。
这种通信方式被广泛地用于多机系统和计算机网络中。
系统为信箱通信提供了若干条原语,用于信箱的创建、撤销和消息的发送、接受。

信箱可由操作系统创建,也可由用户进程创建。
分三类:私有信箱、公用信箱、共享信箱
发送进程和接收进程的四种关系:
一对一
多对一:客户/服务器交互
一对多:广播
多对多


4.2.3 消息缓冲队列通信机制

send原语
receive原语

消息缓冲区
struct message_buffer{
    sender;  //发送者进程标识符
    size;  //消息长度
    text;  //消息正文
    next;  //指向下一个缓冲区的指针
};

PCB中有关通信的数据项
struct processcontrol_block{
    mq; //消息队列首指针
    mutex;  //消息队列互斥信号量
    sm;  //消息队列资源信号量
}

发送原语:
发送进程在利用发送原语发送消息之前应首先在自己的内存空间设置一发送区a,把待发送的消息正文、发送进程标识符、消息长度等信息传入其中,然后调用发送原语,把消息发送给接收进程。
发送原语首先根据发送区a中所设置的消息长度a.size来申请一个缓冲区i,接着把发送区a的信息复制到消息缓冲区i中。
为了能将i挂在接收进程的消息队列mq,上,应先获得接收进程的内部标识符j,然后将i挂在j.mq上。
由于该队列属于临界资源,故在执行insert操作的前后要分别执行wait和signal操作。
void send(receiver, a)
{
    getbuf(a.size, i); //根据a.size申请缓冲区
    i.sender=a.sender;  //将a中的信息复制到缓冲区i中
    i.size=a.size;
    i.text=a.text;
    i.next=0;
    getid(PCB set, receiver, j); //获得接收进程内部标识符j
    wait(j.mutex);
    insert(j.mq,i);  //将消息缓冲区插入消息队列
    singal(j.mutex);
    signal(j.sm);
}


接收原语:
接收原语进程调用接收原语receive(b)从自己的消息缓冲队列mq中摘下第一个消息缓冲区i,并将其中的数据复制到以b为首地址的指定消息接收区内。
void receive(b)
{
    j=inernal.name; //j为接收进程的内部标识符
    wait(j.sm);
    wait(j.mmutex);
    remove(j.mq, i);  //将消息队列中第一个消息移出
    signal(j.mutex);
    b.sender=i.sender;  //把消息缓冲区i中的信息复制到接收区b
    b.size=i.size;
    b.next=i.next;
}

————————————————————————————————————————————————————————

Massage Passing

·Enforce mutual exclusion

·Exchange information

send(destination, message)

receive(source, message)

 

 

Synchronization

·Sender and receiver may or may not be blocked( waiting for message)

·Blocking send, blocking receive

---Both sender and receiver are blocked until message is delivered.

---Called a rendezvous(紧密同步,汇合)

·Non-blocking send, blocking receive

---Sender continues processing such as sending messages as quickly as possible.

---Receive is blocked until the requested message arrives.

·Non-blocking send, non-blocking receive

---Neither part is required to wait.

 

Addressing 寻址

·Direct addressing

---Send primitive includes a specific identifier of the destination process.

---Receive primitive could know ahead of time which process a message is expected.

---Receive primitive could use source parameter to return a value when the receive operation has been performed.

 

·Indirect addressing

---Messages are sent to a shared data structure consisting of queues.

---Queues are called mailboxes.

---One process sends a message to the mailbox and the other process picks up the message from the mailbox.





Message Format

Header                         Body >Message Contents

>Message Type    

>Destination ID

>Source ID

>Message Length

>Control Information


Mutual Exclusion

·若采用Non-blocking send, blocking receive

·多个进程共享mailbox mutex

若进程申请进入临界区,首先申请从mutex邮箱中接受一条消息。

若邮箱空,则进程阻塞;若进程收到邮箱中的消息,则进入临界区,执行完毕退出,并将该消息放回邮箱mutex

该消息as a token(令牌)在进程间传递。



Massage Passing: Producer/Consumer Problem

·解决有限buffer  Problem/Consumer Problem

·设两个邮箱:

---May_consumeProducer存放数据,供Consumer取走(即buffer数据区)

---May_produce:存放空消息的buffer空间







 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/387225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle迁移到greenplum的方案

oracle数据库是一种关系型数据库管理系统&#xff0c;在数据库领域一直处于领先的地位&#xff0c;适合于大型项目的开发&#xff1b;银行、电信、电商、金融等各领域都大量使用Oracle数据库。 greenplum是一款开源的分布式数据库存储解决方案&#xff0c;主要关注数据仓库和BI…

CNN框架的搭建及各个参数的调节

本文代码下载地址&#xff1a;我的github本文主要讲解将CNN应用于人脸识别的流程&#xff0c;程序基于PythonnumpytheanoPIL开发&#xff0c;采用类似LeNet5的CNN模型&#xff0c;应用于olivettifaces人脸数据库&#xff0c;实现人脸识别的功能&#xff0c;模型的误差降到了5%以…

操作系统05死锁

进程管理4--Deadlock and Starvation Concurrency: Deadlock and Starvation 内容提要 >产生死锁与饥饿的原因 >解决死锁的方法 >死锁/同步的经典问题&#xff1a;哲学家进餐问题 Deadlock 系统的一种随机性错误 Permanent blocking of a set of processes that eith…

CNN tensorflow 人脸识别

数据材料这是一个小型的人脸数据库&#xff0c;一共有40个人&#xff0c;每个人有10张照片作为样本数据。这些图片都是黑白照片&#xff0c;意味着这些图片都只有灰度0-255&#xff0c;没有rgb三通道。于是我们需要对这张大图片切分成一个个的小脸。整张图片大小是1190 942&am…

数据结构01绪论

第一章绪论 1.1 什么是数据结构 数据结构是一门研究非数值计算的程序设计问题中&#xff0c;计算机的操作对象以及他们之间的关系和操作的学科。 面向过程程序数据结构算法 数据结构是介于数学、计算机硬件、计算机软件三者之间的一门核心课程。 数据结构是程序设计、编译…

css3动画、2D与3D效果

1.兼容性 css3针对同一样式在不同浏览器的兼容 需要在样式属性前加上内核前缀&#xff1b; 谷歌&#xff08;chrome&#xff09; -webkit-transition: Opera&#xff08;欧鹏&#xff09; -o-transition: Firefox&#xff08;火狐&#xff09; -moz-transition Ie -ms-tr…

数据结构02线性表

第二章 线性表 C中STL顺序表&#xff1a;vector http://blog.csdn.net/weixin_37289816/article/details/54710677链表&#xff1a;list http://blog.csdn.net/weixin_37289816/article/details/54773406在数据元素的非空有限集中&#xff1a; (1)存在唯一一个被称作“第…

训练一个神经网络 能让她认得我

写个神经网络&#xff0c;让她认得我(๑•ᴗ•๑)(Tensorflow,opencv,dlib,cnn,人脸识别) 这段时间正在学习tensorflow的卷积神经网络部分&#xff0c;为了对卷积神经网络能够有一个更深的了解&#xff0c;自己动手实现一个例程是比较好的方式&#xff0c;所以就选了一个这样比…

数据结构03栈和队列

第三章栈和队列 STL栈&#xff1a;stack http://blog.csdn.net/weixin_37289816/article/details/54773495队列&#xff1a;queue http://blog.csdn.net/weixin_37289816/article/details/54773581priority_queue http://blog.csdn.net/weixin_37289816/article/details/5477…

树莓派pwm驱动好盈电调及伺服电机

本文讲述如何通过树莓派的硬件PWM控制好盈电调来驱动RC车子的前进后退&#xff0c;以及如何驱动伺服电机来控制车子转向。 1. 好盈电调简介 车子上的电调型号为&#xff1a;WP-10BLS-A-RTR&#xff0c;在好盈官网并没有搜到对应手册&#xff0c;但找到一份通用RC竞速车的电调使…

数据结构04串

第四章 串 STL&#xff1a;string http://blog.csdn.net/weixin_37289816/article/details/54716009计算机上非数值处理的对象基本上是字符串数据。 在不同类型的应用中&#xff0c;字符串具有不同的特点&#xff0c;要有效的实现字符串的处理&#xff0c;必须选用合适的存储…

CAS单点登录原理解析

CAS单点登录原理解析 SSO英文全称Single Sign On&#xff0c;单点登录。SSO是在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的应用系统。CAS是一种基于http协议的B/S应用系统单点登录实现方案&#xff0c;认识CAS之前首先要熟悉http协议、Session与Co…

数据结构05数组和广义表

第五章 数组 和 广义表 数组和广义表可以看成是线性表在下述含义上的扩展&#xff1a;表中的数据元素本身也是一个数据结构。 5.1 数组的定义 n维数组中每个元素都受着n个关系的约束&#xff0c;每个元素都有一个直接后继元素。 可以把二维数组看成是这样一个定长线性表&…

数据结构06树和二叉树

第六章 树和二叉树 6.1 树的定义和基本术语 树 Tree 是n个结点的有限集。 任意一棵非空树中&#xff1a; &#xff08;1&#xff09;有且仅有一个特定的称为根&#xff08;root&#xff09;的结点&#xff1b; &#xff08;2&#xff09;当n>1时&#xff0c;其余结点可…

CountDownLatch,CyclicBarrier和Semaphore

在java 1.5中&#xff0c;提供了一些非常有用的辅助类来帮助我们进行并发编程&#xff0c;比如CountDownLatch&#xff0c;CyclicBarrier和Semaphore&#xff0c;今天我们就来学习一下这三个辅助类的用法。以下是本文目录大纲&#xff1a;一.CountDownLatch用法二.CyclicBarrie…

数据结构07排序

第十章内部排序 10.1 概述 排序就是把一组数据按关键字的大小有规律地排列。经过排序的数据更易于查找。 排序前KiKj&#xff0c;且Ki在前: 排序方法是稳定的&#xff0c;若排序后Ki在前&#xff1b; 排序方法是不稳定的&#xff0c;如排序后Kj在前。 分类&#xff1a; 内…

数据结构08查找

第九章 查找 另一种在实际应用中大量使用的数据结构--查找表。 所谓查找&#xff0c;即为在一个含有众多的数据元素的查找表中找出某个“特定的”数据元素。 查找表 search table 是由同一类型的数据元素构成的集合。集合中的数据元素之间存在着完全松散的关系&#xff0c;故…

下载Centos7 64位镜像

下载Centos7 64位镜像 1.打开Centos官网 打开Centos官方网站地址&#xff1a;https://www.centos.org/&#xff0c;点击Get CentOS Now 2.点击Minimal ISO镜像 Minimal ISO镜像&#xff0c;与DVD ISO镜像的差别有很多&#xff0c;这里只说两点 1.Minimal ISO类似于Windows的纯净…

Scala01入门

第1章 可伸展的语言 Scala应用范围广&#xff0c;从编写脚本&#xff0c;到建立大型系统。 运行在标准Java平台上&#xff0c;与Java库无缝交互。 更能发挥力量的地方&#xff1a;建立大型系统或可重用控件的架构。 将面向对象和函数式编程加入到静态类型语言。 在Scala中&a…

Java网络01基本网络概念

协议 Protocol&#xff1a;明确规则 &#xff08;1&#xff09;地址格式&#xff1b; &#xff08;2&#xff09;数据如何分包&#xff1b; ... TCP/IP四层模型&#xff1a; 应用层 HTTP SMTP POP IMAP 传输层 TCP UDP 网际层 IP 主机网络层 host to host layer 数模、…