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

目录

1. 消息队列

1.1 消息队列特点

1.2 消息队列相关函数

1.3 ftok 函数

1.4 消息队列实现全双工通信

1.4.1 msg_service.c

1.4.2 msg_client.c

1.4.3 程序运行结果:

2. 共享内存

2.1 共享内存概念

2.2 相关函数

2.3 共享内存特点

2.4 共享内存实现两进程通信

2.4.1 shmWrite.c

2.4.2 shmRead.c

2.4.3 程序运行结果:


1. 消息队列

消息队列,是消息的链表,存放在内核中,一个消息队列由一个标识符(队列ID)来标识。

1.1 消息队列特点

  • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
  • 消息队列独立于发送和接收进程,进程终止时,消息队列及其内容仍存在
  • 消息队列可以实现消息的随机查询,消息不一定要先进先出的次序读取,也可以按消息的类型读取。

1.2 消息队列相关函数

1. int msgget(key_t key, int msgflg);
//创建或打开消息队列,

参数:
      key:和消息队列关联的 key 值
      msgflg:是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。 msgflg 可以与 IPC_CREAT 做或操作,表示当 key 所命名的消息队列不存在时创建一个消息队列,如果 key 所命名的消息队列存在时,IPC_CREAT 标志会被忽略,而只返回一个标识符。
     返回值:成功返回队列 ID,失败则返回‐1,

在以下两种情况下,msgget 将创建一个新的消息队列:

  • 如果没有与键值 key 相对应的消息队列,并且 flag 中包含了 IPC_CREAT 标志
  • key 参数为 IPC_PRIVATE
2.int msgsnd(int msgid, const void *msgp, size_t msgsize, int msgflg);
//读取消息,成功返回消息数据的长度,失败返回‐1

参数:
       msgid:消息队列的 ID
       msgp:指向消息的指针,常用结构体 msgbuf 如下:
                     struct msgbuf
                      {
                        long mtype;     //消息类型
                        char mtext[N];  //消息正文
                      }
         size:发送的消息正文你的字节数
         flag: IPC_NOWAIT: 消息没有发送完成函数也会立即返回
               0: 直到发送完成函数才返回
返回值:  成功:0
        失败:‐1
3.ssize_t msgrcv(int msgid, void *msgp, size_t msgsize, long msgtype, int msgflag);
//从一个消息队列中获取消息

参数:
         msgid:消息队列的 ID
         msgp:要接收消息的缓冲区
         size:要接收的消息的字节数
         msgtype:
                 0:接收消息队列中第一个消息
                 大于0:接收消息队列中第一个类型为 msgtyp 的消息
                 小于0:接收消息队列中类型值不大于 msgtyp 的绝对值且类型值又最小的消息。
         flag:       
                0:若无消息函数一直阻塞
                IPC_NOWAIT:若没有消息,进程会立即返回 ENOMSG。
返回值:
         成功:接收到的消息i长度
         出错:‐1
4.int msgctl(int msgid, int cmd, struct msgid_ds *buf);
//控制消息队列,成功返回0,失败返回‐1

参数:
        msqid:消息队列的队列ID
        cmd:
             IPC_STAT:把 msgid_ds 结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖 msgid_ds 的值。
             IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为 msgid_ds 结构中给出的值
             IPC_RMID:删除消息队列
    
         buf:是指向 msgid_ds 结构的指针,它指向消息队列模式和访问权限的结构
         
返回值:
        成功:0
        失败:‐1

1.3 ftok 函数

key_t ftok( char * fname, int id )
//系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。该id值通过 ftok 函数得到。

参数:
        fname就时你指定的文件名(该文件必须是存在而且可以访问的)。
        id是子序号, 虽然为int,但是只有8个比特被使用(0‐255)。
返回值:
        当成功执行的时候,一个 key_t 值将会被返回,否则 ‐1 被返回。

1.4 消息队列实现全双工通信

1.4.1 msg_service.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct msgbuf // 消息队列结构体
{
	long mtype;
	char mtext[128];
	char ID[4];
};

int main()
{
	struct msgbuf sendbuf, readbuf;
	int msgId;
	key_t key;
	int readret;
	pid_t pid;
	key = ftok("a.c", 1);				   // 获取 key 值
	msgId = msgget(key, IPC_CREAT | 0755); // 创建消息队列
	if (msgId == -1)
	{
		printf("create message queue failed!~\n");
		perror("msgget");
		return -1;
	}
	printf("create msgage queue succeeded!~ msgId = %d\n", msgId);
	system("ipcs -q"); // 显示当前消息队列信息

	// init msgbuf
	sendbuf.mtype = 100;
	pid = fork();

	// parent process write 100
	if (pid > 0)
	{
		while (1)
		{
			memset(sendbuf.mtext, 0, 128);
			printf("please input to message queue:\n");
			fgets(sendbuf.mtext, 128, stdin);

			// send message to message queue
			msgsnd(msgId, (void *)&sendbuf, strlen(sendbuf.mtext), 0);
		}
	}

	// child process read 200
	if (pid == 0)
	{
		while (1)
		{
			memset(readbuf.mtext, 0, 128);
			msgrcv(msgId, (void *)&readbuf, 128, 200, 0);
			printf("datas from client: %s\n", readbuf.mtext);
		}
	}

	return 0;
}

