【Linux】:进程间通信和日志模拟

news2025/1/11 7:57:15

进程间通信

  • 一.基本概念
  • 二.简单的通信-管道(匿名管道)
    • 1.建立通信信道
    • 2.通信接口
  • 三.命名管道
  • 三.模拟命名管道通信(加上日志)
    • 1.完整代码
    • 2.基本使用

一.基本概念

是什么

两个或多个进程实现数据层面的交互。

因为进程独立性的存在,导致进程间的通信成本比较高。

为什么

因为我们有多进程协同的需求。

怎么办

a.进程间通信的本质:必须让不同的进程看到同一份"资源"。
b.“资源”?特定形式的内存空间。
c.这个"资源"谁提供?一般是操作系统。
d.我们进程访问这个空间,进行通信,本质就是访问操作系统!进程代表的就是用户,“资源”从创建,使用(一般),释放―–需要系统调用接口!一般操作系统会有一个独立的通信模块-隶属于文件系统-IPC通信模块。

二.简单的通信-管道(匿名管道)

管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。

在这里插入图片描述

1.建立通信信道

管道是一个文件-内存级文件。

1.管道文件与一般文件不同,一般文件存在于磁盘里,管道文件存在于内存里。也就是说管道文件不需要将修改内容从内存刷新缓冲区到磁盘,这是它的特点。

2.一般管道文件只能具有血缘关系的进程间通信。因为只有具有血缘关系才能继承同一份files_struct。

3.一个父进程在创建管道文件时不能只是以读或者写的方式,必须两者都有。操作系统会把这个文件打开两次,分别用来读和写。但操作系统实际上只想让两个进程进行单向通信,因为如果一个进程又在读又在写,很容易会造成数据混淆,为了避免麻烦,规定只能一个进程写,另一个进程读。

在这里插入图片描述

这个文件不需要有名字,inode…让操作系统区分。所以这种文件也被称为匿名管道。

2.通信接口

在这里插入图片描述

pipe的作用就是帮助我们以读和写打开文件。它的参数是一个输出型参数,它会把分别以读和写的文件的文件描述符通过参数带出,供用户使用。pipefd[0]一般用于读,pipefd[1]一般用于写。

模拟

makefile

testPipe:TestPipe.cpp
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f testPipe

TestPipe.cpp(一个简单的通信,子进程向父进程里写信息)

#include<stdio.h>
#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<string>
#include<cstdio>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>

using namespace std;
#define N 2
#define NUM 1024

//子进程
void Write(int wfd)
{
  //任意写几个数据测试
  string s="hello,i am a child";
  pid_t id=getpid();
  int num=0;

  char buffer[NUM];
  while(1)
  {
    snprintf(buffer,sizeof(buffer),"%s-%d-%d\n",s.c_str(),id,num++);//将数据都变成字符存在buffer里
    
    //把数据写入管道
    write(wfd,buffer,strlen(buffer));
    sleep(1);
  }
}

//父进程
void Read(int rfd)
{
  char buffer[NUM]={0};
  while(1)
  {
    ssize_t n=read(rfd,buffer,sizeof(buffer));
    if(n>0)
    {
      cout<<buffer<<endl;
    }
    else if(n==0) break;
  }
  
}

int main()
{
  int pipefd[N]={0};
  //创建管道
  int n=pipe(pipefd);
  //判断是否创建成功
  if(n<0) return 1;
  
  //创建子进程
  pid_t id=fork();
  if(id<0) return 2;
  if(id==0)
  {
    //子进程
    //关闭读功能
    close(pipefd[0]);

    //IPC code
    Write(pipefd[1]);

    //退出
    close(pipefd[1]);
    exit(0);
  }
  //父进程
  //关闭写功能
  close(pipefd[1]);

  //IPC code
  Read(pipefd[0]);
  //退出

  //回收子进程
  pid_t rid=waitpid(id,nullptr,0);
  if(rid<0) return 3;
  close(pipefd[0]);
  return 0;
}

在这里插入图片描述

管道的4中情况:
1.读写端正常,管道如果为空,读端就要阻塞 2读写端正常,管道如果被写满,写端就要阻塞 3.读端正常读,写端关闭,读端就会读到0,表明读到了文件(pipe)结尾,不会被阻塞
4,写端正常写入,读端被关闭。操作系统就要杀掉正在写入的进程。如何干掉?通过信号杀掉。

管道的特征:
1.具有血缘关系的进程进行进程间通信。
2.管道只能单向通信。
3.父子进程是会进程协同的,同步与互斥的—保护管道文件的数据安全 4.管道是面向字节流的。
5.管道是基于文件的,而文件的生命周期是随进程的!

