当前位置: 首页 > news >正文

理解进程间通信

目录

  • 1.理解层面
    • 1.1为什么要进程间通信??
    • 1.2什么是通信??
    • 1.3怎么通信??
    • 1.4 具体通信的方式??
  • 2.管道
  • 3.匿名管道
    • 3.1 用fork来共享管道
    • 3.2 特性
    • 3.3 通信情况
  • 4.命名管道
    • 4.1 创建一个命名管道
    • 4.2 匿名管道和命名管道的区别
  • 5.System V 共享内存
    • 5.1 共享内存函数

1.理解层面

1.1为什么要进程间通信??

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事情(如进程终止时要通知父进程)
  • 进程控制:有些进程希望完全控制另一个进程的执行(Debug进程)

1.2什么是通信??

进程间通信是指在计算机系统中,运行在不同进程之间的数据交换和通信机制。由于每个进程都有自己的独立内存空间,因此进程间通信需要通过特定的机制来实现数据的共享和传递

1.3怎么通信??

进程间通信的本质:是先让不同的进程,先看到同一份资源[“内存”]

1.4 具体通信的方式??

  • 基于文件的,管道通信
    • 匿名管道pipe
    • 命名管道
  • System V ,本机通信
    • System V 消息队列
    • System V 共享内存
    • System V 信号量

2.管道

什么是管道??

  • 管道是Unix中最古老的进程间通信的形式
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
    在这里插入图片描述

3.匿名管道

#include<unistd.h>
int pipe(int pipefd[2]);
参数:
	pipefd:文件描述符数组,其中pipefd[0]表示读端,pipefd[1]表示写端
	返回值:成功返回0,失败返回-1
//列子:查看pipefd对应的文件描述符
//testPipe.cc

#include <iostream>
#include <unistd.h>

int main()
{
    //1.创建管道
    int pipefd[2]={0};//pipefd[0] 读端  pipefd[1] 写端
    int n=pipe(pipefd);
    if(n<0)
    {
        perror("pipe fail");
        exit(1);
    }

    std::cout<<"pipefd[0]: "<<pipefd[0]<<std::endl;
    std::cout<<"pipefd[1]: "<<pipefd[1]<<std::endl;
}

//运行结果:
$ ./testPipe
pipefd[0]: 3
pipefd[1]: 4

3.1 用fork来共享管道

在这里插入图片描述

//测试样例:测试管道的读写
//让父进程读,子进程写
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>

void ChildWrite(int wfd)
{
    char buff[1024];
    int cnt=0;
    while(true)
    {
        snprintf(buff,sizeof(buff),"I am child,my pid:%d,cnt: %d\n",getpid(),cnt++);
        write(wfd,buff,strlen(buff));
        sleep(1);
    }
}
void FatherRead(int rfd)
{
    char buff[1024];
    while(true)
    {
        buff[0]=0;
        ssize_t n=read(rfd,buff,sizeof(buff)-1);
        if(n>0)
        {
            buff[n]=0;
            std::cout<<"father say: "<<buff<<std::endl;
        }
        sleep(1);
    }
}