1.4.2 msg_client.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct msgbuf // 消息队列结构体
{
	long mtype;
	char mtext[128];
	char ID[4];
};

int main()
{
	struct msgbuf sendbuf, readbuf;
	int msgId;
	key_t key;
	int readret;
	pid_t pid;
	key = ftok("a.c", 1);				   // 获取 key 值
	msgId = msgget(key, IPC_CREAT | 0755); // 创建消息队列
	if (msgId == -1)
	{
		printf("create message queue failed!~\n");
		perror("msgget");
		return -1;
	}
	printf("create msgage queue succeeded!~ msgId = %d\n", msgId);
	system("ipcs -q"); // 显示当前消息队列信息

	// init msgbuf
	sendbuf.mtype = 200;
	pid = fork();

	// child process write 200
	if (pid == 0)
	{
		while (1)
		{
			memset(sendbuf.mtext, 0, 128);
			printf("please input to message queue:\n");
			fgets(sendbuf.mtext, 128, stdin);

			// send message to message queue
			msgsnd(msgId, (void *)&sendbuf, strlen(sendbuf.mtext), 0);
		}
	}

	// parent process read 100
	if (pid > 0)
	{
		while (1)
		{
			memset(readbuf.mtext, 0, 128);
			msgrcv(msgId, (void *)&readbuf, 128, 100, 0);
			printf("datas from service: %s\n", readbuf.mtext);
		}
	}

	return 0;
}

1.4.3 程序运行结果:

Linux 进程间通信 —— 消息队列与共享内存_第1张图片

2. 共享内存

2.1 共享内存概念

共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。

2.2 相关函数

1.int shmget(key_t key, size_t size, int shmflg);
//用来获取或创建共享内存

参数:
       key:IPC_PRIVATE 或 ftok 的返回值
       size:共享内存区大小
       shmflg:同 open 函数的权限位,也可以用8进制表示法
返回值:
       成功:共享内存段标识符‐‐‐ID‐‐‐文件描述符
       出错:‐1
2.void *shmat(int shm_id, const void *shm_addr, int shmflg);
//把共享内存连接映射到当前进程的地址空间

参数:
     shm_id:ID号
     shm_addr:映射到的地址,NULL 为系统自动完成的映射
     shmflg:
               SHM_RDONLY 共享内存只读
               默认是 0,表示共享内存可读写
 返回值:
        成功:映射后的地址
        失败:NULL
3.int shmdt(const void *shmaddr);
//将进程里的地址映射删除

参数:
       shmid:要操作的共享内存标识符
返回值: 
        成功:0
        出错:‐1
4.int shmctl(int shm_id, int command, struct shmid_ds *buf);
//删除共享内存对象

参数:
       shmid:要操作的共享内存标识符
       command :
             IPC_STAT  (获取对象属性)‐‐‐  实现了命令ipcs ‐m
             IPC_SET (设置对象属性)
             IPC_RMID (删除对象)   ‐‐‐实现了命令ipcrm ‐m
       buf :指定 IPC_STAT/IPC_SET 时用以保存/设置属性
返回值:
       成功:0
       出错:‐1

2.3 共享内存特点

  • 共享内存创建之后,一直存在于内核中,直到被删除或系统关闭
  • 共享内存和管道不一样,读取后,内容仍然在共享内存中

2.4 共享内存实现两进程通信

2.4.1 shmWrite.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	int shmId;
	int key;
	char *p;
	key = ftok("a.c", 1); // 生成 key 值
	if (key < 0)
	{
		perror("ftok");
		return -1;
	}
	printf("ftok succeed!~ key = %x\n", key);
	shmId = shmget(key, 128, IPC_CREAT | 0777); // 创建共享内存
	if (shmId < 0)
	{
		perror("share memory:");
		return -1;
	}
	printf("create share memory succeeded!~ shmId = %d\n", shmId);
	system("ipcs -m");
	p = (char *)shmat(shmId, NULL, 0); // 共享内存映射,配置可读可写的方式由系统自动映射
	if (p == NULL)
	{
		perror("share memory function");
		return -1;
	}
	memset(p, 0, 128);
	// write to share memory
	while (1)
	{
		fgets(p, 128, stdin);
	}

	return 0;
}

2.4.2 shmRead.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	int shmId;
	int key;
	char *p;
	key = ftok("a.c", 1);
	if (key < 0)
	{
		perror("ftok");
		return -1;
	}
	printf("ftok succeed!~ key = %x\n", key);
	shmId = shmget(key, 128, 0);
	if (shmId < 0)
	{
		perror("share memory:");
		return -1;
	}
	printf("create share memory succeeded!~ shmId = %d\n", shmId);
	system("ipcs -m");
	p = (char *)shmat(shmId, NULL, 0);
	if (p == NULL)
	{
		perror("share memory function");
		return -1;
	}
	while (1)
	{
		sleep(5);
		printf("share memory data = %s\n", p);
		memset(p, 0, 128);
	}

	// shmdt(p);
	return 0;
}

2.4.3 程序运行结果:

Linux 进程间通信 —— 消息队列与共享内存_第2张图片

你可能感兴趣的:(linux)