三.命名管道

很明显上面的匿名管道只使用于具有血缘关系的通信是远远不够的,为了解决这个问题,又有了命名管道的概念。

如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件。

makefile

在这里插入图片描述

创建一个命名管道命名为myfifo

在这里插入图片描述

三.模拟命名管道通信(加上日志)

1.完整代码

日志(log.hpp)

#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>


#define SIZE 1024

#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4

#define Screen 1
#define Onefile 2
#define Classfile 3

#define LogFile "log.txt"

class Log
{
public:
  Log()
  {
    //初始化打印方式和路径
    printMethod=Screen;
    path="./log/";
  }

  ~Log()
  {}

  //改变打印方式
  void Enable(int methed)
  {
    printMethod=methed;
  }

  //把整形转换成字符串
  std::string levelToString(int level)
  {
    switch (level)
    {
    case Info:
        return "Info";
    case Debug:
        return "Debug";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Fatal:
        return "Fatal";
    default:
        return "None";
    }
  }
  void printOneFile(const std::string &logname, const std::string &logtxt)
  {
    std::string _logname=path+logname;//连接文件路径
    int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);//打开文件
    if (fd < 0) return;

    //向文件里写入
    write(fd, logtxt.c_str(), logtxt.size());
    close(fd); 
  }
  
  void printClassFile(int level,const std::string &logtxt)
  {
    std::string filename=LogFile+'.'+levelToString(level);//拼接路径
    printOneFile(filename,logtxt);
  }
  
  void printLog(int level,const std::string &logtxt)
  {
    //选择打印方式
    switch (printMethod)
    {
    case Screen://向屏幕打印
      std::cout << logtxt << std::endl;
      break;
    case Onefile://向一个文件里打印
      printOneFile(LogFile, logtxt);
      break;
    case Classfile://向多个文件里打印
      printClassFile(level, logtxt);
      break;
    default:
      break;
    }
  }


  //重载括号,让其能像log s; s(...)一样使用
  void operator()(int level,const char*format, ...)
  {
    //首先将时间加入日志
    time_t t=time(nullptr);
    struct tm*ctime=localtime(&t);
    char leftbuffer[SIZE];
    snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

    //接着将用户输入部分加入

    //处理可变参数
    va_list s;
    va_start(s,format);
    char rightbuffer[SIZE];
    vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
    va_end(s);
    
    //将两个部分合并
    char logtxt[2*SIZE];
    snprintf(logtxt,sizeof(logtxt),"%s %s\n",leftbuffer,rightbuffer);

    //打印到对应位置
    printLog(level, logtxt);
  }
private:
  int printMethod;
  std::string path;
};

创建管道(comm.hpp)

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define FIFO_FILE "./myfifo"
#define MODE 0664

enum
{
  FIFO_CREATE_ERR = 1,
  FIFO_DELETE_ERR,
  FIFO_OPEN_ERR
};

class Init
{
public:
  Init()
  {
    // 创建管道
    int n = mkfifo(FIFO_FILE, MODE);
    if (n == -1)
    {
        perror("mkfifo");
        exit(FIFO_CREATE_ERR);
    }
  }
  ~Init()
  {

    int m = unlink(FIFO_FILE);
    if (m == -1)
    {
        perror("unlink");
        exit(FIFO_DELETE_ERR);
    }
  }
};

管道通信读取方(server.cc)

#include "comm.hpp"
#include "log.hpp"

using namespace std;

