进程的通信——管道和共享内存

news2024/11/6 17:29:10

进程间的通信有很多种
管道

匿名管道pipe

命名管道

System V IPC

System V 消息队列

System V 共享内存

System V 信号量

POSIX IPC

消息队列

共享内存

信号量

互斥量

条件变量

读写锁
这篇文章主要介绍管道和共享内存

管道

管道内核数据结构:在Linux2.6中 struct file里有 const struct cred *f_cred;cred里面有struct inode,inode里有union(联合体),union里就一个字段是struct pipe_inode_info,这个就代表管道文件

struct pipe_inode_info {
	wait_queue_head_t wait;
	unsigned int nrbufs, curbuf;
	struct page *tmp_page;
	unsigned int readers;
	unsigned int writers;
	unsigned int waiting_writers;
	unsigned int r_counter;
	unsigned int w_counter;
	struct fasync_struct *fasync_readers;
	struct fasync_struct *fasync_writers;
	struct inode *inode;
	struct pipe_buffer bufs[PIPE_BUFFERS];
};

什么是管道

管道是Linux中很重要的一种进程间通信方式,是把一个进程的输出直接连接到另一个进程的输入

管道分为:

  • 匿名管道

  • 命名管道

管道特点

  1. 都是单向的。(进程间的通信管道,单向的,传输数据的)
  2. 管道传输资源的
    现在不理解没关系,等下看图就能理解原理了

匿名管道

匿名管道接口

#include <unistd.h>

功能:创建一无名管道
原型

int pipe(int fd[2]);

参数

fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

匿名管道原理
管道是进程间通信的一种重要的方式,那么进程通信其实就是让进程看到同一份资源,匿名管道的方式,就是让一个进程打开文件,称为管道文件吧,用结构体struct file来描述,然后用分配两个文件描述符指向这个文件,这个代表读端,一个代表写端;然后让这个进程创建子进程,子进程会继承这个文件描述符,所以子进程也有两个文件描述符指向这个管道文件,然后根据业务需求关闭一方的读端,一方的写端(因为管道是单向的);这样两个进程就可以通过这个管道文件进行通信了
在这里插入图片描述
匿名管道只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创 建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
具体父进程是怎么创建管道和子进程来产生联系的,我们来看图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

匿名管道接口的使用

#include <iostream>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>

using namespace std;

// 匿名管道通信的基本流程
int main()
{
    // 1.创建管道
    int pipefd[2] = {0};
    if (pipe(pipefd) != 0)
    {
        cout << "pipe error" << endl;
        return 1;
    }
    cout << "pipefd[0]:" << pipefd[0] << endl;
    cout << "pipefd[1]:" << pipefd[1] << endl;

    // 2.创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        cout << "fork error" << endl;
        return 2;
    }
    else if (id == 0)
    {
        // child
        // 我们约定让子进程进行读取,父进程进行写
        close(pipefd[1]);
        char buffer[1024];
        while (true)
        {
            memset(buffer, 0, sizeof(buffer));
            ssize_t s = read(pipefd[0], buffer, sizeof(buffer)-1);
            if (s > 0)
            {
                buffer[s] = '\0';
                cout << "子进程收到消息:" << buffer << endl;
            }
            else if (s == 0)
            {
                // 父进程关闭写端
                cout << "父进程关闭写端" << endl;
                break;
            }
            else
            {
                // do nothing
            }
        }
        close(pipefd[0]);
        exit(0);
    }
    else
    {
        // parent
        // 关闭读端
        close(pipefd[0]);
        // 发送5次消息
        int cnt = 0;
        string msg = "I am your parent";
        while (cnt < 5)
        {
            write(pipefd[1], msg.c_str(), msg.size());
            sleep(1);
            cnt++;
        }
        close(pipefd[1]);
        cout << "父进程写完了" << endl;
    }
    
    pid_t res = waitpid(id, nullptr, 0);
    if (res > 0)
    {
        cout << "等待子进程成功" << endl;
    }
    return 0;
}

匿名管道特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创 建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务 ,没有格式边界,需要用户来定义区分内容的边界
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程 (管道是文件 —— 进程退出了,曾经打开的文件会怎么办? —— 打开的文件就会退出 —— 所以管道的生命周期是随进程)
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

命名管道

