Linux下利用共享空间来实现两个没有亲缘关系的进程间通信

Linux下利用共享空间来实现两个没有亲缘关系的进程间通信

功能需求:
1.打开一个Linux命令窗口作为写入端->客户端(client)
2.打开另一个Linux命令窗口作为读取端->服务器(server)
3.在客户端(client)写入数据后服务器(server)可以读取到客户端信息并返回信息内容,如果客户端没有写入消息则服务器进入等待状态
4.用户输入quit进程结束

思路:
1.首先我们应该在客户端(client)和服务器(server)中创建一个可以共同使用的共享空间:
共享空间属于IPC对象中的一种,我们要实现两个没有亲缘关系的进程通信那就要求客户端和服务器传递的信息之间达成某种协议,这种协议可以使服务器收到客户端的写入内容,所以我们可以在共享空间创建时使用相同的key,这样客户端和服务器就可以访问同一个共享空间;

2.实现两个进程之间的同步:
共享空间里数据被读取时如果为空就会直接结束程序,所以为了使数据传输实现同步,就需要引入信号来实现;
(1)首先使用信号让两个进程互相获取到进程号(pid),这就需要在创建共享空间地址的时候使里面的数据包括进程的进程号(pid)和发送的数据区;
(2)然后当共享空间中没有数据时,服务器就等待客户端输入,当客户端输入消息后发送信号结束服务器的等待让服务器读取数据,服务器在读取数据时客户端进行等待,服务器读取完成后发送信号给客户端,此时客户端进行输入,服务器等待…然后依次循环进行从而实现进程之间的同步;

3.实现客户端(client)的输入功能和服务器(server)的接收读取功能:
定义一个全局的数据缓存区buf, 在客户端程序中先将数据写入到buf缓存区中, 再从缓存区传进共享空间,每次输入结束要将缓存区内容清空,方便下次输入;然后从服务器程序中去读取共享空间的内容;

实现步骤:

1.创建共享空间:
      它的组成信息有:
    	  提取码->key,  标识id号->shmid,  所有者->owner,  权限->perms,   大小->bytes,  共享进程数量->nattch;
     
      创建步骤所利用函数:
      	 1. 创建一个提取码ftok()
      	 2. 创建一个共享空间shmget()
      	 3. 设置共享空间映射内存shmat()
      	 4. 撤销共享内存映射shmdt()
      	 5. 删除一个共享内存空间shmctl()
      
      共享空间的地址类型:
      	typedef struct shm{
      		pid_t pid;
      		char buf[MAX];
      	}SHM;

    代码实现:
	```
	#include <sys/types.h>
	#include <sys/ipc.h>
	#include <sys/shm.h>
	
	#define SIZE 1024			//定义一个宏来表示共享空间的大小
	#define MAX 64				//定义一个宏来表示缓存区的大小
	
	typedef struct shm{			//定义共享空间里面需要包含的数据类型
       	    pid_t pid;
       	    char buf[MAX];
       	}SHM;

	int main()
	{
	    key_t  key;			//定义自己的共享空间的提取码
	    int shmid;				//定义一个共享空间的ID
	    SHM *shmaddr = NULL;		//定义共享空间的首地址

	    //创建一个提取码
	    if((key = ftok('.', 'z')) < 0)
	    {
	    	perror("ftok");			//创建失败就返回错误信息
	    	return  -1;
	    } 
	    
	    //创建一个共享空间
	    if((shmid = shmget(key, SIZE, IPC_CREAT  | 0666)) == -1)
	    {
	    	perror("shmget");
	    	return -1;
	    }

	    //设置共享空间的映射
	    if((shmaddr = shmat(shmid, NULL, 0)) == (SHM *)-1)		//-1的类型要和shmaddr类型一致需要进行类型强转
	    {
	    	perror("shmat");
	    	return -1;
	    }
	     
	    //删除共享空间
	    shmdt(shmaddr);
	    shmctl(shmid, IPC_RMID, NULL);
	```
	
2.实现两个进程之间同步:
	使用信号signal()函数来创建两个用户信号(SIGUSR)来向两个进程发送信号;
	用到的函数有:
		1.信号捕捉函数signal()
		2.等待信号函数pause()
		3.信号发送函数kill()
	
	信号捕捉函数用来注册用户信号,等待信号函数用来设定进程执行顺序,信号发送函数用来将信号发送给指定进程进行下一步操作;
这样使用三个信号函数就使两个进程之间产生同步通信;

    代码实现
	```
	client.c
		pid_t pid_server;
		
		//注册客户端信号
		signal(SIGUSR1, myfun);
		pid_server = shmaddr->pid;    		//获取服务器的PID				
		shmaddr->pid = getpid();		//把客户端的PID传到共享空间让服务器获取
		kill(pid_server, SIGUSR2);		//给服务器发信号

	server.c
		pid_t pid_client;
		
		//注册服务器信号
		signal(SIGUSR2, myfun);
		shmaddr->pid = getpid();		//把服务器的PID传到共享空间让客户端获取
		pause();				//等待客户端发送信号
		pid_client = shmaddr->pid;		//获取客户端的PID
	```
	