// 管理管道文件
int main()
{
  Init init;
  Log log;
  // log.Enable(Onefile);
  log.Enable(Onefile);

  // 打开管道
  int fd = open(FIFO_FILE, O_RDONLY); // 等待写入方打开之后,自己才会打开文件,向后执行, open 阻塞了!
  if (fd < 0)
  {
    log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
    exit(FIFO_OPEN_ERR);
  }

  log(Info, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
  log(Warning, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
  log(Fatal, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
  log(Debug, "server open file done, error string: %s, error code: %d", strerror(errno), errno);


  // 开始通信
  while (true)
  {
      char buffer[1024] = {0};
      int x = read(fd, buffer, sizeof(buffer));
      if (x > 0)
      {
        buffer[x] = 0;
        cout << "client say# " << buffer << endl;
      }
      else if (x == 0)
      {
        log(Debug, "client quit, me too!, error string: %s, error code: %d", strerror(errno), errno);
        break;
      }
      else
        break;
  }

  close(fd);
  return 0;
}

管道通信写入方(client.cc)

#include <iostream>
#include "comm.hpp"

using namespace std;

int main()
{
  int fd = open(FIFO_FILE, O_WRONLY);
  if(fd < 0)
  {
    perror("open");
    exit(FIFO_OPEN_ERR);
  }

  cout << "client open file done" << endl;

  string line;
  while(true)
  {
      cout << "Please Enter@ ";
      getline(cin, line);

      write(fd, line.c_str(), line.size());
  }

  close(fd);
  return 0;
}

makefile

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^ -g -std=c++11
	mkdir log
client:client.cc
	g++ -o $@ $^ -g -std=c++11

.PHONY:clean
clean:
	rm -f server client
	rm -r log

2.基本使用

上面代码是向log.txt文件里写入。我们首先运行读文件,再运行写文件。任意通信,然后关闭进程,再打开log.txt,就可以看到日志已经写入。

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【前段基础入门之】=>CSS3新特性 BFC

什么是BFC( 概念) W3C 上对 BFC 的定义&#xff1a; MDN 上对 BFC 的定义&#xff1a; 简而言之 开启了BFC能解决什么问题 元素开启 BFC 后&#xff0c;其子元素不会再产生 margin 塌陷问题元素开启 BFC 后&#xff0c;自己不会被其他浮动元素所覆盖元素开启 BFC 后&#xff0…

【Java】多线程-wait/notify

1、wait和notify Java的多线程中&#xff0c;线程的执行顺序和时间都是不定的。为了控制线程的调度顺序&#xff0c;前面我们引入了join()方法。 但是join()只能在线程执行完后&#xff0c;才能执行其他线程&#xff0c;有没有什么方法可以在线程执行顺序中来调度其他线程呢&…

代码随想录 Day50 单调栈 LeetCodeT503 下一个最大元素II T42接雨水

前言 前面我们说到了单调栈的第一题,下一个最大元素I,其实今天的两道题都是对他的变种,知道第一个单调栈的思想能够想清楚,其实这道题是很简单的 考虑好三个状态,大于等于小于,其实对于前面这些题目只要细心的小伙伴就会发现其实小于和等于的处理是一样的都是直接入栈,只有大于…

灵魂拷问std::enable_shared_from_this,揭秘实现原理

灵魂拷问std::enable_shared_from_this&#xff0c;揭秘实现原理 引言 在C编程中&#xff0c;使用智能指针是一种安全管理对象生命周期的方式。std::shared_ptr是一种允许多个指针共享对象所有权的智能指针。然而&#xff0c;当一个对象需要获取对自身的shared_ptr时&#xff0…

中贝转债上市价格预测

中贝转债-113678 基本信息 转债名称&#xff1a;中贝转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;5.17亿元。 正股名称&#xff1a;中贝通信&#xff0c;今日收盘价&#xff1a;49.2元&#xff0c;转股价格&#xff1a;32.8元。 当前转股价值 转债面值…

JavaScript中的random小案例

前言 Math对象是JavaScript的内置对象&#xff0c;而random是Math对象属性 Math.random&#xff08;&#xff09;函数返回一个浮点数&#xff0c;伪随机数在范围从0 到小于1&#xff0c;也就是说&#xff0c;从 0&#xff08;包括 0&#xff09;往上&#xff0c;但是不包括 1&a…

Python编程技巧 – 使用字典

Python编程技巧 – 使用字典 Python Programming Skills – Using Dictionary Dictionary, 即字典&#xff0c;这是Python语言的一种重要的数据结构&#xff1b;Python字典是以键&#xff08;key&#xff09;值(value)对为元素&#xff0c;来存储数据的集合。 前文提到Python列…

春秋云境靶场CVE-2022-30887漏洞复现(任意文件上传漏洞)

文章目录 前言一、CVE-2022-30887描述和介绍二、CVE-2021-41402漏洞复现1、信息收集2、找可能可以进行任意php代码执行的地方3、漏洞利用找flag 总结 前言 此文章只用于学习和反思巩固渗透测试知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;不能随…

【电路笔记】-星三角变换(Star-Delta Transformation)

星三角变换&#xff08;Star-Delta Transformation&#xff09; 文章目录 星三角变换&#xff08;Star-Delta Transformation&#xff09;1、概述1.1 单相配置1.2 多相配置 2、三相连接2.1 Y配置2.2 Δ配置 3、Y-Δ 和 Δ-Y 变换3.1 Y-Δ变换3.2 Δ-Y变换3.3 应用 4、总结 本文…

使用ssh在本地环境(Windows)连接虚拟机以及其中的docker容器

配置虚拟机防火墙 防火墙的一系列操作需要root权限&#xff0c;默认是没有root密码的&#xff0c;所以首先需要设置root密码&#xff1a; sudo passwd root按提示完成root密码设置 切换到root账户 su root启用22端口并重启防火墙 firewall-cmd --permanent --add-port22/tc…

【zabbix监控四】zabbix之监控tomcat服务报警

一、监控tomcat服务是否正常运行 1、客户端部署 首先要在zabbix-agent客户端上安装tomcat服务&#xff0c;并能正常启动和关闭 1.1 客户端编写脚本 vim /opt/tomcat.sh#!/bin/bash anetstat -natp |grep 8080|awk {print $6}|grep LISTEN if [[ $a LISTEN ]];thenecho &qu…

【目标测距】雷达投影测距

文章目录 前言一、读取点云二、点云投影图片三、读取检测信息四、点云投影测距五、学习交流 前言 雷达点云投影相机。图片目标检测&#xff0c;通过检测框约束等等对目标赋予距离。计算消耗较大&#xff0c;适合离线验证操作。在线操作可以只投影雷达检测框。 一、读取点云 py…

HandBrake :MacOS专业视频转码工具

handbrake 俗称大菠萝&#xff0c;是一款免费开源的视频转换、压缩软件&#xff0c;它几乎支持目前市面上所能见到的所有视频格式&#xff0c;并且支持电脑硬件压缩&#xff0c;是一款不可多得的优秀软件 优点 ∙Windows, Linux, Mac 三平台支持 ∙开源、免费、无广告 ∙支…

SSM2

DataSource mybatis与Spring整合 事务加载业务层上面 开启事务驱动 上面都是声明式开启事务 图书管理系统 命名规范: java命名规范:驼峰命名法类:大驼峰变量,属性名.方法名:小驼峰 常量使用下划线分割:全大写,单词与单词之间下划线分割数据库命名规范:常用命名规范:下划线…

Python将原始数据集和标注文件进行数据增强(随机仿射变换),并生成随机仿射变换的数据集和标注文件

Python将原始数据集和标注文件进行数据增强&#xff08;随机仿射变换&#xff09;&#xff0c;并生成随机仿射变换的数据集和标注文件 前言前提条件相关介绍实验环境生成随机仿射变换的数据集和标注文件代码实现输出结果 前言 由于本人水平有限&#xff0c;难免出现错漏&#x…

基于单片机K型热电偶温度采集报警系统

**单片机设计介绍&#xff0c; 基于单片机K型热电偶温度采集报警系统 文章目录 一 概要简介系统特点系统组成工作原理应用领域 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于单片机K型热电偶温度采集报警系统介绍 简介 该系统是基于单片…

Unity--互动组件(Scrollbar)||Unity--互动组件(DropDown )

此组件中的&#xff0c;交互&#xff0c;过渡&#xff0c;导航与文章&#xff08;Unity--互动组件&#xff08;Button&#xff09;&#xff09;中的介绍如同&#xff1b; handle rect&#xff1a;&#xff08;父节点矩形&#xff09; 用于控件的滑动“句柄”部分的图形&#xf…

Harmony Ble 蓝牙App (一)扫描

Harmony Ble 蓝牙App &#xff08;一&#xff09;扫描 前言正文一、创建工程二、工程配置① 权限配置② Debug配置③ UI配置 三、扫描① 扫描接口② 扫描类 四、业务处理① Slice的生命周期② 蓝牙开关和动态权限请求 五、扫描设备六、显示设备① 自定义蓝牙类② 提供者③ 显示…

【机器学习】037_暂退法

一、实现原理 具有输入噪音的训练&#xff0c;等价于Tikhonov正则化 核心方法&#xff1a;在前向传播的过程中&#xff0c;计算每一内部层的同时注入噪声 从作用上来看&#xff0c;表面上来说是在训练过程中丢弃一些神经元 假设x是某一层神经网络层的输出&#xff0c;是下一…

​EMNLP 2023 findings | 生成式框架下解决输入扰动槽填充任务

©PaperWeekly 原创 作者 | 回亭风 单位 | 北京邮电大学 研究方向 | 自然语言理解 论文标题&#xff1a; DemoNSF: A Multi-task Demonstration-based Generative Framework for Noisy Slot Filling Task 论文链接&#xff1a; https://arxiv.org/abs/2310.10169 代码链接…