【Linux】【进程间通信】管道

进程间通信分类

复制代码
管道
System V进程间通信
POSIX进程间通信

管道

复制代码
匿名管道
命名管道

System V IPC

复制代码
System V 消息队列
System V 共享内存
System V 信号量

POSIX IPC

复制代码
消息队列
共享内存
信号量
互斥量
条件变量
读写锁

管道

是Unix中最古老的进程间通信的形式。

把从一个进程连接到另一个进程的一个数据流称为一个"管道"。

匿名管道

复制代码
#include <unistd.h>
功能:创建一个匿名管道
int pipe(int fd[2]);
参数:
fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端。
返回值:成功返回0,失败返回-1,错误码被设置。
cpp 复制代码
#include <iostream>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define N 2
#define NUM 1024

using namespace std;

void Writer(int wfd)
{
    string s="hello,I am a child";
    pid_t self=getpid();
    int number=0;

    char buffer[NUM];
    while(true)
    {
        sleep(1);
        buffer[0]=0;//字符串清空
        snprintf(buffer,sizeof(buffer),"%s-%d-%d",s.c_str(),self,number++);
        cout<<buffer<<endl;
        //发送/写入给父进程
        write(wfd,buffer,strlen(buffer));
    }
}

void Reader(int rfd)
{
    char buffer[NUM];
    while(true)
    {
        buffer[0]=0;
        ssize_t n=read(rfd,buffer,sizeof(buffer));
        if(n>0)
        {
            buffer[n]=0;
            cout<<"father get a message["<<getpid()<<"]#"<<buffer<<endl;
        }
        else if(n==0)
        {
            cout<<"father process read file done!"<<endl;
            break;
        }
        else 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;
    }
    else if(id==0)
    {
        //child
        close(pipefd[0]);//关闭读
        //IPC code
        Writer(pipefd[1]);

        close(pipefd[1]);
        exit(0);
    }
    //father
    close(pipefd[1]);
    Reader(pipefd[0]);
    pid_t rid=waitpid(id,nullptr,0);
    if(rid<0)
    return 3;
    close(pipefd[0]);
    sleep(5);
    return 0;
}

注意:

管道就是文件(内存级文件)

特点:

1.用管道通信时,通信的进程间需要有血缘关系,常用于父子。

2.单向通行。

3.父子进程是会进程协同的。同步与互斥------保护管道文件的数据安全。

4.管道是面向字节流的。

5.管道是基于文件的。而文件的生命周期是随进程的。
管道中的四种情况:

1.读写端正常,管道如果为空,读端就要阻塞。

2.读写端正常,管道如果被写满,写端就要阻塞。

3.读端正常,写端关闭,读端就会读到0,表示读到了管道文件的结尾,不会被阻塞。

4.写端正常写入,读端关闭了,操作系统要通过信号杀掉正在写入的进程。

命名管道

可用于毫不相关的进程间通信。

创建一个命名管道:

复制代码
mkfifo filename

也可以从程序里创建:

复制代码
int mkfifo(const char* filename,mode_t mode);
 int n=mkfifo(FIFO_FILE,0664);

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

复制代码
1.匿名管道由pipe函数创建并打开。
2.命名管道由mkfifo函数创建,打开用open
3.FIFO和pipe唯一区别是它们创建和打开的方式不同,一旦这些过程完成后,它们具有相同的语义。

例子:

common.hpp

cpp 复制代码
#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

cpp 复制代码
#include "common.hpp"
#include "log.hpp"
using namespace std;

// 管理管道文件
int main()
{
    Init init;
    Log log;
    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);
    }
    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,errno code:%d",strerror(errno),errno);
            break;
        }
        else break;
    }
    close(fd);//关闭管道
    return 0;
}

client.cc

cpp 复制代码
#include "common.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;
}

log.hpp

cpp 复制代码
#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/";
    }
    void Enable(int method)
    {
        printMethod = method;
    }
    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 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;
        }
    }
    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;
        filename+=".";
        filename+=levelToString(level);
        printOneFile(filename,logtxt);
    }
    ~Log()
    {}
    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[SIZE*2];
        snprintf(logtxt,sizeof(logtxt),"%s %s\n",leftbuffer,rightbuffer);
        printLog(level,logtxt);
    }
private:
    int printMethod;
    std::string path;
};
相关推荐
^乘风破浪^40 分钟前
Centos升级openssh及openssl
linux·运维·centos
赖small强41 分钟前
【Linux驱动开发】Linux EXT4文件系统技术深度解析与实践指南
linux·驱动开发·ext4·superblock·super block·block bitmap·inode bitmap
linux修理工42 分钟前
CentOS Stream 9 软件仓库 清华源
linux·运维·centos
naodianbozzz43 分钟前
nginx的https的搭建
运维·nginx·https
liefyuan44 分钟前
【嵌入式Linux】添加sshd (顺便dropbear--scp命令)
linux·运维
wasp5201 小时前
Hudi 元数据管理分析
java·大数据·linux·hudi·数据湖·数据湖仓
Han.miracle1 小时前
JavaEE--网络编程 传输层 (一) UDP TCP特点
运维·服务器·网络·java-ee·三次握手·四次挥手·超时重传
Gogo8161 小时前
docker 容器
运维·docker·容器
^乘风破浪^1 小时前
centos7离线升级openssh
linux