刚才讲的是有血缘关系进程才能用匿名管道通信,但是我想让两个没有关系的进程通信呢?这个时候,就要用到命名管道了。
命名管道的原理跟匿名管道类型,区别就是会在磁盘创建实体文件.fifo;但是命名管道文件创建完,打开后,不会往磁盘写数据,只是内存级别;磁盘上的.fifo文件相当于一个符号,让多个进程可以找到它,然后用直接的文件描述符表指向struct_file(.fifo),这样就能构成通信了

命名管道接口函数

int mkfifo(const char *filename,mode_t mode);

函数的使用

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ICP_PATH "./.fifo"

using namespace std;

int main()
{
    if(mkfifo(ICP_PATH,0600) != 0)
    {
        cout << "mkfifo error" << endl;
        return 1;
    }
    cout << "服务器启动" << endl;
    int fd = open(ICP_PATH,O_RDONLY);
    if(fd < 0)
    {
        cout << "open error" << endl;
        return 2;
    }
    char msg[1024];
    while(true)
    {
        memset(msg,0,sizeof(msg));
        ssize_t s = read(fd,msg,sizeof(msg)-1);  
        if(s > 0)
        {
            cout << "客户端发来消息:" << msg << endl;
        }
        else if(s == 0)
        {
            cout << "客户端退出" << endl;
            break;
        }
        else
        {
            //do nothing
        }
    }
    close(fd);
    //服务器退出删除管道文件,不然下一次运行,会出错
    unlink(ICP_PATH);
    return 0;
}
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ICP_PATH "./.fifo"



using namespace std;

int main()
{
    int fd = open(ICP_PATH,O_WRONLY);
    if(fd < 0)
    {
        cout << "open error" << endl;
        return 2;
    }
    char line[1024];
    while(true)
    {
        cout << "请输入你的消息: ";
        fflush(stdout);
      if(fgets(line,sizeof(line),stdin) != nullptr)
       {
            //line[strlen(line) - 1] = '\0';
            //这里strlen(line) 为什么不加1 ,因为这是管道文件,'\0'是c语言的东西,不需要这个
            write(fd,line,strlen(line));
       }
       else
       {
            break;
       }
    }

    close(fd);
    return 0;
}

共享内存

什么是共享内存

共享内存也是进程间通信的一种方式,它让多个进程的虚拟地址空间映射到同一个块内存,实现通信。
共享内存是最快的IPC形式,因为一旦这样的内存映射到共享它的进程的地址空间,这样进程间数据传递不再说是进程不再通过执行进入内核的系统调用来传递彼此的数据
在这里插入图片描述
内核中共享内存的数据结构

struct shmid_ds {
 struct ipc_perm shm_perm; /* operation perms */
 int shm_segsz; /* size of segment (bytes) */
 __kernel_time_t shm_atime; /* last attach time */
 __kernel_time_t shm_dtime; /* last detach time */
 __kernel_time_t shm_ctime; /* last change time */
 __kernel_ipc_pid_t shm_cpid; /* pid of creator */
 __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
 unsigned short shm_nattch; /* no. of current attaches */
 unsigned short shm_unused; /* compatibility */
 void *shm_unused2; /* ditto - used by DIPC */
 void *shm_unused3; /* unused */
};



共享内存函数

shmget函数

功能:用来创建共享内存
原型

 int shmget(key_t key, size_t size, int shmflg);

参数

 key:这个共享内存段名字

 size:共享内存大小

 shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

功能:将共享内存段连接到进程地址空间
原型

 void *shmat(int shmid, const void *shmaddr, int shmflg);

参数

 shmid: 共享内存标识

 shmaddr:指定连接的地址

 shmflg:它的两个可能取值是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

shmget

//函数
int shmget(key_t key,size_t size,int shmflg);
 key:这个共享内存段名字

 size:共享内存大小

 shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

IPC_CREAT:创建共享内存,如果已经存在,就获取之;不存在,就创建之
IPC_EXCL:不单独使用,必须和IPC_CREAT 配合,如果不存在指定的共享内存,创建之,如果存在,出错返回(这样可以保证,如果当使用IPC_EXCL和IPC_CREXAT标记位时,如果shmget函数调用成功,一定是一个全新的share memory)

共享内存的基本编写

#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
using namespace std;

#define PATH_NAME "/home/XHBIN/_linux_code/cpp/systemV"

