Linux进程间通讯—旗语

本文详细阐述了生产者消费者模型的实现原理及关键步骤,通过使用共享内存和信号量来解决仓库的唯一性访问、满载限制和空载状态问题。包括创建共享内存、初始化信号量、主循环中的生产者和消费者操作,以及如何处理信号中断以优雅地退出程序。
/*
 *	生产者/消费者模型
 *	给定仓库容积(N)
 *	生成者生成的产品入库
 *	消费者从仓库中取出产品消费
 *	仓库满了时生产者不能继续生成
 *	仓库为空时消费者不能继续消费
 *	对仓库的访问是独占的
 *
 *	semBin	控制独占访问
 *	semMax	控制仓库满
 *	semMin	控制仓库空
 *
 *	生产者			消费者
 *	P(semMax)		P(semMin)
 *	P(semBin)		P(semBin)
 *	(*product)++	(*product)--
 *			输出产品信息
 *	V(semBin)		V(semBin)
 *	V(semMin)		V(semMax)
 *
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <assert.h>
#include <signal.h>

#define	PC_SHM_NAME			"/pcshm"
#define	PC_SEM_NAME_BIN		"/pcsembin"
#define	PC_SEM_NAME_MAX		"/pcsemmax"
#define	PC_SEM_NAME_MIN		"/pcsemmin"

#define SHM_LENGTH	512		//共享内存长度

#define	PRODUCT_MAX		30	//仓库容积

#ifndef FALSE
#define FALSE	0
#endif
#ifndef TRUE
#define	TRUE	1
#endif

int	exitFlag = 0;	//退出标志

/*
 * ctrl+c signal
 */
void ctrlcHandle(int signum)
{
	exitFlag = 1;
}

int main(void)
{
	int * product;	//产品数,指向共享内存
	int shmFd;		//共享内存句柄
	sem_t * semBin;	//二进制旗语,控制仓库唯一性访问
	sem_t * semMax;	//计数旗语,控制产品上限
	sem_t * semMin;	//计数旗语,控制产品下限

	_Bool isFirst = FALSE;	//首次运行?
	
	signal(SIGINT, ctrlcHandle);
	
	//准备共享内存
	shmFd = shm_open(PC_SHM_NAME,
					O_RDWR | O_CREAT | O_EXCL,
					0666);
	if( shmFd >= 0 )
	{//第一次创建
		isFirst = TRUE;
		ftruncate(shmFd, SHM_LENGTH);
	}else
	{//重新打开
		shmFd = shm_open(PC_SHM_NAME, O_RDWR, 0666);
		if(shmFd < 0)
		{
			perror("shm_open");
			exit(EXIT_FAILURE);
		}
	}
	
	//内存映射
	product = (int *)mmap(NULL,
						  SHM_LENGTH,
						  PROT_READ|PROT_WRITE,
						  MAP_SHARED,
						  shmFd,
						  0);
	if(product == MAP_FAILED)
	{
		perror("mmap");
		shm_unlink(PC_SHM_NAME);
		exit(EXIT_FAILURE);
	}
	if(isFirst)
	{//首次创建共享内存,初始化
		*product = 0;
	}
	
	//打开/创建旗语
	if(isFirst)
	{
		semBin = sem_open(PC_SEM_NAME_BIN,
					O_CREAT, 0666, 1);
		semMax = sem_open(PC_SEM_NAME_MAX,
					O_CREAT, 0666, PRODUCT_MAX);
		semMin = sem_open(PC_SEM_NAME_MIN,
					O_CREAT, 0666, 0);
	}else
	{
		semBin = sem_open(PC_SEM_NAME_BIN, 0);
		semMax = sem_open(PC_SEM_NAME_MAX, 0);
		semMin = sem_open(PC_SEM_NAME_MIN, 0);
	}
	if( (semBin == SEM_FAILED) ||
		(semMax == SEM_FAILED) ||
		(semMin == SEM_FAILED) )
	{
		perror("sem_open");
		munmap(product, SHM_LENGTH);
		shm_unlink(PC_SHM_NAME);
		exit(EXIT_FAILURE);
	}
	
	//主循环
	while(!exitFlag)
	{
#ifdef PRODUCE	//生产者
		//P(MAX)
		if( sem_wait(semMax) != 0 )
			continue;
		//P(BIN)
		if( sem_wait(semBin) != 0 )
		{
			sem_post(semMax);
			continue;
		}
		//生产
		(*product)++;
		printf("produce:%d\n", *product);
		//V(BIN)
		assert( sem_post(semBin) == 0 );
		//V(MIN)
		assert( sem_post(semMin) == 0 );		
#else	//消费者
		//P(MIN)
		if( sem_wait(semMin) != 0 )
			continue;
		//P(BIN)
		if( sem_wait(semBin) != 0 )
		{
			sem_post(semMin);
			continue;
		}
		//消费
		(*product)--;
		printf("consume:%d\n", *product);
		//V(BIN)
		assert( sem_post(semBin) == 0 );
		//V(MAX)
		assert( sem_post(semMax) == 0 );
#endif
		usleep(100000);
	}
	
	munmap(product, SHM_LENGTH);
	shm_unlink(PC_SHM_NAME);
	sem_close(semBin);
	sem_close(semMin);
	sem_close(semMax);
	sem_unlink(PC_SEM_NAME_BIN);
	sem_unlink(PC_SEM_NAME_MIN);
	sem_unlink(PC_SEM_NAME_MAX);
	
	return 0;
}


