在前面的博文中,我们认识到了进程间通信的两种方式--->管道点击打开链接以及消息队列点击打开链接,今天我们来看另一种方式---->共享内存
之前我们在剖析程序在内存中到底如何分配的时候,我们肯定见过下面的图
那,望文生义,linux中的共享内存是不是跟这块的共享内存区有关系呢?
【概念】
1、共享内存允许两个或多个进程共享一个给定的存储区,这一段村书去可以被两个或两个以上的进程映射到自身的地址空间,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过简单的内存读取方式而读走,从而实现进程间通信。
2、优点:共享内存区是最快的IPC形式,不同于管道和消息队列等通信方式,它一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
【数据结构】
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
【函数介绍】
1、shmget函数---->创建共享内存:
int shmget(key_t key,size_t size,int shmflg);
参数:key:该共享内存段名字;size:共享内存大小;
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
上述的shmflg参数为模式标志参数,使用时需要与IPC对象读取权限(如0666)进行|运算来确定信号量集的存取权限
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
2、shmat函数----->将共享内存连接到进程地址空间
void* shmat(int shmid,const void* shmaddir,int shmflg)
参数:shmid:共享内存标识
shmaddir:制定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(只读)
返回值:成功返回一个指针,指向共享内存第一节;失败返回-1
说明
shmaddr为NULL,核⼼心⾃自动选择⼀一个地址
shmaddr不为NULL且shmflg⽆无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃自动向下调整为SHMLBA的整数倍。公式:s
hmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表⽰示连接操作⽤用来只读共享内存
共享内存函数
3、shmdt函数---->将共享内存段与当前进程脱离
int shmdt(const void* shmaddr)
参数:shmaddr:由shmat所指向的指针
返回值:成功返回0;失败返回-1
【注】将共享内存段与当前进程脱离不等于删除共享内存段
4、shmctl函数--->控制共享内存
int shmctl(int shmid,int cmd,struct shmid_ds *buf)
参数:shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个取值),如下
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0,失败返回-1
【实例代码】
Makefile
.PHONY:all
all:server client
client:client.c comm.c
gcc -o $@ $^
server:server.c comm.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f client server
comm.h
#ifndef __COMM_H_
#define __COMM_H_
#include
#include
#include
#include
#define PATHNAME "."
#define PROJ_ID 0X6666
int createshm(int sz);//创建
int destroyshm(int shmid);//销毁
int getshm(int sz);//获得
#endif// __COMM_H_
comm.c
#include "comm.h"
static int commshm(int sz,int flag)
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0){
perror("ftok");
return -1;
}
int shmid=0;
if((shmid=shmget(key,sz,flag)) < 0){
perror("shmget");
return -2;
}
return shmid;
}
int destroyshm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0){
perror("shmctl");
return -1;
}
return 0;
}
int createshm(int sz)
{
return commshm(sz,IPC_CREAT|IPC_EXCL|0666);
}
int getshm(int sz)
{
return commshm(sz,IPC_CREAT);
}
client.c
#include"comm.h"
int main()
{
int shmid=getshm(4096);
sleep(1);
char* addr=shmat(shmid,NULL,0);
sleep(2);
int i=0;
while(i<26){
addr[i] = 'A'+i;
i++;
addr[i] = 0;
sleep(1);
}
shmdt(addr);
sleep(2);
return 0;
}
server.c
#include"comm.h"
int main()
{
int shmid=createshm(4096);
char* addr=shmat(shmid,NULL,0);
sleep(2);
int i=0;
while(i++<26){
printf("client# %s\n",addr);
sleep(1);
}
shmdt(addr);
sleep(2);
destroyshm(shmid);
return 0;
}
【运行结果】