目录🌝🌝🌝
🌻🌻2、shmat( ) :用来将共享内存映射到进程的虚拟地址空间。
🌻🌻3、shmdt( ):把一个共享存储区从指定进程的虚地址空间断开。
🌻🌻4、shmctl( ):共享存储区的控制,对其状态信息进行读取和修改。
🌸🌸🌸二、编写程序,利用信号量机制实现多进程对共享存储区的同步互斥访问。
🌝🌝🌝1、第一题中, 通过循环判断共享存储区状态实现进程间同步时,出现应答延迟的现象?
前言:🍀🍀
🌻🌻🌻本篇有点小难~懒zhuzhu可自行阅读红体字。在实验前,我们需要先了解一些基本知识~
【实验资料】 🍀🍀🍀
一、什么是共享存储区🤓🤓🤓?
共享存储区(Share Memory)是LINUX系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。进程之间便可通过对共享存储区中数据的读、写来进行直接通信。
可直接理解下图:
进程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是操作命令,可分为多种类型:
- IPC_STAT:获取共享内存的信息,存放在buf指向的结构中。
- IPC_SET:用buf所指向的结构中的数据重置共享内存的shmid_ds结构中的成员shm_perm.uid、shm_perm.gid和shm_perm.mode。
- IPC_RMID:从系统中删除共享内存对象。
- SHM_LOCK:锁住共享内存段,由超级用户执行,阻止共享内存段被交换到swap中去。
- SHM_UNLOCK:解锁共享内存段,由超级用户执行,允许共享内存段被交换到swap中去。
函数执行成功返回0,出错则为-1
正文:🍀🍀
【实验目的】:🍀🍀🍀
- 了解和熟悉共享存储机制
- 应用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、运行结果🌴🌴🌴
🌸🌸🌸二、编写程序,利用信号量机制实现多进程对共享存储区的同步互斥访问。
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、运行结果🌴🌴🌴
🍀🍀【实验感想】:
🌝🌝🌝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两子进程对共享存储区的同步访问。