作业必备:操作系统实验七【生产者消费者实验(二):共享存储区的同步互斥】

e9cf45ad6e0b4304a7210b334ab22e27.gif

目录🌝🌝🌝

前言:🍀🍀

 【实验资料】 🍀🍀🍀

一、什么是共享存储区🤓🤓🤓?

 二、 相关系统调用👀👀

🌻🌻1、shmget( ):创建、获得一个共享存储区。

🌻🌻2、shmat( ) :用来将共享内存映射到进程的虚拟地址空间。

🌻🌻3、shmdt( ):把一个共享存储区从指定进程的虚地址空间断开。

🌻🌻4、shmctl( ):共享存储区的控制,对其状态信息进行读取和修改。

正文:🍀🍀

【实验目的】:🍀🍀🍀

【实验内容】:🍀🍀🍀

🌸🌸🌸一、编写程序,实现多进程对共享存储区的同步访问。

🌸🌸🌸二、编写程序,利用信号量机制实现多进程对共享存储区的同步互斥访问。

🍀🍀【实验感想】: 

🌝🌝🌝1、第一题中, 通过循环判断共享存储区状态实现进程间同步时,出现应答延迟的现象?

🌝🌝🌝2、评价以上两种实现进程间同步的方法。


 

前言:🍀🍀

🌻🌻🌻本篇有点小难~懒zhuzhu可自行阅读红体字。在实验前,我们需要先了解一些基本知识~

 【实验资料】 🍀🍀🍀

一、什么是共享存储区🤓🤓🤓?

共享存储区(Share  Memory)是LINUX系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。进程之间便可通过对共享存储区中数据的读、写来进行直接通信。

可直接理解下图:

02dceaf2f1144baaad4237cdfa500094.png 进程A将建立的共享存储区附接到自己的AA’区域,进程B将它附接到自己的BB’区域。

 二、 相关系统调用👀👀

🌻🌻1、shmget( ):创建、获得一个共享存储区。

系统调用格式: shmid=shmget(key,size,flag)

参数定义       int  shmget(key_t key, int size,flag);

其中,key是共享存储区的名字;size是其大小(以字节计);flag是用户设置的标志,如IPC_CREAT。

例:shmid=shmget(key,size,(IPC_CREAT|0400))

    创建一个关键字为key,长度为size的共享存储区

🌻🌻2、shmat( ) :用来将共享内存映射到进程的虚拟地址空间。

所有需要访问共享内存的进程,都需要使用该函数将共享内存映射到自己的地址空间,然后就可以方便地对共享区进行访问操作了。

系统调用格式:virtaddr=shmat(shmid,addr,flag)

参数定义   int  *shmat(int shmid, char * addr ,flag);

其中,shmid是共享存储区的标识符;addr用来指定共享内存的映射地址,一般为0,这样系统会用进程中的首个适用地址来映射共享内存;flag规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。该系统调用的返回值是共享存储区所附接到的进程虚地址viraddr。

🌻🌻3、shmdt( ):把一个共享存储区从指定进程的虚地址空间断开。

当进程不再需要使用共享内存时,需要使用shmdt来断开地址映射,此时共享内存对象并没有被删除,仍然存在直到某个进程调用shmctl的IPC_RMID操作将其删除。

系统调用格式: shmdt(addr)

参数定义   int shmdt(const void *addr);

其中,addr是要断开连接的虚地址,亦即以前由连接的系统调用shmat( )所返回的虚地址。调用成功时,返回0值,调用不成功,返回-1。

🌻🌻4、shmctl( ):共享存储区的控制,对其状态信息进行读取和修改。

系统调用格式:shmctl(shmid,cmd,buf)

参数定义   int  shmctl(int shmid,cmd,struct shmid_ds *buf);

其中,buf是用户缓冲区地址,cmd是操作命令,可分为多种类型:

  1. IPC_STAT:获取共享内存的信息,存放在buf指向的结构中。
  2. IPC_SET:用buf所指向的结构中的数据重置共享内存的shmid_ds结构中的成员shm_perm.uid、shm_perm.gid和shm_perm.mode。
  3. IPC_RMID:从系统中删除共享内存对象。
  4. SHM_LOCK:锁住共享内存段,由超级用户执行,阻止共享内存段被交换到swap中去。
  5. SHM_UNLOCK:解锁共享内存段,由超级用户执行,允许共享内存段被交换到swap中去。

函数执行成功返回0,出错则为-1

正文:🍀🍀

实验目的】:🍀🍀🍀

  1. 了解和熟悉共享存储机制
  2. 应用PV操作解决进程间的同步互斥问题。

【实验内容】:🍀🍀🍀

🌸🌸🌸一、编写程序,实现多进程对共享存储区的同步访问。

1、参考代码:🐰🐰🐰

通过循环判断共享存储区状态实现进程间同步。

#include <sys/sem.h> 
#include <sys/shm.h> 
#include <sys/ipc.h> 
#include <stdio.h> 
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>

#define  SHMKEY  75 
int  shmid;   
int  *addr; 

