Linux c 基于内存的进程通信—共享内存、共享队列(消息队列)
基于内存的进程通信:
1. 内核共享内存
编程模型:
1.1.创建共享内存,得到一个ID shmget
1.2.把ID影射成虚拟地址(挂载) shmat
1.3.使用虚拟地址访问内核共享内存使用任何内存函数与运算符号 1.4.卸载虚拟地址 shmdt
1.5.删除共享内存 shctl(修改/获取共享内存的属性)
案例:
A.创建共享内存,并且修改内存数据
1. 创建共享内存
#include<sys/shm.h>
intshmget(key_t key,//为什么需要key
int size,//共享内存大小
int flags//共享内存的属性与权限
)
为什么要key_t:
约定创建与访问的是同一个共享内存。Key为两个进程之间访问同一块共享内存的约定
注:key需要唯一性,因为我们不能保证我们自己定义的key的唯一性,所以为了保证kay的唯一性,我们需要用某个文件对应的整数来充当kay值,可以用ftok函数来将一个文件转化为一个kay值。(一般我们用两个进程的共同的工程目录文件来确定kay值)
ftok函数:
#include<sys/ipc.h>
key_t ftok( const char * pathname,int proj_id);
参数二:一个控制因子。建议在0—255之间
第三个参数:
方式|权限
方式:创建 IPC_CREAT IPC_EXCL(如果内存已经创建,直接错误返回)
打开:0
常见的两种方式:
创建:IPC_CREAT|IPC_EXCL | 0666;
打开:0
返回:
成功返回共享内存ID
失败返回-1
失败返回-1
B.根据ID得到共享内存,并且访问内存数据。
挂载共享内存
void* shmat(int id,
void*startaddr,//0:系统指定首地址
intflags)//挂载方式,建议0默认读写,可以使用IPC_RDONLY
返回值:合法地址成功,-1失败
C.删除
intshmctl(int id,//被操作的共享内存ID
inthow,//操作方式:一共三种操作
structshmid_ds*ds)//共享内存属性
how:
IPC_STAT
IPC_SET //修改属性
IPC_RMID //删除 参数三无用
案例代码:
ShmA.c
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/shm.h>
#include<sys/ipc.h>
void main()
{
key_t key;
int shmid;
int * p;
//1.创建共享内存
key=ftok( “.” , 255 ); //用当前路径的目录来确定kay值
if(key == -1) printf(“ftok error%m\n”) , exit( - 1 );
shmid=shmget( key , 4 , IPC_CREAT|IPC_EXCL | 0666 );
if(shmid == -1) printf(“shmget error %m\n”) , exit( -1 );
//2.挂载共享内存
p=shmat( shmid , 0 , 0);
if(p==(int *) - 1) printf(“at error %m\n”) , exit( -1 );
//3.访问共享内存
*p=999;
//4.卸载共享内存
shmdt(shmid);
//删除共享内存
shctl( shmid , IPC_RMID , 0);
}
不创建共享内存,只访问已有的
shmB.c
ShmA.c
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/shm.h>
#include<sys/ipc.h>
void main()
{
key_t key;
int shmid;
int * p;
//1.得到共享内存
key=ftok( “.” , 255 ); //用当前路径的目录来确定kay值
if(key == -1) printf(“ftok error%m\n”) , exit( - 1 );
shmid=shmget( key , 4 ,0 );
if(shmid == -1) printf(“shmget error %m\n”) , exit( -1 );
//2.挂载共享内存
p=shmat( shmid , 0 , 0);
if(p==(int *) - 1) printf(“at error %m\n”) , exit( -1 );
//3.访问共享内存
printf(“%d\n”,*p);
//4.卸载共享内存
shmdt(shmid);
}
2. 内核共享队列(有序)
编程模型:
2.1.创建共享队列/得到队列msgget
2.2.使用队列(发送消息msgsnd/接收消息msgrcv)
2.3.删除队列msgctl
案例:
创建共享队列
#include<sys/msg.h>
intmsgget(key_t,int); 除了不用指定大小,和shmget函数的参数一样
发送消息
intmsgsnd(
intid,//消息队列ID
constvoid *msg,//要发送消息
size_tlen,//消息的长度
int flags//发送消息的方式,建议为0
);
返回:
-1:失败
0:成功
第二个参数的消息有固定的格式
4字节:表示消息的类型
若干字节:消息内容。
消息格式:(该结构体需要我们自己定义)
struct msgbuf{
long mtype; //消息类型
char mtext[1]; //消息内容
}
第三个参数:
消息的大小,不包含类型的4个字节
接收消息:
size_t msgrcv(int id ,
void * msgp ,
size_t msgsz, ,
long msgtype , //那种类型的消息
int msgflg);
返回:
-1:失败
大小:成功
删除队列:msgctl 参数和shctl一样
案例代码:
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct msgbuf
{
long type;
char data[32];
}
void main()
{
key_t key;
int msgid;
//1创建消息队列
key= ftok(“ . ” , 254);
if(key == -1) printf(“ftok error:%m\n”) , exit(-1);
msgid= msgget(key,IPC_CREAT | IPC_EXCL|0666);
if(msgid==-1) printf(“get error : %m\n”), exit(-1);
//2构造消息
struct msgbuf msg;
//3发送消息
for(i=1;i<=10;i++)
{
msg.type=1; //消息类型自己定义一个long类型
sprintf(msg.data , “Message:%d”,i);
msgsnd(msgid ,&msg , strlen(msg.data) , 0);
}
//4删除队列
//msgctl(msgid, IPC_RMID,0);
}
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct msgbuf
{
long type;
char data[32];
}
void main()
{
key_t key;
int msgid;
//1得到消息队列
key = ftok(“ . ” , 254);
if(key == -1) printf(“ftok error:%m\n”) , exit(-1);
msgid= msgget(key,0);
if(msgid== -1) printf(“get error : %m\n”), exit(-1);
//2构造消息
struct msgbuf msg;
//3接收消息
while(1)
{
bzero(&msg,sizeof(msg));
msgrcv(msgid , & msg sizeof(msg.data) , 1 , 0);
printf(“%s\n”,msg.data);
}
}
说明:如果消息队列中没有消息了读取消息的程序会阻塞等待
当程序发送消息到队列中,一个程序读取了所以消息,队列中就没有消息,就无法再读取了,只能等待在发送消息后在读取消息。