ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

linux系统进程间通信方式(一):共享内存

2022-05-05 22:01:46  阅读:216  来源: 互联网

标签:p2 IPC 共享内存 间通信 p1 key linux include


 

前言

        linux系统可以创建多个用户进程,进程之间有多种通信方式,可以使用system-V的IPC对象进行通信;从本文开始,首先介绍system-V IPC之一的共享内存。

一、了解system-V IPC

(一)什么是SYSTEM-V IPC?

        linux系统的内核可以创建一些对象(共享内存、消息队列、信号量),这些对象拥有它们自己的标识符(ID),并且每一个对象都是通过一个唯一的key来绑定(识别)它;多个进程可以这些对象来通信,这些内核中的对象被人们称为system-V IPC。

        上面讲的这些IPC对象它是存在于内核中,和进程是相互独立的;进程想使用这些IPC对象,那它怎么知道现在用的是哪一个对象呢?所以需要提供一个唯一的值来识别(绑定)这些IPC对象。那么在linux系统里面怎么获取一个唯一的值呢?我们都知道,对于linux系统来说,一切都是文件,文件是唯一的,那么它的inode节点也是唯一的,所以可以利用它来创建一个唯一的值,也就是上面提到的key。

        linux提供了一个api函数ftok,它可以帮我们创建一个唯一的key,来绑定一个IPC对象。(创建key的原理这里简述一下:就是通过文件的inode号和一个用户指定的工程id(proj_id)来制作key,具体怎么生成这里不详细展述)

        那么多个进程使用这个相同的key,就可以获取到同一个IPC对象的ID,从而使用同一个IPC对象来进行通信。

        下面展示一下linux系统中的IPC对象的有关信息(使用命令ipcs)

注意:输入ipcs -m只显示共享内存;输入ipcs -q只显示消息队列;输入ipcs -s只显示信号量

        上述的IPC 对象在内核内部使用链表维护,不同的对象使用 IPC 标识符来标识,如消息队列标识符 msqid、共享内存标识符 shmid,信号量标识符 semid。对于用户来说,内核提供了简洁的接口,不同的进程通过指定的key 就可以访问具体的对象。

(二)system-V IPC的特点

1.独立于进程
2.没有文件名和文件描述符
3.IPC对象具有key和ID

 

二、使用共享内存实现进程间通信(示例)

1.设计意图

实现两个进程通信,一个进程往共享内存写数据,另一个从共享内存读取数据

2.设计思路

1、首先有两个程序:p1.c、p2.c;

2、这两个程序先后运行之后系统会创建两个进程(下面统称为p1、p2);

3、p1创建一块共享内存、创建两个信号量

4、p2获取这块共享内存、打开信号量

5、p1从键盘获取字符串,然后往共享内存写入数据

6、p2从共享内存读取数据,并打印出数据

7、通信过程使用posix有名信号量实现同步(即p1写完数据p2才能读取数据)

3.代码实现

程序p1.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <error.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

static void free_resource(char *shmaddr, int shmid, sem_t *sem1, sem_t *sem2)
{
	int ret;

	ret = shmdt(shmaddr);
	if(ret < 0)
	{
		perror("shm detach fail");
	}
	
	if(shmctl(shmid, IPC_RMID, NULL)<0)
	{
		perror("remove a shm fail");
	}
	
	if(sem_close(sem1) < 0)
	{
		perror("close sem1 fail");
	}

	if(sem_close(sem2) < 0)
	{
		perror("close sem2 fail");
	}

	if(sem_unlink("sem1") < 0)
	{
		perror("remove sem1 fail");
	}

	if(sem_unlink("sem2") < 0)
	{
		perror("remove sem2 fail");
	}
}

