1.实验目的

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

 

 

2.实验软硬件环境

  • 安装Windows XP的计算机
  • VirtualBox软件,以及在其上安装的Ubuntu虚拟机

3.实验内容

    生产者进程生产产品,消费者进程消费产品。当生产者进程生产产品时,如果没有空缓冲区(仓库)可用,那么生产进程必须等待消费者进程释放出一个缓冲区,当消费者进程消费产品时,如果缓冲区产品,那么消费者进程将被阻塞,直到新的产品被生产出来。模拟一个生产者两个消费者的情况。

 

   

使用函数:

 

1 semget函数

它的作用是创建一个新信号量或取得一个已有信号量,原型为:

int semget(key_t  key, int num_sems, int sem_flags); 

semget函数成功返回一个相应信号标识符(非零),失败返回-1.

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

2  semop函数

它的作用是改变信号量的值,原型为:

int semop(int sem_id, struct sembuf *sem_opa, size_tum_sem_ops);  

sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:

struct sembuf{  

    short sem_num;//除非使用一组信号量,否则它为0  

    short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,P(等待)操作,一个是+1,即V(发送信号)操作。  

      short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量  

};  

3  semctl函数

该函数用来直接控制信号量信息,它的原型为:

int semctl(int sem_id, int sem_num, int command, ...);  

如果有第四个参数,它通常是一个union semum结构,定义如下:

union semun{  

    int val;  

    struct semid_ds *buf;  

    unsigned short *arry;  

};  

前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个

SETVAL:用来把信号量初始化为一个已知的值。这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。

IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

 

4.实验程序及分析

实验程序:

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/sem.h>
#define KEY (key_t)14010322
union semun{  
    int val;  
    struct semid_ds *buf;  
    unsigned short *arry;  
};


static int sem_id = 0;

static int set_semvalue();
static void del_semvalue();
static void semaphore_p();
static void semaphore_v();

int main(int argc, char *argv[])
{
	//创建信号量,利用semget函数
	//初始化信号量,利用semctl函数
	int semid;
	int product = 1;
	int i;
	if((semid = semget(KEY,3,IPC_CREAT|0660))==-1)
	{
		printf("ERROR\n");
		return -1;
	}
	union semun arg[3];
	arg[0].val = 1;//mutex = 1;
	arg[1].val = 5;//empty = 5;
	arg[2].val = 0;//full = 0;

	for(i=0;i<3;i++)
	{
		semctl(semid,i,SETVAL,arg[i]);
	}

	for(i=0;i<3;i++)
	{
		printf("The semval(%d) = %d\n",i,semctl(semid,i,GETVAL,NULL));
	}

					pid_t p1,p2;
		      if((p1=fork())==0){
		         while(1){
							//生产者……………..
							semaphore_p(semid,1);//P(empty)
							printf("1\n");
							semaphore_p(semid,0);//P(mutex)
							printf("2\n");
							product++;
							printf("Producer %d: %d things",getpid(),product);
							semaphore_v(semid,0);//V(mutex);
							semaphore_v(semid,2);//V(full);
						
		         sleep(2);
		         }
		      }else{
		        if((p2=fork())==0){
		          while(1){
		             sleep(2);
							semaphore_p(semid,2);//p(full)
							printf("3\n");
							semaphore_p(semid,0);//p(mutex)
							printf("4\n");
							product--;
							printf("Consumer1 %d: %d things",getpid(),product);
							semaphore_v(semid,0);//v(mutex)
							semaphore_v(semid,1);//v(empty)
		             sleep(5);
		          }
		        }
						else{
		          while(1){
		             sleep(2);
							semaphore_p(semid,2);//p(full)
							printf("5\n");
							semaphore_p(semid,0);//p(mutex)
							printf("6\n");
							product--;
							printf("Consumer2 %d: %d things",getpid(),product);
							semaphore_v(semid,0);//v(mutex)
							semaphore_v(semid,1);//v(empty)
		             sleep(5);
		          }
		        }
		      }
}


static void del_semvalue()
{
	//删除信号量
	union semun sem_union;

	if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
		fprintf(stderr, "Failed to delete semaphore\n");
}

static int set_semvalue()
{
	//用于初始化信号量,在使用信号量前必须这样做
	union semun sem_union;
	sem_union.val = 1;
	if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
		return 0;
	return 1;
}

void semaphore_p(int sem_id,int semNum)
{
	//对信号量做减1操作,即等待P(sv)
	struct sembuf sem_b;
	sem_b.sem_num = semNum;
	sem_b.sem_op = -1;//P()
	sem_b.sem_flg = SEM_UNDO;
//	semop(semid,&sem_b,1);
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_p failed\n");
		return;
	}
	return;
}

void semaphore_v(int sem_id,int semNum)
{
	//这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
	struct sembuf sem_b;
	sem_b.sem_num = semNum;
	sem_b.sem_op = 1;//V()
	sem_b.sem_flg = SEM_UNDO;
//	semop(semid,&sem_b,1);
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_p failed\n");
		return;
	}
	return ;
}

 

分析:

 

定义3个信号量,full,empty,mutex,分别表示产品个数,缓冲区空位个数,对缓冲区进行操作的互斥信号量,对应的初始化值分别为0,5,1。

生产者:

P(empty)---->P(mutex)----->V(mutex)----->V(full)

消费者:

P(full)----->P(mutex)------->V(mutex)------->V(empty)

由此实现了缓冲区为5,一个生产者和两个消费者的同步。

 

 

5.实验截图

 

 

 

 

6.实验心得体会

    此次实验利用信号量实现了进程同步。其中用 semop函数具体实现了PV操作函数,并实现了一个生产者和两个消费者之间的同步。

 

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