目录
一、共享内存概念
共享内存是一种进程间通信的方式,允许多个进程访问和操作同一块内存区域。这样的内存区域被所有共享它的进程所拥有,进程可以将数据写入共享内存区域,也可以从中读取数据。共享内存在提高进程通信效率和降低开销方面具有优势,但也需要进行同步和互斥操作以避免数据竞争和冲突。
共享内存通常适用于需要高效地在进程间传递大量数据的场景,比如多个进程需要共享大型数据结构、图形图像处理或多媒体应用等。在使用共享内存时,需要注意管理内存的权限、同步访问和处理异常情况等问题,以确保数据的一致性和安全性。
二、共享内存的一些函数
2.1 shmget 创建共享内存
它的函数原型如下:
int shmget(key_t key, size_t size, int shmflg);
参数含义:
1. key: 用于标识共享内存段的键值,不同的进程使用相同的 key 值可以获取到同一个共享内存2. size: 创建共享内存时,指定要申请的共享内存空间大小3. shmflg: 用于指定创建共享内存段的访问权限和其他标志,比如权限位和内存段的创建方式等。常见的标志包括IPC_CREAT
(如果不存在则创建共享内存段)和IPC_EXCL
(如果存在则返回错误)。shmget() 成功则返回共享内存的 ID, 失败返回-1
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
}
2.2 shmat 访问共享内存
函数shmat()
用于将共享内存段附加到调用进程的地址空间,并返回一个指向共享内存段起始地址的指针。
它的函数原型如下:
void* shmat(int shmid, const void *shmaddr, int shmflg);
参数含义:
shmid
: 表示要附加的共享内存段的标识符,通常是由shmget()
函数返回的共享内存标识符。
shmaddr
: 指定共享内存段连接到调用进程地址空间的地址。一般设置为 NULL,由系统自动选择映射的虚拟地址空间
shmflg
: 用以指定附加共享内存段的附加方式。常见的标志包括SHM_RDONLY
(只读方式附加共享内存段)和SHM_RND
(指示共享内存段将在系统限定的地址范围内附加)。 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写返回值:
函数的返回值是一个指针,指向共享内存段的起始地址。 shmat()成功则返回共享内存的首地址,失败返回 NULL
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
char* s=(char*)shmat(shmid,NULL,0);
if(s==(char*)-1)
{
exit(1);
}
strcpy(s,"hello");
shmdt(s);
}
2.3 shmdt 解除共享内存的映射
当进程不再需要访问共享内存时,需要使用 shmdt
函数将共享内存段从进程地址空间中解除映射。其函数原型如下:
int shmdt(const void *shmaddr);
参数含义:
shmaddr
: 表示要分离的共享内存段的起始地址。通常是由shmat()
函数返回的指针,指向共享内存段的起始位置。返回值:
当成功分离共享内存段时,函数返回0。如果出现错误,函数会返回-1,并且可以通过检查
errno
变量来获取错误信息。
2.4 shnctl 删除共享内存段
函数shmctl()
用于控制共享内存段的属性
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数含义:
shmid
: 共享内存段的标识符,通常由shmget()
函数返回。
cmd
: 控制命令,指定对共享内存段执行的操作类型。常见的命令有:
IPC_STAT
: 获取共享内存段的状态信息,并将其存储在buf
指向的shmid_ds
结构体中。IPC_SET
: 设置共享内存段的权限模式,需要提供buf
指向的shmid_ds
结构体。IPC_RMID
: 从系统中删除共享内存段,同时释放其占用的资源。
buf
: 指向一个shmid_ds
结构体的指针,用于存储或传递共享内存段的状态信息或权限设置。返回值:
函数成功执行时返回0,否则返回-1,并通过设置
errno
变量来指示错误类型。
三、共享内存
3.1 创建测试进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
char* s=(char*)shmat(shmid,NULL,0);
if(s==(char*)-1)
{
exit(1);
}
printf("s=%s\n",s);// 打印共享内存的地址
shmdt(s);
}
运行结果:
3.2 使用循环测试
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
char* s=(char*)shmat(shmid,NULL,0);
if(s==(char*)-1)
{
exit(1);
}
while(1)
{
if(strncmp(s,"end",3) == 0)
{
break;
}
printf("s=%s\n",s);
sleep(1);
}
shmdt(s);
}
3.3 共享内存写入程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
char* s=(char*)shmat(shmid,NULL,0);
if(s==(char*)-1)
{
exit(1);
}
while(1)
{
printf("input\n");
char buff[128]={0};
fgets(buff,128,stdin);
strcpy(s,buff);
if( strncmp(buff,"end",3) == 0)
{
break;
}
}
shmdt(s);
}
3.4 带有信号量的共享内存
首先要创建两个信号量,一个设为0,另一个设为1。
头文件代码sem.h
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>
enum INDEX{SEM1=0,SEM2};
union semun
{
int val;
};
void sem_init();//创建并初始化信号量
void sem_p(int index);
void sem_v(int index);
void sem_destroy();
sem.c代码
#include"sem.h"
static int semid=-1;
void sem_init()//创建并初始化信号量
{
semid=semget((key_t)1234,2,IPC_CREAT|IPC_EXCL|0600);
if(semid==-1)
{
semid=semget((key_t)1234,2,0600);
if(semid==-1)
{
printf("semget err\n");
}
}
else
{
int arr[2]={1,0};
for(int i=0;i<2;i++)
{
union semun a;
a.val=arr[i];
if(semctl(semid,i,SETVAL,a)==-1)
{
printf("semctl init err\n");
}
}
}
}
void sem_p(int index)
{
struct sembuf buf;
buf.sem_num=index;
buf.sem_op=-1;//p
buf.sem_flg=SEM_UNDO;
if(semop(semid,&buf,1) == -1)
{
printf("op p err\n");
}
}
void sem_v(int index)
{
struct sembuf buf;
buf.sem_num=index;
buf.sem_op=1;//v
buf.sem_flg=SEM_UNDO;
if(semop(semid,&buf,1) == -1)
{
printf("op v err\n");
}
}
void sem_destroy()
{
if(semctl(semid,0,IPC_RMID) == -1)
{
printf("semctl del\n");
}
}
使用信号量的main.c代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
#include"sem.h"
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
char* s=(char*)shmat(shmid,NULL,0);
if(s==(char*)-1)
{
exit(1);
}
sem_init();//创建 初始化
while(1)
{
printf("input\n");
char buff[128]={0};
fgets(buff,128,stdin);
sem_p(SEM1);//p s1
strcpy(s,buff);
sem_v(SEM2);//v s2
if( strncmp(buff,"end",3) == 0)
{
break;
}
}
shmdt(s);
}
测试代码test.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/shm.h>
#include"sem.h"
int main()
{
int shmid = shmget((key_t)2345,128,IPC_CREAT|0600);
if(shmid == -1)
{
exit(1);
}
char* s=(char*)shmat(shmid,NULL,0);
if(s==(char*)-1)
{
exit(1);
}
sem_init();
while(1)
{
sem_p(SEM2);
if(strncmp(s,"end",3) == 0)
{
break;
}
printf("s=%s\n",s);
sem_v(SEM1);
}
shmdt(s);
sem_destroy();
exit(0);
}
运行结果: