Linux进程通信之共享内存与消息队列
- 在物理内存中开辟出一块空间
- 不同的进程通过页表将物理内存空间映射到自己的进程虚拟地址空间中去
- 不同的进程通过操作自己的进程虚拟地址空间当中的虚拟地址来操作共享内存
int shmget(key_t,key,size_t,size,int shmflg);
key:共享内存标识符(相当于身份证)
size:共享内存大小
shmflg:获取/创建共享内存时,传递的属性信息
IPC_EXCL|IPC_CREATE:如果获取的共享内存存在,则报错;如果获取的共享内存不存在,则创建(该组合本质上是要获取是重新创建的共享内存)
返回值:成功返回共享内存操作句柄,失败-1
void *shmat(int shmid,const void * shmaddr,int shmflg);
shmid:共享内存操作句柄
shmaddr:将共享内存附加到共享区中对应的地址上,一般让操作系统自己分配,传递NULL
shmflg:以什么权限将共享内存附加到进程中去
SHM_RDONLY:只读
0:可读可写
返回值:返回附加的虚拟地址,失败返回NULL
int shmdt(const void *shmaddr);
shmaddr:shmat的返回
返回值:成功返回0,失败返回-1;
int shmctl(int shn_id,int cmd,struct shmid_ds *buf);
shmid:共享内存的操作句柄
cmd:告诉shmctl函数需要完成什么功能
IPC_SET :设置共享内存属性信息
IPC_STAT :获取共享内存属性信息
IPC_RMID :删除共享内存,第三个参数传递NULL
buf:共享内存数据结构buf
write:
#include
#include
#include
#include
int main(){
//1.获取共享内存
int shm_id = shmget(0x34343434, 1024, IPC_CREAT | 0664);
if (shm_id < 0){
perror("shmget");
return 0;
}
//2.附加
void* addr = shmat(shm_id, NULL, 0);
if (addr == NULL){
perror("shmat");
return 0;
}
printf("shmat addr : %p\n", addr);
//3.写, 把这个地址当中是正常的地址使用即可
/*
* char* lp = (char*)malloc(1024);
* strcpy
* */
strcpy((char*)addr, "java");
while (1){
sleep(1);
}
/*
* 1.可以将进程分离
* */
shmdt(addr);
return 0;
}
read:
#include
#include
#include
int main(){
//1.获取共享内存
int shm_id = shmget(0x34343434, 1024, IPC_CREAT | 0664);
if(shm_id < 0){
perror("shmget");
return 0;
}
//2.附加
void* addr = shmat(shm_id, NULL, SHM_RDONLY);
if(addr == NULL){
perror("shmat");
return 0;
}
//3.读
// 打印读到的内容
/*
* char* lp = "abc";
* printf("%s\n", lp);
* */
printf("read_shm : %s\n", (char*)addr);
while(1){
sleep(1);
}
//4.分离
shmdt(addr);
return 0;
}
与管道相比的区别:管道当中的字节流,是直接将数据读走了,没了
而共享内存并没有将数据读走,仅仅是访问地址
ipcs命令&ipcrm命令
一旦共享内存被删除之后,共享内存在物理内存中的空间被销毁了
如果删除共享内存的时候,共享内存附加的进程数量为0,则内核当中描述该共享内存的结构体也被释放了
如果删除共享内存的时候,共享内存附加的进程数量不为0,则会将共享内存中的key变成0x00000000,表示当前共享内存不能被其他进程所附加,共享内存的状态会被设置为destory,附加的进程一旦全部退出后,内核当中描述该共享内存的结构体会被释放掉
msgqueue采用链表来实现消息队列,该链表是由系统内核维护
系统还有很多的msgqueue,每个MQ用消息队列描述符来区分(消息队列id:qid),qid是唯一的用来区分不同的MQ
在系统进行通信时,一个进程将消息加到MQ尾端,另一个进程从消息队列中取消息,这样就实现了进程间的通信
int msgget(key_t,key,int msgflg)
key:消息队列的标识符
msgflg:创建的标志
返回值:成功返回队列ID,失败返回-1,并设置erron
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
msgid:消息队列ID
msgp:指向msbuf的指针,用来指定发送的消息
操作系统为该函数发送的消息定义了发送格式,只是定义了一部分,另一部分需要程序员自己定义
msgsz:要发送消息的长度(消息内容)
masgflg:创建标记,如果指定IPC_NOWAIT,失败会立即返回(0:阻塞放松,IPC_NOWAIT:非阻塞放松)
返回值:成功返回0,失败返回-1,并设置erron
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg );
msgid:消息队列ID
msgp:指向msbuf的指针,用来接收消息
msgsz:要接受消息的长度
msgtyp:接收消息的方式
msgtyp=0,读取列表中的第一条消息
msgtyp>0,读取列表类型为msgtyp的第一条消息,除非在msgflg中指定了MSG_EXCEPT,将读取类型不等于msgtyp的第一条消息
msgflg:创建标记,如果指定IPC_NOWAIT,获取失败会立即返回
返回值:成功返回实际读取消息的字节数,失败返回-1,并设置erron
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
msqid:消息队列ID
cmd:控制命令(如:IPC_RMID:删除命令,IPC_STAT:获取状态)
buf:存储消息队列的相关信息的buf
返回值:成功根据不同的cmd有不同的返回值,失败返回-1,并设置erron
send;
#include
#include
#include
#include
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[255]; /* message data */
};
int main(){
//1.获取/创建消息队列
int que_id = msgget(0x23232323, IPC_CREAT | 0664);
if(que_id < 0){
perror("msgget");
return 0;
}
printf("msgqueue id is %d\n", que_id);
//2.组织要发送的消息
for(int i = 0; i < 10; i++){
struct msgbuf mb;
mb.mtype = i + 1;
const char* msg = "i am send msg prcess";
sprintf(mb.mtext, "%s, %d", msg, i + 1);
//3.发送
msgsnd(que_id, &mb, sizeof(mb.mtext), 0);
}
return 0;
}
receive:
#include
#include
#include
#include
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[255]; /* message data */
};
int main(){
//1.获取/创建消息队列
int que_id = msgget(0x23232323, IPC_CREAT | 0664);
if(que_id < 0){
perror("msgget");
return 0;
}
printf("msgqueue id is %d\n", que_id);
//2.准备接收的buf
struct msgbuf mb;
//3.接收
msgrcv(que_id, &mb, sizeof(mb.mtext), 9, 0);
printf("recv content : %s\n", mb.mtext);
//4.打印内容
return 0;
}
1.拥有一个消息队列(不管是哪一个进程创建出来的)
不同进程想要使用消息队列继续通信的时候,只需要获取同样的消息队列标识符就可以了
2.一个进程发送,一个进程接收