int fatal(char *mes) 
{ 	perror(mes); 
	exit(1); 
} 

void  client( ) 
{  int i; 
addr=shmat(shmid,0,0);           /*获得共享存储区首地址*/ 
for (i=4;i>=0;i--) 
  {  
     while (*addr!=-1); 
     printf("(client) sent *addr= %d\n",i); 
     *addr=i;
 } 
sleep(1);
exit(0); 
} 
 
void  server( ) 
{ 
 int tmp;
addr=shmat(shmid,0,0);        /*获取首地址*/ 
do  
 {    
     *addr=-1; 
     while (*addr==-1); 
     tmp=*addr;
     printf("(server) received *addr=%d\n",tmp); 
 }while (tmp);  
shmctl(shmid,IPC_RMID,0);     /*撤消共享存储区,归还资源*/ 
exit(0); 
} 
int main() 
{ 
    int pid;
    shmid=shmget(SHMKEY,1024,0660|IPC_CREAT); /*创建1024字节大小的共享存储区*/     
   while ((pid=fork( ))==-1); 
   if (!pid) server( ); 
   system("ipcs  -m"); 
   while ((pid=fork( ))==-1); 
   if (!pid) client( ); 
   system("ipcs  -m"); 
   wait(0); 
   system("ipcs  -m"); 
   wait(0); 
   system("ipcs  -m"); 
}

2、运行结果🌴🌴🌴

 

5db8624af58941448e5e8aafca35f549.png

 

🌸🌸🌸二、编写程序,利用信号量机制实现多进程对共享存储区的同步互斥访问。

 1、参考代码:🐰🐰🐰

#include <sys/sem.h> 
#include <sys/shm.h> 
#include <sys/ipc.h> 
#include <stdio.h> 

#define  SHMKEY  75 

int  shmid;   
struct sembuf p={0,-1,0},
v={0,1,0};


int fatal(char *mes) 
{ 	perror(mes); 
	exit(1); 
}
 /******************/
int  senid,recid;   
typedef union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
} semun;

int sem_init(int key,int i)
{
int semid;
semun arg;
if((semid=semget(key,1,0660|IPC_CREAT))<0)
 fatal("sem_init:semget");
arg.val=i;
if(semctl(semid,0,SETVAL,arg)<0)
 fatal("sem_init:semctl");
return semid;
}

/******************/

void  client( ) 
{  int i,*addr; 
addr=shmat(shmid,0,0);           /*获得共享存储区首地址*/ 

for (i=9;i>=0;i--) 
  {  
     semop(senid,&p,1); 
     printf("(client) sent *addr= %d\n",i); 
     *addr=i;
     semop(recid,&v,1);         
 } 
sleep(1);
exit(0); 
} 
 
void  server( ) 
{ 
 int tmp,*addr;
addr=shmat(shmid,0,0);        /*获取首地址*/ 

do  
 {    
     semop(recid,&p,1); 
     tmp=*addr;
     printf("(server) received *addr=%d\n",tmp); 
     semop(senid,&v,1); 
 }while (tmp);  
exit(0); 
} 
 
int main() 
{ 
    int pid;
    shmid=shmget(SHMKEY,1024,0660|IPC_CREAT); /*创建1024字节大小的共享存储区*/ 
    /****************/
 //   mutex=78;
   sen=73;
   rec=74;    
   senid=sem_init(sen,1);
   recid=sem_init(rec,0);
    /****************/
    
   while ((pid=fork( ))==-1); 
   if (!pid) server( ); 
   system("ipcs  -m"); 
   system("ipcs  -s"); 
   while ((pid=fork( ))==-1); 
   if (!pid) client( ); 
   system("ipcs  -m"); 
   wait(0); 
   system("ipcs  -m"); 
   wait(0); 
   system("ipcs  -m"); 
   semctl(senid,0,IPC_RMID);
   semctl(recid,0,IPC_RMID);
   shmctl(shmid,IPC_RMID,0);     /*撤消共享存储区,归还资源*/ 
}

2、运行结果🌴🌴🌴

b9285cebcd854b029afa62d2f1fdd435.png

🍀🍀【实验感想】: 

🌝🌝🌝1、第一题中, 通过循环判断共享存储区状态实现进程间同步时,出现应答延迟的现象?

linux是基于时间片轮转的调度策略。client模拟生产者,当共享存储区空时放数据,server模拟消费者当共享存储区满时取数据。在运行过程中,发现每当client发送一次数据后,server要等待大约0.1秒才有响应。同样,之后client又需要等待大约0.1秒才发送下一个数据。

🌝🌝🌝2、评价以上两种实现进程间同步的方法。

  答:(1)是建立一个key为75的共享区,先后fork()两个子进程:server和client,两子进程间通过共享存储区进行通信。通过循环判断共享存储区状态实现进程间同步。

 

(2)是增加了两个同步信号量集、sem-p、sem-v函数和存储信号操作的数组来实现server和client两子进程对共享存储区的同步访问。

 

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值