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

进程间通信

  • 一.基本概念
  • 二.简单的通信-管道(匿名管道)
    • 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,就可以看到日志已经写入。

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spark shuffle 剖析

ShuffleExchangeExec private lazy val writeMetrics SQLShuffleWriteMetricsReporter.createShuffleWriteMetrics(sparkContext)private[sql] lazy val readMetrics SQLShuffleReadMetricsReporter.createShuffleReadMetrics(sparkContext)用在了两个地方&#xff0c;承接的是…

禁止安装新软件怎么设置(超详细图文介绍)

很多公司的网管向我们反应&#xff0c;总是有员工随意下载软件&#xff0c;并且不去正规网站、正规官网下载&#xff0c;导致公司的电脑总是又卡又慢&#xff0c;网管的工作很难开展。 此时就需要对公司安装软件的情况&#xff0c;进行统一管控了。 方法一&#xff1a;适合个人…

Git - 版本控制系统

目录 一、概述 配置用户信息 二、Git仓库 创建 本地仓库 git的三个区域 示例 Git文件状态 举例 三、区域使用 暂存区使用 版本库使用 文件忽略 四、分支 步骤 合并与删除 步骤 合并与提交 合并冲突 五、常用指令 六、Git远程仓库 使用步骤 克隆 同步 …

一键合并多个TXT文本,将保存在TXT的快递单号进行一键合并

如果你需要处理大量的TXT文本文件&#xff0c;那么你可能会遇到需要将这些文件合并为一个文件的情况。这不仅涉及到文件的组织和管理&#xff0c;还可能涉及到文件内容的连贯性和完整性。现在&#xff0c;我们有一个强大的工具&#xff0c;可以帮助你轻松实现一键文件整理&…

身份证号码校验

根据《新版外国人永久居留身份证适配性改造要点》&#xff0c;公司需要把代码中对身份证的校验进行优化 就文档内容可以看到需要优化的要点是&#xff1a; 新版永居证号码以 9 开头 受理地区代码出生日期顺序码校验码&#xff1b;&#xff08;共18位&#xff09; eg&#xff…

2023年约特干故城夜间演艺《万方乐奏有于阗》完美谢幕

11月19日&#xff0c;记者走进约特干故城看到演员在欢乐地跳着刀郎舞和古典舞&#xff0c;庆祝今年以来夜间演艺《万方乐奏有于阗》演出200场完美谢幕。 11月19日在约特干故城&#xff0c;演员正在表演迎宾乐舞。阿卜力克木依卜拉依木摄 当天晚上&#xff0c;城楼上旌旗猎猎&am…

Transmit v5.10.3(FTP客户端)

Transmit 5是一款由Panic开发的功能强大的FTP(文件传输协议)客户端软件&#xff0c;专为 macOS 平台设计。它提供了简单、直观的界面和丰富的功能&#xff0c;使用户能够轻松地管理和传输文件。 在文件传输和同步方面&#xff0c;Transmit 5提供了强大的文件同步功能&#xff…

18张值得收藏的高清卫星影像

这里分享的18张高清卫星影像&#xff0c;由吉林一号卫星拍摄。 原图来自长光卫星嘉宾在直播中分享的PPT演示文档。 18张高清卫星影像 吉林一号高分04A星&#xff0c;于2022年05月21日拍摄的北京紫禁城高清卫星影像。 北京紫禁城 云南昆明滇池国际会展中心高清卫星影像&…

【STM32外设系列】JW01三合一空气质量检测模块

&#x1f380; 文章作者&#xff1a;二土电子 &#x1f338; 关注公众号获取更多资料&#xff01; &#x1f438; 期待大家一起学习交流&#xff01; 文章目录 一、JW01模块简介二、数据格式介绍三、程序设计3.1 串口初始化3.2 串口接收中断服务函数3.3 数据解析函数 四、其他…

思福迪 运维安全管理系统 test_qrcode_b 远程命令执行漏洞

思福迪 运维安全管理系统 test_qrcode_b 远程命令执行漏洞 一、漏洞描述二、漏洞影响三、网络测绘四、漏洞复现1.手动复现2.自动化复现3.python源代码 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任…

OOM问题排查+Jvm优化