#define PROJ_ID 0X14

#define MEM_SIZE 1024

const int flags = IPC_CREAT | IPC_EXCL;

key_t CreateKey()
{
    key_t key = ftok(PATH_NAME,PROJ_ID);//形成key值
    if(key < 0)
    {
        cerr << "ftok: " <<  strerror(errno) << endl;
        exit(1);
    }
}

int main()
{
    key_t key = CreateKey();
    cout << "key: " << key << endl;
    //创建共享内存
    int shmid = shmget(key,MEM_SIZE,flags | 0666);
    if(shmid < 0)
    {
        cout << "shmget: " << strerror(errno) << endl;
        return 2;
    }
    cout << "create shm success shmid: " << shmid << endl;
    //将共享内存和自己的进程产生关联attach
    char *str = (char*) shmat(shmid,nullptr,0);
    int cnt = 0;
    while(cnt <= 26)
    {
        str[cnt] = 'A' + cnt;
        ++cnt;
        str[cnt] = '\0';
        cout << str << endl;
        sleep(1);
    }
    cout  << str << endl;
    //2.去关联
    shmdt(str);
    cout << "detach shm: " << shmid << "success" << endl;
    //删除
    shmctl(shmid,IPC_RMID,nullptr);
    cout << "delete shm: " << shmid << "success" << endl;
    return 0;

}

但是共享内存不推荐使用,因为它没有提供互斥同步机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/625093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建自动化测试环境

目录 1、安装Python并配置环境变量。2、安装Pycharm开发工具。3、安装Selenium4、安装浏览器&#xff1a;Chrome和Firefox的其中之一。5、浏览器驱动&#xff1a;下载Chrome浏览器驱动或者是Firefox浏览器驱动。6、配置webdriver公众号粉丝福利 自动化测试环境&#xff1a; Pyt…

接口反应慢优化

遇到某个功能&#xff0c;页面转圈好久&#xff0c;需要优化 1.F12 查看接口时间 2.看参数 总共耗时9.6s Waiting for sercer response 时间是2秒 Content Download 7秒 慢在Content Download F12查看接口响应 显示Failed to load response data:Request content was e…

这些10款优秀的交互设计软件,你知道吗?

交互软件可以帮助设计师从“可用性”和“用户体验”的角度优化他们的作品。如果设计师想创建一个令人满意的交互设计作品&#xff0c;一个方便的交互设计软件是必不可少的。 根据设计师的个人喜好和方便&#xff0c;选择易于使用的交互设计软件来完成创建。本文盘点十款易于使…

【P55】JMeter 图形结果(Graph Results)

文章目录 一、图形结果&#xff08;Graph Results&#xff09;参数说明二、准备工作三、测试计划设计 一、图形结果&#xff08;Graph Results&#xff09;参数说明 可以以图形的方式查看和分析相关指标 使用场景&#xff1a;一般在调试测试计划期间用来查看相关指标&#xf…

微信小程序基础使用-请求数据并渲染

小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象&#xff0c;不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…

Qt Quick-QML地图引擎之v4版本(新增3D模型/抗锯齿任意多边形下载)

在上个版本Qt quick-QML地图引擎之v4版本(新增3D模型/高德/谷歌在线/离线预览/多线程离线裁剪下载/区域查询/位置搜索/路径规划)_qt 高德地图离线_诺谦的博客-CSDN博客更新了很多小功能。经过朋友们一致需求建议&#xff0c;所以V4继续优化。 B站视频&#xff1a; Qt Quick-QML…

【办公效率提升】Window10与ubuntu递归列出当前目录及其所有子目录中的文件和文件夹

在Windows操作系统中&#xff0c;没有内置的类似于Ubuntu的"tree"命令的功能。但是&#xff0c;你可以使用以下两种方法来实现相似的功能&#xff1a; 方法一&#xff1a;使用dir命令和递归 你可以使用Windows的内置命令"dir"以及递归参数"/s"来…

前后端交互二、form表单与模板引擎

零、文章目录 前后端交互二、form表单与模板引擎 1、form表单的基本使用 HTML相关知识请参考HTML入门 &#xff08;1&#xff09;表单是什么 表单在网页中主要负责数据采集功能。HTML中的<form>标签&#xff0c;就是用于采集用户输入的信息的&#xff0c;并通过<…

