C++实现进程之间的通信(一)FIFO篇

本文最后更新于:2022年7月9日 下午

C++实现进程之间的通信(一)FIFO篇

一,C++ 常用进程间通信

管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。

命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。

信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。

消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

今天我们要研究的就是其中的FIFO实现进程之间的通信。

二、无名FIFO的实现

请看代码:

/*
    2022-3-17
    测试无名管道实例
    作者:连思鑫
*/
#pragma once
#include <stdio.h>
#include <iostream>
#include <fcntl.h>	
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#include "main.h"

using namespace std;

int main()
{
    int _pipe[2] = { 0, 0 };
    int ret = pipe(_pipe); //创建无名管道,参数返回写和读文件操作符

    if (ret == -1)
    {
        cout << "cerate pipe fail" << endl;
        return 1;
    }

    cout << "create pipe:" << _pipe[0] << _pipe[1] << endl;

    //fork一个子进程出来
    pid_t cid = fork();
    if (cid < 0)
    {
        cout << "fork fail!" << endl;
        return 2;
    }

    //测试子进程写——>父进程读消息
    if (cid == 0)
    {
        cout << "Child Writing...." << endl;
        close(_pipe[0]);
        //发送数据到pipe
        int count = 5;
        const char* msg = "I love you !!!";
        while (count--)
        {
            write(_pipe[1], msg, strlen(msg));
            sleep(1);
        }
        close(_pipe[1]);
        exit(1); //退出子进程
    }
    else
    {
        cout << "father reading......" << endl;
        close(_pipe[1]);
        // 读取pipe数据
        char msg[1024];
        int count = 5;
        while (true)
        {
            ssize_t n = read(_pipe[0], msg, sizeof(msg) - 1);
            if (n > 0)
            {
                msg[n] = '\0';
                cout << "recive from chil: " << msg << endl;
            }
            else
            {
                cout << "read emoty!" << endl;
                break;
            }
        }

        //*等子进程结束后在退出父进程
        //if (waitpid(cid, 0, 0) != -1)
        //{
        //	cout << "child closed!" << endl;
        //}
    }
    return 0;
}

   

三、管道的建立与通信的实现

直接看代码吧,我都写了注释….

发送端头文件 fifo_s.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>

发送端实现fifo_s.cpp

/*
    2022-3-18
    测试有名管道fifo发送端实例
    作者:连思鑫
*/

#include "fifo_s.h"

using namespace std;

int main()
{
    string name = ".myfifo";

    //创建一个存取权限为0666的命名管道
    unlink(name.c_str());  //name.c_str()是将string类型转换成char类型,因有的函数只能打开char类型命名的文件。unlink是删除文件,删除之前会检查链接,如果有链接则不删
    int namepipo = mkfifo(name.c_str(),S_IFIFO | 0666); // mkfifo是创建管道对象

    if (namepipo == -1)
    {
        perror("mkfifo fail!\n"); //创建失败
        exit(1);
    }

    //只写的方式打开命名的管道
    int fd = open(name.c_str(), O_WRONLY); //O_WRONLY是以只写的方式打开
    // int fd = open(name.c_str(), O_RDONLY); //只读
    // int fd = open(name.c_str(), O_RDWR); //可读可写等。。。

    if (fd == -1)
    {
        perror("open fifo fail!\n");  //打开失败
        exit(2);
    }

    // 向管道发送数据
    char buf[1024]; //缓存,用于存放发送数据的
    while (1)
    {
        printf("sendto fifo:");
        //该语句整体作用:一般在printf后加,强制马上输出缓冲区的内容,防止输出错误。
        fflush(stdout); //fflush作用,清空文件缓冲区,如果文件是打开的则把缓冲区内容写入文件 ||stdout:与标准输出流关联,用于写入约定的输出。程序启动时,该流为完全缓冲当且仅当能确定流不引用交互式设备
        ssize_t n = read(0, buf, sizeof(buf) - 1); //从标准输入获得消息,读取文件,字节数大小-1,循环读取字节
        if (n > 0)
        {
            buf[n - 1] = '\0';  //过滤掉换行符
            if (write(fd, buf, n) == -1)
            {
                perror("write fifo fail!");
                exit(3);
            }
        }

    }
    close(fd);

    return 0;

}

接受端头文件fifo_u.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>

   
接收端实现fifo_u.cpp

  /*
      2022-3-18
      测试有名管道fifo接受端实例
      作者:连思鑫
  */

  #include "fifo_u.h"

  using namespace std;

  int main()
  {
      string name = ".myfifo";

      int fd = open(name.c_str(), O_RDONLY);

      if (fd == -1)
      {
          perror("open fifo fail!");
          exit(1);
      }

      //接受管道数据
      char buf[1024];
      while (1)
      {
          ssize_t s = read(fd, buf, sizeof(buf) - 1);
          if (s >0 )
          {
              printf("receive from fifo: %s\n", buf);
          }
          else
          {
              perror("read fifo fail");
              exit(2);
          }
      }
      close(fd);

      return 0;
  }

C++实现进程之间的通信(一)FIFO篇
https://jinbilianshao.github.io/2022/03/23/用C-实现FIFO的功能/
作者
连思鑫
发布于
2022年3月23日
许可协议