int main(int argc, char **argv)
{
	key_t key;
	int shmid;
	char *shmaddr = NULL;
	sem_t *sem1 = NULL;
	sem_t *sem2 = NULL;
	int ret;

#if 1
	//首先通过ftok申请一个IPC的key
	key = ftok("./1", 4);
	if((int)key < 0)
	{
		perror("creat a ipc key fail");
		return -1;
	}
	printf("key value:%d\n", (int)key);
#endif

	//申请一块共享内存
	shmid = shmget(key, 100, IPC_CREAT|0666);	
	if(shmid < 0)
	{
		perror("creat a shm fail");
		return -1;
	}

	//映射到本用户进程
	shmaddr = shmat(shmid, NULL, 0);
	if((long)shmaddr < 0)
	{
		perror("shm attach fail");
		return -1;
	}

	//创建一个posix有名信号量
	sem1 = sem_open("sem1", O_CREAT|O_RDWR, 0666, 1);
	if(sem1 == SEM_FAILED)
	{
		perror("sem1 open fail");
		return -1;
	}
	
	//创建一个posix有名信号量
	sem2 = sem_open("sem2", O_CREAT|O_RDWR, 0666, 0);
	if(sem2 == SEM_FAILED)
	{
		perror("sem2 open fail");
		return -1;
	}
	
	while(1)
	{
		//P操作,等待信号量有效
		sem_wait(sem1);

		if(fgets(shmaddr, 11, stdin) != shmaddr)
		{
			perror("get char from stdin fail");
			return -2;
		}

		//V操作,释放信号量
		sem_post(sem2);

		if(strncmp("quit", shmaddr, 4) == 0)
		{
			printf("p1 ready toquit\n");
			break;
		}
	}
	
	//释放资源
	free_resource(shmaddr, shmid, sem1, sem2);
	
	return 0;
}

程序p2.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <error.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>


int main(int argc, char **argv)
{
	key_t key;
	int shmid;
	char *shmaddr = NULL;
	sem_t *sem1 = NULL;
	sem_t *sem2 = NULL;
	char read_buf[100] = {0};

#if 1
	//首先通过ftok申请一个IPC的key
	key = ftok("./1", 3);
	if((int)key < 0)
	{
		perror("creat a ipc key fail");
		return -1;
	}
#endif
	
	//申请一块共享内存
	shmid = shmget(key, 100, IPC_CREAT|0666);	
	if(shmid < 0)
	{
		perror("creat a shm fail");
		return -1;
	}

	//映射到本用户进程
	shmaddr = shmat(shmid, NULL, 0);
	if((long)shmaddr < 0)
	{
		perror("shm attach fail");
		return -1;
	}

	//创建一个posix有名信号量
	sem1 = sem_open("sem1", O_CREAT|O_RDWR, 0666, 1);
	if(sem1 == SEM_FAILED)
	{
		perror("sem1 open fail");
		return -1;
	}
	
	//创建一个posix有名信号量
	sem2 = sem_open("sem2", O_CREAT|O_RDWR, 0666, 0);
	if(sem2 == SEM_FAILED)
	{
		perror("sem2 open fail");
		return -1;
	}
	
	while(1)
	{
		//P操作,等待信号量有效
		sem_wait(sem2);

		strncpy(read_buf, shmaddr, 10);
		printf("read info from shm:%s\n", read_buf);
		
		//V操作,释放信号量
		sem_post(sem1);

		if(strncmp("quit", read_buf, 4) == 0)
		{
			printf("p2 ready toquit\n");
			break;
		}
	}
	
	//释放资源
	
	return 0;
}

4.调试过程

新建一个普通文件,用于产生key

touch 1

 

编译程序:

gcc p1.c -o p1 -pthread
gcc p2.c -o p2 -pthread

打开两个终端,先运行p1,再运行p2

在p1所在终端输入数据,回车,效果如下:

 ​​​

 从上图可以看出,p1发送了数据,p2接收到了数据并打印出来。

最后在p1终端输入“quit”,退出进程;p2收到“quit”也退出进程,如下图:

5.参考资料

 关于共享内存的api函数如何使用,可以参考下面博主的文章:

版权声明:本文为CSDN博主「艾特号」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lpwsw/article/details/121932268

总结

以上就是今天要讲的内容,本文介绍了system-V的IPC对象和它的特点,并简要介绍了共享内存实现进程通信的案例。

关于共享内存操作的内容,今后再补充。

标签:p2,IPC,共享内存,间通信,p1,key,linux,include
来源: https://www.cnblogs.com/lxyjrx/p/16226555.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有