问题模型

  1. A、B两个进程通过管道通信,A 进程每次接收到的数据通过共享内存传递给A1进程显示,同理,B进程每次接收到的数据通过共享内存传递给B1进程显示;
  2. 对于A、B 进程,采用ctrl+c(实际为SIGINT信号)方式退出,A、B进程通过捕捉SIGINT信号注册信号处理函数进行资源清理,A1、B1进程手动关闭即可。

特别注意

  1. A、B通过管道通信,如果首先通过ctrl+c退出A进程,那么B进程的fifo1管道的写端会收到SIGPIPE信号而终止B进程,因此必须在B进程终止前清理掉被B占用的共享内存2,将共享内存2的引用计数减一,否则,当B1进程退出并清理共享内存2后,共享内存2的引用计数不为0,会导致共享内存2得不到释放;
  2. 为了解决前一个问题,A、B进程在启动后立即将各自的进程id通过管道发送给对方,并在各自的进程退出时向对方进程id发送SIGINT信号,触发对方进程进入信号处理接口执行资源回收工作;
  3. A和A1通过共享内存1通信,会从A进程和A1进程的虚拟地址空间分配一段连续的页映射到同一块连续的物理内存页上,这样A、A1两个进程都可以间接访问物理内存页,从而达到通信的目的,一般共享内存需要进行保护,读写不能同时进行,也不能同时进行写操作,共享内存省去了从内核缓冲区到用户缓冲区的拷贝,因此效率高。

编码与效果图

func.h:

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

processA.cpp:

#include "func.h"

int shmid;
int pidB; // 存放对端进程B的进程id号
char *p; // 共享内存指针 // 回收共享内存资源前先杀死对端进程,否则回收失败
void handle(int num)
{
kill(pidB, SIGINT);
shmdt(p);
int ret;
if(-1 == (ret=shmctl(shmid, IPC_RMID, NULL)))
{
perror("shmctl");
return (void)-1;
}
exit(0);
} int main(int argc, char **argv)
{
signal(SIGINT, handle);
if(-1 == (shmid=shmget(1234, 4096, IPC_CREAT|0666)))
{
perror("shmget");
return -1;
} if((char*)-1 == (p=(char*)shmat(shmid, NULL, 0)))
{
perror("shmat");
return -1;
} // 管道文件为单工通信方式,因此需要建立两条管道
// A进程通过管道文件fifo1的读端fdr读取B进程发送的数据
// A进程通过管道文件fifo2的写端fdw向B进程发送数据
int fdr, fdw;
if(-1 == (fdr=open("fifo1", O_RDONLY)) || -1 == (fdw=open("fifo2", O_WRONLY)))
{
perror("open fifo1 or open fifo2");
return -1;
} // 通信之前先通过管道互相告知对方自己的进程id
char s1[10] = {0};
char s2[10] = {0};
sprintf(s1, "%d\n", getpid());
write(fdw, s1, strlen(s1) - 1);
read(fdr, s2, strlen(s1) - 1);
pidB = atoi(s2);
printf("pipe connect success, A to A1 shmid:[%d], pidA:[%d], pidB:[%d]\n", shmid, getpid(), pidB); char buf[1024] = {0};
int ret;
fd_set rdset;
while(true)
{
FD_ZERO(&rdset);
FD_SET(0, &rdset);
FD_SET(fdr, &rdset);
if((ret=select(fdr+1, &rdset, NULL, NULL, NULL) > 0))
{
// fdr可读,则接收数据之后通过共享内存传给A1
if(FD_ISSET(fdr, &rdset))
{
bzero(buf, sizeof(buf));
if(read(fdr, buf, sizeof(buf)) > 0)
{
strncpy(p, buf, sizeof(buf));
}
else
{
break;
}
} // 标准输入可读,读出来传递给B进程
if(FD_ISSET(0, &rdset))
{
bzero(buf, sizeof(buf));
if(read(STDIN_FILENO, buf, sizeof(buf)) > 0)
{
write(fdw, buf, strlen(buf) - 1);
}
else
{
break;
}
}
}
} close(fdr);
close(fdw);
return 0;
}

processB.cpp:

#include "func.h"

