【linux】信号量与PV操作 (进程和线程的同步) 您所在的位置:网站首页 linux实现生产者与消费者报告的区别 【linux】信号量与PV操作 (进程和线程的同步)

【linux】信号量与PV操作 (进程和线程的同步)

2024-07-18 00:09| 来源: 网络整理| 查看: 265

目录

1、基本含义

使用PV操作实现进程互斥时应该注意的是:

2、例子:生产者/消费者模型

3.代码实现信号量

信号量分类:进程间信号量、线程间信号量

在计算机操作系统中,PV操作是进程管理中的难点。

1、基本含义

     什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。      PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:

    P(S):①将信号量S的值减1,即S=S-1;                                                                         ②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。     V(S):①将信号量S的值加1,即S=S+1;                                                                       ②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。  利用信号量和PV操作实现进程互斥的一般模型是:

    进程P1              进程P2           ……          进程Pn     ……                  ……                           ……     P(S);              P(S);                         P(S);     临界区;             临界区;                        临界区;     V(S);              V(S);                        V(S);     ……                  ……            ……           ……

使用PV操作实现进程互斥时应该注意的是:

    (1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查其成对性。     (2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。     (3)互斥信号量的初值一般为1。

利用信号量和PV操作实现进程同步:

    PV操作是典型的同步机制之一。用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生;当信号量的值非0时,表示期望的消息已经存在。用PV操作实现进程同步时,调用P操作测试消息是否到达,调用V操作发送消息。     使用PV操作实现进程同步时应该注意的是:

    (1)分析进程间的制约关系,确定信号量种类。在保持进程间有正确的同步关系情况下,哪个进程先执行,哪些进程后执行,彼此间通过什么资源(信号量)进行协调,从而明确要设置哪些信号量。     (2)信号量的初值与相应资源的数量有关,也与P、V操作在程序代码中出现的位置有关。     (3)同一信号量的P、V操作要成对出现,但它们分别在不同的进程代码中。

2、例子:生产者/消费者模型

(1)一个生产者,一个消费者,公用一个缓冲区。  定义两个同步信号量:          S_empty——表示缓冲区空位数量,初值为1。          S_ full    ——表示缓冲区占位数量,初值为0。

伪代码:

生产者进程 while(TRUE){ 生产一个产品;      P(S_empty); //buff有空的吗?      产品送往Buffer;      V(S_full); //buff已满 } 消费者进程 while(True){ P(S_full); //buff有数据?    从Buffer取出一个产品;    V(S_empty); //buff有空    消费该产品; }

(2)一个生产者,一个消费者,公用n个(单元的)环形缓冲区。  定义两个同步信号量:

     S_empty——表示缓冲区空位数量,初值为n。     S_full      ——表示缓冲区占位数量,初值为0。

    设缓冲区的编号为1~n-1,定义两个指针in和out,分别是生产者进程和消费者进程使用的指,指向下一个可用的缓冲区。

伪代码:

生产者进程 while(TRUE){      生产一个产品;      P(S_empty);      产品送往buffer(in);      in=(in+1)mod n;      V(S_full); }   消费者进程 while(TRUE){  P(S_full);    从buffer(out)中取出产品;    out=(out+1)mod n;    V(S_empty);    消费该产品;    }

(3)一组生产者,一组消费者,公用n个(单元的)环形缓冲区     在这个问题中,不仅生产者与消费者之间要同步,而且各个生产者之间、各个消费者之间还必须互斥地访问缓冲区。 定义四个信号量:         empty——表示缓冲区空位数量,初值为n。         full——表示缓冲区占位数量,初值为0。         mutex1——生产者之间的互斥信号量,初值为1。         mutex2——消费者之间的互斥信号量,初值为1。

    设缓冲区的编号为1~n-1,定义两个指针in和out,分别是生产者进程和消费者进程使用的指针,指向下一个可用的缓冲区。

伪代码:

生产者进程 while(TRUE){      生产一个产品;      P(empty);      P(mutex1);      产品送往buffer(in);      in=(in+1)mod n;      V(mutex1);      V(full); } 消费者进程 while(TRUE){  P(full)    P(mutex2);    从buffer(out)中取出产品;    out=(out+1)mod n;    V(mutex2);    V(empty);    消费该产品;    }

  需要注意的是无论在生产者进程中还是在消费者进程中,两个P操作的次序不能颠倒。应先执行同步信号量的P操作,然后再执行互斥信号量的P操作,否则可能造成进程死锁。

原文链接:https://blog.csdn.net/lixiangsheng2012/article/details/83421359

代码示例:

当有进程要求使用共享资源时,需要执行以下操作:

1.系统首先要检测该资源的信号量;

2.若该资源的信号量值大于0,则进程可以使用该资源,此时,进程将该资源的信号量值减1;

3.若该资源的信号量值为0,则进程进入休眠状态,直到信号量值大于0时进程被唤醒,访问该资源;

       当进程不再使用由一个信号量控制的共享资源时,该信号量值增加1,如果此时有进程处于休眠状态等待此信号量,则该进程会被唤醒。

2.信号量的具体结构 每个信号量集都有一个与其相对应的结构,该结构定义如下:  

/* Data structure describing a set of semaphores. */ struct semid_ds { struct ipc_perm sem_perm; /* operation permission struct */ struct sem *sem_base; /* ptr to array of semaphores in set */ unsigned short sem_nsems; /* # of semaphores in set */ time_t sem_otime; /* last-semop() time */ time_t sem_ctime; /* last-change time */ }; /* Data structure describing each of semaphores. */ struct sem { unsigned short semval; /* semaphore value, always >= 0 */ pid_t sempid; /* pid for last successful semop(), SETVAL, SETALL */ unsigned short semncnt; /* # processes awaiting semval > curval */ unsigned short semzcnt; /* # processes awaiting semval == 0 */ };

信号量集的结构图如下所示:

3.代码实现信号量

使用信号量实现生产者消费者模式

#include "stdafx.h" #include #include #include #include #include #include #include #include #include using namespace std; #pragma comment(lib,"pthreadVC2.lib") #define N 5 //消费者或者生产者的数目 #define M 10 //缓冲数目 int productin = 0; //生产者放置产品的位置 int prochaseout = 0; //消费者取产品的位置 int buff[M] = {0}; //缓冲区初始化为0,开始时没有产品。 sem_t empty_sem; // 同步信号量,当满的时候阻止生产者放产品。 sem_t full_sem; //同步信号量,当没有产品的时候阻止消费者消费。 pthread_mutex_t mutex; //互斥信号量,一次只有一个线程访问缓冲区。 int product_id = 0; //生产者id int prochase_id = 0; //消费者id void SignalExit(int signo) { printf("程序退出%d\n",signo); return; } void PrintProduction() { printf("此时的产品队列为::"); for(int i = 0; i < M; i++ ) { printf("%d ",buff[i]); } printf("\n\n"); } //生产者方法 void* Product(void* pramter) { int id = ++product_id; while(1) { Sleep(5000); //毫秒 sem_wait(&empty_sem); //给信号量减1操作 pthread_mutex_lock(&mutex); productin = productin % M; printf("生产者%d在产品队列中放入第%d个产品\n\n",id,productin+1); buff[productin] = 1; PrintProduction(); ++productin; pthread_mutex_unlock(&mutex); //释放互斥量对象 sem_post(&full_sem); //给信号量的值加1操作 } } //消费者方法/// void* Prochase( void* pramter ) { int id = ++prochase_id; while(1) { Sleep(7000); sem_wait(&full_sem); pthread_mutex_lock(&mutex); prochaseout = prochaseout % M; printf("消费者%d从产品队列中取出第%d个产品\n\n",id,prochaseout+1); buff[prochaseout] = 0; PrintProduction(); ++prochaseout; pthread_mutex_unlock(&mutex); sem_post(&empty_sem); } } int main() { cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有