int main()
{
    // 1.创建管道
    int pipefd[2] = {0}; // pipefd[0] 读端  pipefd[1] 写端
    int n = pipe(pipefd);
    if (n < 0)
    {
        perror("pipe fail");
        exit(1);
    }

    std::cout << "pipefd[0]: " << pipefd[0] << std::endl;
    std::cout << "pipefd[1]: " << pipefd[1] << std::endl;

    // 2.创建子进程
    pid_t id =fork();
    if(id<0)
    {
        perror("fork fail");
        exit(1);
    }
    else if(id==0)
    {
        //child
        //3.关闭不需要的读端,形成通信
        close(pipefd[0]);
        ChildWrite(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }
    else
    {
        //father
        //3.关闭不需要的写端,形成通信
        close(pipefd[1]);
        FatherRead(pipefd[0]);
        close(pipefd[0]);
    }
}

//查看运行结果:
$ ./testPipe
pipefd[0]: 3
pipefd[1]: 4
father say: I am child,my pid:1670938,cnt: 0

father say: I am child,my pid:1670938,cnt: 1

father say: I am child,my pid:1670938,cnt: 2

father say: I am child,my pid:1670938,cnt: 3

father say: I am child,my pid:1670938,cnt: 4

father say: I am child,my pid:1670938,cnt: 5
...

3.2 特性

  • 匿名管道,只能用来进行具有血缘关系的进程进行进程间通信(常用于父子)
  • 管道文件,自带同步机制
  • 管道是单向通信的
  • (管道)文件的生命周期,是随进程的
  • 管道是面向字节流的,意味着数据以字节为单位字管道中传输

3.3 通信情况

  • 写慢,读快 —读端就要阻塞(进程)

  • 写快,读慢 —满了的时候,写就要阻塞等待
    在这里插入图片描述

  • 写关,继续读 —read就会读到返回值为0,表示文件结尾

  • 读关闭,写继续 —写端在写入没有任何意义,OS会杀掉写端进程,发送异常信号

4.命名管道

  • 让2个不相关的进程之间交换数据,可以使用FIFO文件来做这个工作,它叫做命名管道
  • 命名管道是一种特殊类型的文件

4.1 创建一个命名管道

# 从命令行上创建
$ mkfifo filename

#从程序里创建
int mkfifo(const char *pathname, mode_t mode);

4.2 匿名管道和命名管道的区别

  • 匿名管道由pipe函数创建并打开
  • 命名管道由mkfifo函数创建,open打开
  • 匿名管道只能在具有血缘关系的进程里进行进程间通信
  • 命名管道可以用来进行不相关的进程的进程间通信

5.System V 共享内存

  • 共享内存是进程间通信中,速度最快的方式(优点):
    • 映射之后,读写,直接被对方看到
    • 不需要进行系统调用获取或者写入
  • 共享内存没有保护机制(缺点):
    • 通信双方,没有所谓的“同步机制”
    • 数据不一致
      在这里插入图片描述

5.1 共享内存函数

  • shmget函数
功能:用来创建共享内存
原型:
	int shmget(key_t key, size_t size, int shmflg);
参数:
	key:这个共享内存段名字
	size:共享内存大小
	shmflg:权限标志
		取值IPC_CREAT:共享内存不存在,创建并返回;共享内存已存在,获取并返回
		取值IPC_CREAT|IPC_EXCL:共享内存不存在,创建并返回;共享内存已存在,出错返回
返回值:
	成功返回一个非负整数,即共享内存标识码;失败返回-1
  • shmat函数
功能:将共享内存段连接到进程地址空间
原型:
	void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
	shmid:共享内存标识
	shmaddr:虚拟地址,固定地址进行挂接
	shmflg:它的2个可能取值是SHM_RND和SHM_RDONLY
返回值:
	成功返回起始的虚拟地址;失败返回-1
  • shmdt函数
功能:将共享内存段与当前进程脱离
原型:
	int shmdt(const void *shmaddr);
参数:
	shmaddr:由shmat所返回的指针
返回值:
	 成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
  • shmctl函数
功能:用于控制共享内存
原型: 	
	int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
	shmid:由shmget返回的共享内存标识码
	cmd:要采用的行动
	buf:指向一个保存着共享内存模式状态和访问权限的数据结构
返回值:
	成功返回0;失败返回-1
命令(cmd)说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置成shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

相关文章:

  • ANSYS Swan 条件激活与重置操作符教程
  • 目标在哪里?——寻找人生的意义与方向
  • 【虚幻C++笔记】引擎源码下载及编译步骤
  • Kubernetes之ETCD
  • 即插即用模块--KANLinear
  • c++的static和java的有何不同和联系
  • pywinauto自动安装python和java
  • 小程序配置
  • 企业数据管理的成本与效率革命
  • 社区版Uos20.9从源码编译QT5.15.2
  • 阿里云企业邮箱出现故障怎么处理?
  • 算法日记40:最长上升子序列LIS(单调栈优化)n*log^n
  • 如何通过Odoo 18采购模块优化管理供应商价格表
  • [CISCN 2022 初赛]ezpop(没成功复现)
  • Java多线程与高并发专题——原子类和 volatile、synchronized 有什么异同?
  • Mac中nvm切换node版本失败,关闭终端再次打开还是之前的node
  • Protobuf 学习与实践
  • JVM--垃圾回收
  • 网络空间安全(32)Kali MSF基本介绍
  • 【DevOps】 基于数据驱动的Azure DevOps案例实现
  • 男子服用头孢后饮酒应酬致昏迷在家,救援人员破门施救后脱险
  • 新华社千笔楼:地方文旅宣传应走出“魔性尬舞”的流量焦虑
  • 女子应聘文员被说“太丑”?官方回应:有关部门启动核查处置
  • 竞彩湃|英超欧冠悬念持续,纽卡斯尔诺丁汉能否拿分?
  • 李洋谈美国黑帮电影与黑帮文化
  • 北方首场高温将进入鼎盛阶段,江南华南多地需警惕降雨叠加致灾