3.实现客户端(client)的输入功能和服务器(server)的接收读取功能:
     客户端:
	(1)使用fgets函数从标准输入获取到客户端输入的字符信息存入共享空间缓存区;
	(2)使用kill()函数给服务器发信号让它读取内容;
	(3)使用pause()函数等待服务器读取完毕的信号;
	(4)使用strncmp()函数来判断用户输入的退出信息结束来通信;
	(5)使用memset()函数来清空共享空间缓存区内容;
    服务器:
    	(1)使用pause()函数等待客户端输入消息完成的信号;
    	(2)使用strncmp()函数来判断用户输入的退出信息结束来通信;
    	(3)使用printf直接读取共享空间缓存区的内容;	
         (4)使用kill()函数给客户端发送信号表示读取完成;
         
    代码实现
	```
	client.c
		while(1)
		{
			printf("input>");
			fflush(stdout);
			fgets(shmaddr->buf, MAX, stdin);
			kill(pid, SIGUSR2);
			
			if(strncmp(shmaddr->buf, "quit", 4) == 0)
			      break;
			pause();
			memset(shmaddr->buf, 0, MAX);
		}

	server.c
		while(1)
		{
			pause();
			if(strncmp(shmaddr->buf, "quit", 4) == 0)
			      break;
			printf("read:%s", shmaddr->buf);
			kill(pid, SIGUSR1);
		}

	```

完整实验代码:

    客户端:
    client.c
    	#include <stdio.h>
    	#include <stdlib.h>
    	#include <string.h>
    	#include <sys/types.h>
    	#include <sys/ipc.h>
    	#include <sys/shm.h>
    	#include <unistd.h>
    	#include <signal.h>

	#define SIZE 1024
	#dedine MAX 64

	typedef struct shm{
	      pid_t pid;
	      char buf[MAX];
	}SHM;

	void myfun(int sig)
	{
	      return ;
	}

	int main()
	{
	      key_t key;
	      SHM *shmaddr = NULL;
	      int shmid;
	      pid_t pid_server;
	      
	      //创建一个提取码
	      if((key = ftok(".", 'z')) == -1)
	      {
	      	    perror("ftok");
	      	    return -1;
	      }
	      
	      //创建共享空间
	      if((shmid = shmget(key, SIZE, IPC_CREAT | 0666)) == -1)
	      {
	      	    perror("shmget");
	      	    return -1;
	      }
	      
	      //设置共享空间地址
	      if((shmaddr = shmat(shmid, NULL, 0)) == (SHM *)-1)
	      {
	      	    perror("shmat");
	      	    return -1;
	      }
	      
	      //注册信号
	      signal(SIGUSR1, myfun);
	      pid_server = shmaddr->pid;
	      shmaddr->pid = getpid();
	      kill(pid_server, SIGUSR2);
	      
	      //写入发送程序
	      while(1)
	      {
	      	    printf("input>");
	      	    fflush(stdout);
	      	    fgets(shmaddr->buf, MAX, stdin);
	      	    kill(pid_server, SIGUSR2);
	      	    if(strncmp(shmaddr->buf, "quit", 4) == 0)
	      	    	    break;
	      	    pause();
	      	    memset(shmaddr->buf, 0, MAX);
	      }
	      
	      //删除共享空间
	      shmdt(shmaddr);
	      shmctl(shmid, IPC_RMID, NULL);
	      return 0;
	}

服务器:
server.c
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	#include <sys/types.h>
	#include <sys/ipc.h>
	#include <sys/shm.h>
	#include <unistd.h>
	#include <signal.h>
	
	#define SIZE 1024
	#define MAX 64
	
	typedef struct shm{
	      pid_t pid;
	      char buf[MAX];
	}SHM;
	
	void myfun(int sig)
	{
	      return ;
	}
	
	int main()
	{
	      key_t key;
	      SHM *shmaddr = NULL;
	      int shmid;
	      pid_t pid_client;
	      
	      //创建一个提取码
	      if((key = ftok(".", 'z')) == -1)
	      {
	      	    perror("ftok");
	      	    return -1;
	      }
	      
	      //创建一个共享空间
	      if((shmid = shmget(key, SIZE, IPC_CREAT | 0666) == -1)
	      {
	      	    perror("shmget");
	      	    return -1;
	      }
	      
	      //设置共享空间地址
	      if((shmaddr = shmat(shmid, NULL, 0) == (SHM *)-1)
	      {
	      	    perror("shmat");
	      	    return -1;
	      }
	      
	      //注册信号
	      signal(SIGUSR2, myfun);
	      shmaddr->pid = getpid();
	      pause();
	      pid_client = shmaddr->pid;
	      
	      //读取程序
	      while(1)
	      {
	      	    pause();
	      	    if(strncmp(shmaddr->buf, "quit", 4) == 0)
	      	    	  break;
	      	    printf("read:%s\n", shmaddr->buf);
	      	    kill(pid_client, SIGUSR1);
	      }
	      
	      //删除共享空间
	      shmdt(shmaddr);
	      shmctl(shmid, IPC_RMID, NULL);
	      return 0;
	}

运行结果:
运行时先运行服务器在运行客户端!!!

服务器运行
客户端运行
本博客记录学习过程中的总结,可能会存在诸多不严谨和部分的理解错误,仅供参考,互相交流

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值