Linux进程通信之共享内存与消息队列


文章目录

  • 1.共享内存的原理
  • 2.共享内存的接口
  • 1.创建或者获取共享内存接口
  • 2.将共享内存附加到进程的虚拟地址空间
  • 3.分离
  • 4.操作共享内存接口
  • 3.共享内存代码
  • 4.共享内存特性
  • 1.生命周期跟随系统
  • 2.共享内存时覆盖写的方式
  • 3.共享内存的删除特性
  • 5.消息队列原理
  • 6.消息队列接口
  • 1.创建消息队列:
  • 2.发送消息:
  • 3.接收消息:
  • 4.操作消息队列的接口
  • 7.消息队列代码


1.共享内存的原理

  • 在物理内存中开辟出一块空间
  • 不同的进程通过页表将物理内存空间映射到自己的进程虚拟地址空间中去
  • 不同的进程通过操作自己的进程虚拟地址空间当中的虚拟地址来操作共享内存

共享内存实现消息队列_c++

2.共享内存的接口

1.创建或者获取共享内存接口

int shmget(key_t,key,size_t,size,int shmflg);
key:共享内存标识符(相当于身份证)
size:共享内存大小
shmflg:获取/创建共享内存时,传递的属性信息
IPC_EXCL|IPC_CREATE:如果获取的共享内存存在,则报错;如果获取的共享内存不存在,则创建(该组合本质上是要获取是重新创建的共享内存)
返回值:成功返回共享内存操作句柄,失败-1

2.将共享内存附加到进程的虚拟地址空间

void *shmat(int shmid,const void * shmaddr,int shmflg);
shmid:共享内存操作句柄
shmaddr:将共享内存附加到共享区中对应的地址上,一般让操作系统自己分配,传递NULL
shmflg:以什么权限将共享内存附加到进程中去
SHM_RDONLY:只读
0:可读可写
返回值:返回附加的虚拟地址,失败返回NULL

3.分离

int shmdt(const void *shmaddr);
shmaddr:shmat的返回
返回值:成功返回0,失败返回-1;

4.操作共享内存接口

int shmctl(int shn_id,int cmd,struct shmid_ds *buf);
shmid:共享内存的操作句柄
cmd:告诉shmctl函数需要完成什么功能
IPC_SET :设置共享内存属性信息
IPC_STAT :获取共享内存属性信息
IPC_RMID :删除共享内存,第三个参数传递NULL
buf:共享内存数据结构buf

3.共享内存代码

write:

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <string.h>

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 <stdio.h>
#include <unistd.h>
#include <sys/shm.h>

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;
}

共享内存实现消息队列_共享内存实现消息队列_02


共享内存实现消息队列_c++_03

4.共享内存特性

1.生命周期跟随系统

共享内存实现消息队列_c++_04

2.共享内存时覆盖写的方式

与管道相比的区别:管道当中的字节流,是直接将数据读走了,没了
而共享内存并没有将数据读走,仅仅是访问地址

3.共享内存的删除特性

ipcs命令&ipcrm命令
一旦共享内存被删除之后,共享内存在物理内存中的空间被销毁了
如果删除共享内存的时候,共享内存附加的进程数量为0,则内核当中描述该共享内存的结构体也被释放了
如果删除共享内存的时候,共享内存附加的进程数量不为0,则会将共享内存中的key变成0x00000000,表示当前共享内存不能被其他进程所附加,共享内存的状态会被设置为destory,附加的进程一旦全部退出后,内核当中描述该共享内存的结构体会被释放掉

5.消息队列原理

msgqueue采用链表来实现消息队列,该链表是由系统内核维护
系统还有很多的msgqueue,每个MQ用消息队列描述符来区分(消息队列id:qid),qid是唯一的用来区分不同的MQ
在系统进行通信时,一个进程将消息加到MQ尾端,另一个进程从消息队列中取消息,这样就实现了进程间的通信

6.消息队列接口

1.创建消息队列:

int msgget(key_t,key,int msgflg)
key:消息队列的标识符
msgflg:创建的标志
返回值:成功返回队列ID,失败返回-1,并设置erron

2.发送消息:

int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
msgid:消息队列ID
msgp:指向msbuf的指针,用来指定发送的消息
操作系统为该函数发送的消息定义了发送格式,只是定义了一部分,另一部分需要程序员自己定义

共享内存实现消息队列_共享内存实现消息队列_05


msgsz:要发送消息的长度(消息内容)


masgflg:创建标记,如果指定IPC_NOWAIT,失败会立即返回(0:阻塞放松,IPC_NOWAIT:非阻塞放松)


返回值:成功返回0,失败返回-1,并设置erron

3.接收消息:

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

4.操作消息队列的接口

int msgctl(int msqid,int cmd,struct msqid_ds *buf);
msqid:消息队列ID
cmd:控制命令(如:IPC_RMID:删除命令,IPC_STAT:获取状态)
buf:存储消息队列的相关信息的buf
返回值:成功根据不同的cmd有不同的返回值,失败返回-1,并设置erron

7.消息队列代码

send;

#include <stdio.h>
#include <unistd.h>
#include <sys/msg.h>
#include <string.h>

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 <stdio.h>
#include <unistd.h>
#include <sys/msg.h>
#include <string.h>

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;
}

共享内存实现消息队列_共享内存实现消息队列_06


共享内存实现消息队列_c++_07

1.拥有一个消息队列(不管是哪一个进程创建出来的)
不同进程想要使用消息队列继续通信的时候,只需要获取同样的消息队列标识符就可以了
2.一个进程发送,一个进程接收

共享内存实现消息队列_消息队列_08