问题模型

  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. Codeforces 1304D. Shortest and Longest LIS

    根据题目,我们可以找最短的LIS和最长的LIS,找最短LIS时,可以将每一个increase序列分成一组,从左到右将最大的还未选择的数字填写进去,不同组之间一定不会存在s[i]<s[j]的情况, ...

  2. smoj2806建筑物

    题面 有R红色立方体,G绿色立方体和B蓝色立方体.每个立方体的边长是1.现在有一个N × N的木板,该板被划分成1×1个单元.现在要把所有的R+G+B个立方体都放在木板上.立方体必须放置在单元格内,单 ...

  3. W3C网页标准

    W3C标准是一些列标准的集合!在各个企业的基础上做一定的整合,形成一套标准集,便于推广和使用! 根据网页主要由三部分组成:结构(Structure).表现(Presentation)和行为(Behav ...

  4. div 浮动

    浮动 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <titl ...

  5. tomcat点击startup.bat出现闪退,启动不成功的解决办法

    问题描述:tomcat点击startup.bat出现命令行闪退的情况 打开startup.bat,在第一行加入 SET JAVA_HOME=D:\jdk\jdk1.8.0_121[jdk路径] SET ...

  6. Linux CentOS7 VMware linux和windows互传文件、用户配置文件和密码配置文件、用户组管理、用户管理

    一. linux和windows互传文件 X-shell.Securecrt远程终端,与Windows之间互传文件. 安装一个工具lrzsz [root@davery ~]# yum install ...

  7. wyh的dp入门刷题笔记

    0: 靠前感觉之前dp抄题解都是抄的题解,自己从没有真正理解过dp.wyh下了很大决心从头学dp,于是便有了这篇文章. 1.背包 前四讲01背包&多重背包&完全背包(混合背包) :樱花 ...

  8. mabatis--动态sql

    1.mybatis核心,对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接.组装: 2.使用if判断: <where> <if test="customer ...

  9. IP、TCP、DNS

    负责传输的IP协议 按层次分,IP网际协议位于网络层.几乎所有使用网络的系统都会用到 IP 协议. “IP”和“IP地址不同”,“IP”是一种协议的名称.IP 协议的作用是把各种数据包传送给对方.而要 ...

  10. ng-校验重复并提示具体重复内容

    //校验其他等级模块是否存在"职业类别"完全一致的等级模块 var moreFlag=false; for(var i=0;i<$scope.djArr.length;i++ ...