int shmid;
int pidA; // 存放对端进程id
char *p; // 共享内存指针 // 回收共享内存资源前先杀死对端进程,否则回收失败
void handle(int num)
{
kill(pidA, SIGINT);
shmdt(p);
int ret;
if(-1 == (ret=shmctl(shmid, IPC_RMID, NULL)))
{
perror("shmctl");
return (void)-1;
}
exit(0);
} int main(int argc, char **argv)
{
signal(SIGINT, handle);
if(-1 == (shmid=shmget(1235, 4096, IPC_CREAT|0666)))
{
perror("shmget");
return -1;
} if((char*)-1 == (p=(char*)shmat(shmid, NULL, 0)))
{
perror("shmat");
return -1;
} // 管道文件为单工通信方式
// B进程通过管道文件fifo1的写端fdw向A进程发送数据
// B进程通过管道文件fifo2的读端fdr接收A进程的数据
int fdr, fdw;
if(-1 == (fdw=open("fifo1", O_WRONLY)) || -1 == (fdr=open("fifo2", O_RDONLY)))
{
perror("open fifo1 or open fifo2");
return -1;
} // 通信之前先通过管道互相告知对方自己的进程id
char s1[10] = {0};
char s2[10] = {0};
sprintf(s1, "%d\n", getpid());
write(fdw, s1, strlen(s1) - 1);
read(fdr, s2, strlen(s1) - 1);
pidA = atoi(s2);
printf("pipe connect success, B to B1 shmid:[%d], pidA:[%d], pidB:[%d]\n", shmid, pidA, getpid()); char buf[1024] = {0};
int ret;
fd_set rdset;
while(true)
{
FD_ZERO(&rdset);
FD_SET(0, &rdset);
FD_SET(fdr, &rdset);
if((ret=select(fdr+1, &rdset, NULL, NULL, NULL) > 0))
{
// fdr可读,则接收数据之后通过共享内存传给B1
if(FD_ISSET(fdr, &rdset))
{
bzero(buf, sizeof(buf));
if(read(fdr, buf, sizeof(buf)) > 0)
{
strncpy(p, buf, sizeof(buf));
}
else
{
break;
}
} // 标注输入可读,读出来传递给A进程
if(FD_ISSET(0, &rdset))
{
bzero(buf, sizeof(buf));
if(read(STDIN_FILENO, buf, sizeof(buf)) > 0)
{
write(fdw, buf, strlen(buf) - 1);
}
else
{
break;
}
}
}
} close(fdr);
close(fdw);
return 0;
}

processA1.cpp:

#include "func.h"

int main(void)
{
char buf[1024] = {0};
int shmid;
if(-1 == (shmid=shmget(1234, 4096, IPC_CREAT|0666)))
{
perror("shmget");
return -1;
} char *p;
if((char*)-1 == (p=(char*)shmat(shmid, NULL, 0)))
{
perror("shmat");
return -1;
} while(true)
{
if(!(strcmp(buf, p)))
{
continue;
}
else
{
// 共享内存有数据可读
bzero(buf, sizeof(buf));
strcpy(buf, p);
printf("I am A1, recv from A:[%s]\n", buf);
}
} if(-1 ==(shmctl(shmid, IPC_RMID, 0)))
{
perror("shmctl");
return -1;
} return 0;
}

processB1.cpp:

#include "func.h"

int main(void)
{
char buf[1024] = {0};
int shmid;
if(-1 == (shmid=shmget(1235, 4096, IPC_CREAT|0666)))
{
perror("shmget");
return -1;
} char *p;
if((char*)-1 == (p=(char*)shmat(shmid, NULL, 0)))
{
perror("shmat");
return -1;
} while(true)
{
if(!(strcmp(buf, p)))
{
continue;
}
else
{
// 共享内存有数据可读
bzero(buf, sizeof(buf));
strcpy(buf, p);
printf("I am B1, recv from B:[%s]\n", buf);
}
} if(-1 ==(shmctl(shmid, IPC_RMID, 0)))
{
perror("shmctl");
return -1;
} return 0;
}

回收资源

  1. 这里首先通过ctrl+c退出A进程,然后B进程收到SIGPIPE信号退出,A、B进程同时调用各自的信号处理函数回收资源,通过ipcs命令发现拥有者为root的共享内存资源的nattch都为1,分别被A1和B1占有。
  2. 然后手动关闭A1、B1进程,再次执行ipcs命令,发现拥有者为root的共享内存资源不存在,已经释放成功。
$ ipcs  # 查看共性内存资源数量

源码获取

本文所有源码链接