OOM问题排查&#xff1a; 1、top命令&#xff1a;查看cpu和内存的使用情况。 2、jstat命令&#xff1a;查看YGC和FGC情况&#xff0c;一般都是老年代不够用。导致OOM 3、jmap命令&#xff1a; 查看哪个类的实例过多,以每个类占用多少了内存。4、jstack 查看线程与线程之间的阻…

【广州华锐互动】昆虫3D虚拟动态展示:探索神奇的微观世界

在这个充满科技魅力的时代&#xff0c;我们可以通过各种方式去了解和探索自然界的奥秘。而昆虫作为地球上最为丰富多样的生物群体之一&#xff0c;其独特的生活习性和形态特征一直吸引着人们的目光。 由广州华锐互动开发的昆虫3D虚拟动态展示系统&#xff0c;成为了一种全新的科…

原始类型 vs. 对象(基本类型 vs. 引用类型)

原始类型 首先我们先看一段代码&#xff1a; let age 30; let oldAge age; age 31; console.log(age); console.log(oldAge);在 JavaScript 中&#xff0c;原始类型的赋值是通过值复制的方式进行的&#xff0c;而不会相互影响。只有对象类型的值才是通过引用复制的方式进行…

【数据结构(三)】双向链表(2)

文章目录 1. 基本概念2. 管理双向链表的思路3. 代码实现 1. 基本概念 管理单向链表的缺点分析: ①单向链表&#xff0c;查找的方向只能是一个方向&#xff0c;而双向链表可以向前或者向后查找。     ②单向链表不能自我删除&#xff0c;需要靠辅助节点 &#xff0c;而双向…

基于springboot实现在线外卖平台系统项目【项目源码】

基于springboot实现在线外卖平台管理系统演示 Java技术 Java是由SUN公司推出&#xff0c;该公司于2010年被oracle公司收购。Java本是印度尼西亚的一个叫做爪洼岛的英文名称&#xff0c;也因此得来java是一杯正冒着热气咖啡的标识。Java语言在移动互联网的大背景下具备了显著的…

AUTOSAR实战篇:基于ETAS工具链的信息安全协议栈集成指南

AUTOSAR实战: 基于ETAS工具链的信息安全协议栈集成指南 前言 小T出品,必是精品! 手把手带你集成信息安全协议栈,你值得拥有! 正文 随着汽车信息安全的不断发展与完善,其在汽车电子领域如智能驾驶(ADAS),智能座舱等方向上不断被重视起来,越来越多的Tier1,主机厂都在全面…

LeetCode:2304. 网格中的最小路径代价(C++)

目录 2304. 网格中的最小路径代价 题目描述&#xff1a; 实现代码&#xff1a; dp&#xff08;dp有很多相似的经典题目&#xff0c;比较简单&#xff0c;不再给出解析&#xff09; 2304. 网格中的最小路径代价 题目描述&#xff1a; 给你一个下标从 0 开始的整数矩阵 grid …

中国信息通信研究院发布《中国金融科技生态白皮书》(2023)

加gzh“大数据食铁兽”&#xff0c;回复“20231122”&#xff0c;获取材料完整版 导读 本白皮书是中国信息通信研究院连续第六年针对金融科技领域的跟踪研究成果&#xff0c;聚焦过去一年来国内外金融科技领域新的发展情况&#xff0c;重点分析了中国金融科技产业、技术、市…

vue3组件化开发页面之渲染函数实现

文章目录 前言一、渲染机制虚拟 DOM渲染管线 二、渲染函数基本用法声明渲染函数Vnodes 必须唯一 三、页面使用渲染函数及组件配置总结如有启发&#xff0c;可点赞收藏哟~ 前言 组件化开发是目前开发的常态 本文记录页面拆分多个不同组件模块&#xff0c;然后再基于渲染函数实现…

智能交通收费RFID读写器在不停车收费(ETC)系统中的应用

随着公路收费规模的不断扩大&#xff0c;传统的人工收费效率低下&#xff0c;收费没有监督&#xff0c;导致票款流失严重甚至还有车辆非法逃票。为了解决这些问题&#xff0c;引入了RFID等多种技术的新型的收费系统-不停车收费(ETC)系统应运而生。 电子不停车收费系统(ETC)系统…