主要作用
- 不同进程之间,高效率传输大量的数据。
- 高效率是相对于之前谈及的
管道
和消息队列
而言(我的另外几篇博文中有)的。管道和消息队列
需要借助linux kernel
来进行数据转交,效率较低,适合传输少量数据。 - 把
进程1对应的虚拟地址
和进程2对应的虚拟地址
映射到同样的物理空间。那么进程1往它的虚拟地址写,进程2读它自己的虚拟地址就能直接读到。
共享内存的用法
- 定义一个唯一的
key
(可以用ftok() 也可以认为指定键值 (key_t 0x666 )) - 构造一个
共享内存对象
(shmget) - 共享内存映射(shmat)
- 解除共享内存映射(shmdt)
- 删除共享内存(shmctl RMID)
shmget函数
- 功能:获取共享内存对象的id
- 函数原型:
int shmget(key_t key,int size,int shmflg);
-
参数:
-
key:共享内存键值(可以自己定义,也可以依靠ftok函数)
-
size:共享内存大小
-
shmflg:
- IPC_CREAT:共享内存不存在则创建
- mode:共享内存 rwx 权限
-
-
返回值:
- 成功:共享内存id
- 失败:-1
shmat函数
- 功能:映射共享内存
- 函数原型:
void* shmat(int shmid,const void *shmaddr,int shmflg);
-
参数:
- shmid:共享内存id
- shmaddr:映射地址,为NULL时表示自动分配
- shmflg:
SHM_RDONLY:只读方式映射
0:可读可写
-
返回值:
- 成功:共享内存地址
- 失败:-1
shmdt函数
- 功能:解除共享内存映射
- 函数原型:
int shmdt(const void *shmaddr);
-
参数:
- shmaddr:映射地址
-
参数:
- 成功:0
- 失败:-1
shmctl函数
- 功能:获取或设置共享内存的相关属性
- 函数原型:
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
-
参数:
-
shmid:共享内存id
-
cmd:
IPC_STAT:获取共享内存的属性信息,由参数buf返回
IPC_SET:设置共享内存的属性,由参数buf传入
IPC_RMID:删除共享内存 -
buf:属性缓冲区
-
-
返回值:
- 成功:由cmd类型决定,上面列出的这三个都返回-1.
- 失败:-1
测试代码
#include<sys/ipc.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include <sys/types.h>
union semun
{
int val;
struct semid_ds *buf;
};
//初始化信号量
int init_sem(int sem_id,int init_value)
{
union semun sem_union;
sem_union.val = init_value;
if(semctl(sem_id,0,SETVAL,sem_union) == -1)
{
printf("Initialize semaphore.\n");
return -1;
}
return 0;
}
//删除信号量
int del_sem(int sem_id)
{
union semun sem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union) == -1)
{
perror("Delete semahore.\n");
return -1;
}
}
// p 操作
int sem_p(int sem_id)
{
pid_t a;
struct sembuf sops;
printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid());
sops.sem_num = 0;//单个信号量的编号应该为0
sops.sem_op = -1;//表示 p 操作,减操作
sops.sem_flg = SEM_UNDO;//到最后系统会自动释放系统中残留的信号量
printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid());
if(semop(sem_id,&sops,1) == -1)//不符合操作条件时会阻塞于此
{
perror("P operation.\n");
return -1;
}
printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid());
return 0;
}
// v 操作
int sem_v(int sem_id)
{
pid_t a;
struct sembuf sops;
printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid());
sops.sem_num = 0;//单个信号量的编号为 0
sops.sem_op = 1;//表示 v 操作
sops.sem_flg = SEM_UNDO;//系统自动释放残留的信号量
printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid());
if(semop(sem_id,&sops,1) == -1)//不符合操作条件时会阻塞于此
{
perror("V operation.\n");
return -1;
}
printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid());
return 0;
}
#define DELAY_TIME 3
int main(void)
{
pid_t result;
int sem_id;
int shm_id;
char *addr;
//创建一个信号量
sem_id = semget((key_t)6666, 1, 0666 | IPC_CREAT);
//创建一个共享内存对象
shm_id = shmget((key_t)7777, 1024, 0666 | IPC_CREAT);
//信号量的id,和信号量初始化的值为0
init_sem(sem_id, 0);
//调用 fork() 函数
result = fork();
if(-1 == result)
{
perror("Fail fork.\n");
}
else if(0 == result)//子进程
{
printf("Child process will wait for some seconds ...\n");
sleep(DELAY_TIME);
//映射共享内存
addr = shmat(shm_id,NULL,0);
if(addr == (void *)-1)
{
printf("Fail shmat.\n");
exit(-1);
}
//设置共享内存中的内容
memcpy(addr,"HelloWorld",11);
printf("The child process is running ...\n");
//给信号量的值加 1
sem_v(sem_id);
}
else//父进程
{
//同步工作:
//若信号量的值已经为0,那么这个减1的操作将阻塞于此
//从而保证子进程运行在父进程的前面
sem_p(sem_id);
printf("The father process is running ...\n");
//映射共享内存的地址
addr = shmat(shm_id,NULL,0);
if(addr == (void *)-1)
{
printf("Fail father shmat.\n");
exit(-1);
}
printf("Shared memory string : %s\n",addr);
//解除共享内存映射
shmdt(addr);
//删除共享内存映射
shmctl(shm_id,IPC_RMID,NULL);
sem_v(sem_id);
del_sem(sem_id);
}
exit(0);
}
编译与运行
jl@jl-virtual-machine:~/test$ ./a.out
test.c(48):pid = 19588
test.c(52):pid = 19588
Child process will wait for some seconds ...
The child process is running ...
test.c(67):pid = 19589
test.c(71):pid = 19589
test.c(77):pid = 19589
test.c(58):pid = 19588
The father process is running ...
Shared memory string : HelloWorld
test.c(67):pid = 19588
test.c(71):pid = 19588
test.c(77):pid = 19588
jl@jl-virtual-machine:~/test$
测试成功!