基于共享内存、信号、命名管道和Select模型实现聊天窗口的更多相关文章

  1. Mysql服务器相互作用的通讯协议包括TCP/IP,Socket,共享内存,命名管道

    MySQL实现了四种通信协议 TCP/IP协议,通常我们通过来连接MySQL,各种主要编程语言都是根据这个协议实现了连接模块 Unix Socket协议,这个通常我们登入MySQL服务器中使用这个协议 ...

  2. ACE框架 基于共享内存的分配器 (算法设计)

    继承上一篇<ACE框架 基于共享内存的分配器设计>,本篇分析算法部分的设计. ACE_Malloc_T模板定义了这样一个分配器组件 分配器组件聚合了三个功能组件:同步组件ACE_LOCK, ...

  3. (原创)[.Net] 进程间通信框架(基于共享内存)——SimpleMMF

    一.前言 进程间通信技术的应用非常广泛,在Windows下常用的实现方式有:管道.Socket.消息.本地文件.共享内存等,每种方式都有各自适应的场景. 在进行大数据交换时,最优的方式便是共享内存. ...

  4. Unix IPC之基于共享内存的计数器

    目的 本文主要实现一个基于共享内存的计数器,通过父子进程对其访问. 本文程序需基于<<Unix网络编程-卷2>>的环境才能运行.程序中大写开头的函数为其小写同名函数的包裹函数, ...

  5. ACE框架 基于共享内存的进程间通讯

    ACE框架将基于共享内存的进程间通讯功能,如其它IO组件或IPC组件一样,设计成三个组件.流操作组件ACE_MEM_Stream,连接器组件ACE_MEM_Connector,以及接收连接组件ACE_ ...

  6. ACE框架 基于共享内存的分配器

    ACE框架提供了一个内存分配器模板,并且提供了(仅且)一个模板实例,基于共存内存的内存分配器.这个共存内存分配器模板实例在ACE框架应用于,基于内存映射的进程通讯,以及进程间同步等. ACE内存分配器 ...

  7. 撸代码--linux进程通信(基于共享内存)

    1.实现亲缘关系进程的通信,父写子读 思路分析:1)首先我们须要创建一个共享内存. 2)父子进程的创建要用到fork函数.fork函数创建后,两个进程分别独立的执行. 3)父进程完毕写的内容.同一时候 ...

  8. Linux进程间通信方式--信号,管道,消息队列,信号量,共享内存

    1.概述 通信方法 无法介于内核态与用户态的原因 管道(不包括命名管道) 局限于父子进程间的通信. 消息队列 在硬.软中断中无法无阻塞地接收数据. 信号量 无法介于内核态和用户态使用. 内存共享 需要 ...

  9. Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

    Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...

随机推荐

  1. 39 (guava包)AbstractScheduledService的使用

    package com.da.tool.guava; import com.google.common.util.concurrent.AbstractScheduledService; import ...

  2. 更新Android Studio,提示后直接点更新即可。gradle 两种更新方法,我用的第二种:手动添加gradle

    直接更新即可. 更新完毕后,随即会让你更新gradle,但是会一直更新一直更新...... 解决方法: 第一种方法: 手动下载Android Studio 对应的 gradle版本,然后设置一下即可. ...

  3. Spring Boot整合Mybatis(注解方式和XML方式)

    其实对我个人而言还是不够熟悉JPA.hibernate,所以觉得这两种框架使用起来好麻烦啊. 一直用的Mybatis作为持久层框架, JPA(Hibernate)主张所有的SQL都用Java代码生成, ...

  4. 三 基于Java动态数组手写队列

    手写队列: package dataStucture2.stackandqueue; import com.lt.datastructure.MaxHeap.Queue; import dataStu ...

  5. 越南FCK批量拿站

    关键词:inurl:detail_product.asp?lang= /FCKeditor/_samples/asp/sample01.asp/FCKeditor/_samples/asp/sampl ...

  6. layui-简单的登录注册界面

    register.html 源代码: <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  7. 4专题总结-图论和DFS、BFS

    1图论: 1.1  133. Clone Graph https://leetcode.com/problems/clone-graph/#/description 思路:这题可以对照拷贝随机链表那道 ...

  8. Metasploit学习笔记——移动环境渗透测试

    书364页配置假冒AP步骤,因为没有无线网卡,先跳过这个实验.

  9. 「USACO08JAN」电话线Telephone Lines

    传送门 Luogu 解题思路 考虑二分,每次把大于二分值的边的权设为1,小于等于的设为0,如果最短路<=k则可行,记得判无解 细节注意事项 咕咕咕 参考代码 #include <algor ...

  10. 104、Java中String类之使用indexOf()等功能查找

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...