作业必备:操作系统实验六【生产者消费者实验:用信号量实现PV操作】

🌸🌸🌸前期回顾:🌸🌸🌸

实验五:管道通信

实验四:软中断通信

实验三:进程控制

实验二:进程创建

实验一:熟悉Ubuntu系统(梦开始的地方~)


目录🌝🌝🌝

前言:🍀🍀

【实验资料】 🍀🍀🍀

一、什么是信号量呢🤓🤓🤓?

二、 相关系统调用👀👀

🌻🌻1、ftok函数:生成关键字。

2、以下信号量相关的操作函数有相同的头文件:

🌻🌻(1)semget函数:建立新的信号量对象或获取已有对象的标识符。

🌻🌻(3) semop函数:改变信号量对象中各个信号量的状态。

🌻🌻(4) semctl函数:直接对信号量对象进行控制。

正文:🍀🍀

【实验目的】🍀🍀🍀

【实验内容】🍀🍀🍀

🌹🌹一、使用信号量机制来模拟实现PV操作,控制多进程对共享资源的互斥访问。

🌹🌹🌹 二、编程解决苹果桔子问题

【实验心得】:🍀🍀


        are you ready? go!

前言:🍀🍀

🌻🌻🌻按照惯例,在实验前,我们需要先了解一些基本知识~

【实验资料】 🍀🍀🍀

一、什么是信号量呢🤓🤓🤓?

信号量是一个计数器,用来控制多个进程对共享资源的使用。进程为了获得共享资源,需要执行下列操作:

(1) 测试控制该资源的信号量。

(2) 若此信号量的值为正,则进程可以使用该资源。进程将信号量值减1,表示它使用了一个资源单位。

(3) 若此信号量的值为0,则进程进入睡眠状态,直至信号量值大于0。若进程被唤醒后,它返回至(第(1)步)。

信号量通常是在内核中实现的。常用的信号量形式被称之为双态信号量。它控制单个资源,其初始值为1。但是,一般而言,信号量的初值可以是任一正值该值说明有多少个共享资源单位可供共享应用。

二、 相关系统调用👀👀

🌻🌻1、ftok函数:生成关键字。

系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该ID值通过ftok函数得到。使用 ftok创建共享内存,毫无关系的进程,可以通过得到同样的key,来操作同一个共享内存,对共享内存进行读写时,需要利用信号量进行同步或互斥。

头文件:#include <sys/types.h>

               #include <sys/ipc.h>

2、以下信号量相关的操作函数有相同的头文件:

#incldue <sys/types.h>

#include <sys/ipc.h>

#include <sem.h>

🌻🌻(1)semget函数:建立新的信号量对象或获取已有对象的标识符。

int semget(key_t key,int nsems,int semflg);

参数nsems是信号量对象所特有的,指定了新生成的信号量对象中信号量的数目。如果函数执行的是打开而不是创建操作,则这个参数被忽略。flag是用户设置的标志,如IPC_CREAT。IPC_CREAT表示若系统中尚无指名的共享存储区,则由核心建立一个信号量;若系统中已有信号量,便忽略IPC_CREAT。

例:semid=semget(key, nsems,(IPC_CREAT|0400))创建一个关键字为key的信号量.

函数调用成功时返回信号量标识符,失败时则返回-1。

🌻🌻(3) semop函数:改变信号量对象中各个信号量的状态。

它的声明格式为:

int semop(int semid,struct sembuf *sops,unsigned nsops);

       参数semid是要操作的信号量对象的标识符sops是sembuf的数组,它定义了semop函数要进行的操作序列nsops保存了sops数组的长度,即semop函数将进行操作的个数。

该函数调用成功时返回0,失败时则返回-1。

🌻🌻(4) semctl函数:直接对信号量对象进行控制。

int semctl(int semid,int semnum,int cmd,union semun arg);

semid:对象的标识符,semnum:信号量在集合中的序号,二者唯一确定一个信号量。

参数arg提供了操作所需要的一些信息。参数cmd指定了函数进行的具体操作:IPC_RMID,从内存中删除信号量对象;SETVAL,用arg参数中val成员的值来设定对象内某个信号量的值;GETVAL,返回信号量的计数值。


本文案例可供参考(直接ctrl+C/V到作业嘿嘿~🌝🌝)***

正文:🍀🍀

实验目的】🍀🍀🍀

1、熟悉临界资源、信号量及 PV 操作的定义与物理意义; 

2、了解进程间通信机制,熟悉信号量机制,掌握进程互斥与进程同步的相关知识;掌握用信号量机制解决进程之间的同步与互斥问题;

3.实现生产者-消费者问题,进一步理解进程同步问题。

【实验内容】🍀🍀🍀

🌹🌹一、使用信号量机制来模拟实现PV操作,控制多进程对共享资源的互斥访问。

1、参考代码:🌛🌛🌛

/**********    sem_pv.c    *************************/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h> 
#include <sys/wait.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

typedef union semun{   //semun是用来操作信号量的 共用体!!!
	int val;                    //信号量的值
	struct semid_ds *buf;      //缓冲区
	unsigned short *array;     //记录生产消费者数量的数组
}semun;
//初始化信号量
int sem_init(int key)
{
	int semid;                //对象的标识符
	semun arg;                //信号量的新名称
	
	if((semid=semget(key,1,0660|IPC_CREAT|IPC_EXCL))<0)  //创建包含1个信号量的信号量对象
	{
		perror("sem_init:semget error");
		exit(1);
	}	
	arg.val=1;             
	if(semctl(semid,0,SETVAL,arg)<0)     //给信号量赋初值为1
	{		
		perror("sem_init:semctl error");
		exit(2);	
	};
	
	return semid;                   //返回对象标识符
}
//信号量的P操作
int sem_p(int semid)
{
	struct sembuf pbuf;
	pbuf.sem_num=0;
	pbuf.sem_op=-1;
	pbuf.sem_flg=SEM_UNDO;
	if(semop(semid,&pbuf,1)==-1)
	{
		perror("sem_p:semop error");
		exit(3);
	}
	return 0;
}
//信号量的V操作
int sem_v(int semid)
{
	struct sembuf vbuf;
	vbuf.sem_num=0;
	vbuf.sem_op=1;
	vbuf.sem_flg=SEM_UNDO;
	if(semop(semid,&vbuf,1)==-1)
	{
		perror("sem_v:semop error");
		exit(3);
	} 
	return 0;   
}
//删除信号量
void sem_rmv(int semid)
{
	if(semctl(semid,0,IPC_RMID)<-1)
	{
		perror("sem_rmv:semctl error");
		exit(2);
	} 
}

int main(void)
{      
	key_t key;	
	int pid,fd,semid,n;   
	char str[80];

	key=ftok(".",5);     //将fname设置为当前目录
	semid=sem_init(key);   //初始化信号量
	fd=open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0644);//打开指定文件
 	while((pid=fork())==-1);         //创建进程
   	if(pid==0)                //子进程(模拟消费者)
	{                                 
		sleep(1);
		sem_p(semid);
		lseek(fd,SEEK_SET,0);  //lseek是打开文件设置偏移量,文件偏移量设置为,距文件开始处
		read(fd,str,sizeof(str));
		sem_v(semid);
		printf("child:read str from test file:%s\n",str);  //从文件中读出这个字符串
		exit(0);
	} 
	else                  //父进程(模拟生产者)
	{
		sem_p(semid);    
		printf("parent:please enter a str for test file(strlen<80):\n");//写入文件字符串
		fgets(str,80,stdin);		
		n=strlen(str);
		lseek(fd, SEEK_SET,0);		
		write(fd,str,n);		
		sem_v(semid);
		wait(0);
		close(fd);
		sem_rmv(semid);
		exit(0);
   }
}

2、运行结果:🌜🌜🌜

3、分析结果:⭐⭐⭐

本实验主要通过信号量模拟机制,来实现PV操作。父进程即代表生产者,子进程代表消费者。父进程向buffer(文件)内写入字符串,子进程从buffer(文件)内读出字符串。程序中的sem_p (),sem_v()都是原子操作。由结果可知,能够达到预期效果。

🌹🌹🌹 二、编程解决苹果桔子问题

桌上有1空盘,允许存放1个水果。爸爸向盘中放苹果,也可以向盘中放桔子。儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放1个水果供吃者取用。请用PV操作实现爸爸、儿子、女儿三个并发进程的同步。(具体实现时,可以设置爸爸放水果次数,例如放五次水果,三次放苹果,两次放桔子。)

1、参考代码:🌛:🌛:🌛:🌛

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>
#define P sem_wait
#define V sem_post
#define full_apple &fullA
#define full_orange &fullB
#define empty &empty_aha
sem_t fullA;
sem_t fullB;
sem_t empty_aha;
int num=0,num1=0,num2=0;
void* Dad(void *p)
{
	while (num<5)
	{	if(num1<3)
	{	P(empty);
		num++;
		num1++;
		printf("dad put an apple%d\n",num1);
		V(full_apple);}
		if (num2<2){
		P(empty);
		num++;
		num2++;
		printf("dad put an orange%d\n",num2);
		V(full_orange);}
		}
		}
void* Daughter(void *p)
{
	while(num<5)
	{
		P(full_apple);
		printf("daughter eat an apple%d\n",num1);
		V(empty);
		}
		}
void* Son(void *p)
{
	while(num<5)
	{
		P(full_orange);
		printf("son eat an orange%d\n",num2);
		V(empty);
		}
		}
int main()
{
	sem_init(full_apple,0,0);
	sem_init(full_orange,0,0);
	sem_init(empty,0,1);
	pthread_t tid0;
	pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;
	pthread_create(&tid0,NULL,Dad,NULL);
	pthread_create(&tid1,NULL,Dad,NULL);
	pthread_create(&tid2,NULL,Son,NULL);
	pthread_create(&tid3,NULL,Daughter,NULL);
	getchar();
	pthread_exit(0);
	return 0;
	}
	


2、运行结果:🌜🌜🌜

3、分析结果:⭐⭐⭐ 

整体思路:可以将盘子看作是缓冲区,它的容量是1,所以是互斥信号量;

同步关系:

父亲将苹果放入盘子后,女儿才可以取苹果,否则必须等待;

父亲将橘子放入盘子后,儿子才可以取橘子,否则必须等待;

只有盘子为空时,父亲才能放入水果,否则必须等待。

编写代码思路:进行具体问题的pv操作时,由于是多线程的运行,所以我们在编译时加上-pthread来进行多线程的函数调用。要进行pv操作,首先需要掌握特定的函数,例如信号量的设置,标识符的设定。

【实验心得】:🍀🍀

本次实验主要是针对操作系统中的进程间同步和互斥等知识的运用。在实验中,掌握了Linux和Windows下共享内存的创建、使用和销毁,掌握了信号量的申请、使用记忆销毁,对Linux和Windows下关于共享内存、信号量创建、使用、销毁以及进程创建、销毁的系统调用有了更深刻的认识和理解。在实验中,掌握了如何通过信号量实现进程间的同步和互斥。要注意释放已申请的共享内存和信号量。

 

 

坚持完成的小伙伴最棒! JUJUJ为你打call~

  • 32
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值