linux PerfCollect收集日志及perfview分析

Perfview&#xff1a;https://github.com/Microsoft/perfview/releases PerfCollect&#xff1a;https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md Linux 环境中运行的 ASP.NET Core应用中收集跟踪 PerfCollect&#…

5.6.1 Ext JS之标签页的关闭和批零关闭

Tab Panel 是包含多个标签页的面板, 这是一种很常用的组件, 类似于浏览器的标签页。关于 Ext JS的Tab Panel的基本使用可以参考: [Ext JS3.9] 标签面板(TabPanel )介绍与开发, 本篇介绍如何关闭单个标签页和批量关闭标签页。 Tab 标签页的可关闭 默认状况下,标签页是无…

Bitmiracle Docotic.Pdf 9.015 Crack

Docotic.Pdf 库是正确的法语和强大的编程和界面&#xff0c;可以让用户和开发人员创建专业和高质量的 PDF 文件&#xff0c;甚至可以阅读和修改那些已经存在的。它具有干净而强大的编程接口&#xff0c;能够帮助用户创建质量非常好的 PDF 文档。在这个库的帮助下&#xff0c;用…

linux log

linux log 一:printk日志级别二:printk打印消息控制printkprintk消息等级查看与修改/etc/rsyslog.conf 一:printk日志级别 数字越小级别越高 二:printk打印消息控制 console_loglevel&#xff1a;只有当printk打印消息的log优先级高于console_loglevel时&#xff0c;才能输出…

C++编译一些常见的错误集锦

目录 1、段错误&#xff08;Segmentation Fault&#xff09; 2、强异常保证&#xff08;strong exception guarantee&#xff09; 3、有效但未定义的状态&#xff08;valid but unspecified state&#xff09; 1、段错误&#xff08;Segmentation Fault&#xff09; &#…

Material—— VAT(Houdini To UE)

目录 一&#xff0c;介绍 二&#xff0c;柔体 二&#xff0c;刚体 一&#xff0c;介绍 VAT是将动画数据存储在纹理中&#xff0c;通过GPU运算来实现动画的技术&#xff1b;VAT纹理包含每个顶点在不同帧的位置信息&#xff0c;而每个像素代表一个顶点在某个时间点的位置&…

Linux项目流程 + 用git将本地代码上传到gitee

目录 前言 一个"进度条"项目 git上传代码 首次上传代码 安装git 克隆仓库连接 将文件传入路径目录 文件上传三板斧 后续提交更新 git实用用法补充 前言 本文将介绍如何使用makefile编写项目并将其上传到git远程仓库。Makefile是一种用于自动化构建、编译…

Python爬虫:从后端分析为什么你爬虫爬取不到数据

仅仅是小编总结的三点而已&#xff0c;可能不是很全面&#xff0c;如果之后小编了解到新的知识点&#xff0c;可能还会增加的哈&#xff01; 1. 最简单的爬虫代码 也就是各位最常使用的&#xff0c;直接利用requests模块访问当前网站链接&#xff0c;利用相关解析模块从而获取…

第三届陕西省大学生网络安全技能大赛wp

文章目录 第三届陕西省大学生网络安全技能大赛wpwebezpoptestezrceunserializeEsc4pe_T0_Mong0 misc管道可是雪啊飘进双眼 第三届陕西省大学生网络安全技能大赛wp web ezpop 在源码找到base64 解码&#xff1a; /pop3ZTgMw.php&#xff0c;访问获得源码&#xff1a; <?…

通过帮助中心提高客户满意度,帮助中心的最佳实践方式

随着技术的不断发展和产品的不断更新&#xff0c;消费者对产品的需求也越来越高。在这个竞争激烈的市场中&#xff0c;企业必须建立一个完善的帮助中心&#xff0c;来提供及时、准确的技术支持和解决方案&#xff0c;以满足客户的各种需求。这样能够有效地提高客户满意度&#…

java+springboot8高校教职工教师档案管理系统

系统设计遵循界面层、业务逻辑层和数据访问层的Web开发三层架构。采用B/S结构,使得系统更加容易维护。系统的设计与实现主要实现角色有管理员和用户,管理员在后台管理用户表模块、token表模块、公告信息模块、教职工模块、工资信息模块、调动离职模块、配置文件模块、出勤信息模…