### 线程控制机制 SystemVerilog中的线程是仿真过程中并发执行的基本单元,用于描述硬件或验证平台的并发行为。线程可以通过`fork`和`join`语句来创建和管理。`fork`语句用于启动一个新的线程,而`join`语句用于等待该线程完成。此外,SystemVerilog还提供了`disable`语句来提前终止一个线程。这些机制使得开发者能够灵活地控制线程的生命周期和执行顺序。 ### 线程间通信(IPC) 在复杂的数字系统验证中,多线程协同工作是实现高效验证的关键。SystemVerilog提供了多种线程间通信(Inter-Process Communication, IPC)方法,能够有效解决并发执行中的同步与数据交换问题。 #### 事件(Event) 事件是SystemVerilog中最基本的同步机制之一。它允许一个线程等待某个条件的发生,而另一个线程在条件满足时触发该事件。事件的使用通常涉及`event`关键字和`->`操作符来触发事件,以及`@`操作符来等待事件的发生。 ```systemverilog event my_event; initial begin fork begin #10ns -> my_event; // 触发事件 end begin @my_event; // 等待事件 $display("Event triggered"); end join end ``` #### 旗语(Semaphore) 旗语是一种用于控制对共享资源访问的同步机制。SystemVerilog中的旗语通过`semaphore`类来实现,提供了`get`和`put`方法来获取和释放资源。旗语可以限制同时访问的线程数量,从而避免资源竞争问题。 ```systemverilog semaphore sem = new(1); // 初始化一个旗语,初始值为1 initial begin fork begin sem.get(1); // 获取一个旗语钥匙 // 执行临界区代码 sem.put(1); // 释放旗语钥匙 end begin sem.get(1); // 获取一个旗语钥匙 // 执行临界区代码 sem.put(1); // 释放旗语钥匙 end join end ``` #### 信箱(Mailbox) 信箱是一种用于线程间传递数据的通信机制。SystemVerilog中的信箱通过`mailbox`类来实现,提供了`put`和`get`方法来发送和接收数据。信箱在创建时可以指定容量,如果信箱已满,后续的`put`操作将会被阻塞,直到信箱中有空间可用。 ```systemverilog mailbox mb = new(); // 创建一个不限制容量的信箱 initial begin fork begin mb.put(10); // 向信箱中放入数据 end begin int data; mb.get(data); // 从信箱中取出数据 $display("Received data: %0d", data); end join end ``` ### 总结 SystemVerilog提供了丰富的线程控制和线程间通信机制,包括事件、旗语和信箱。这些机制能够帮助开发者有效地管理和协调并发线程,确保系统的正确性和高效性。通过合理使用这些工具,可以在复杂的数字系统验证中实现高效的并发控制和数据交换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值