【Linux】进程通信篇Ⅰ:管道进程 pipe、mkfifo、unlink

文章目录

  • 一、匿名管道
    • 1. 创建管道 pipe
    • 2. 管道的特点
    • 3. 四种场景
  • 二、命名管道
    • 1. Linux命令:mkfifo (命名管道的创建)
    • 2. 函数 mkfifo
    • 3. 函数 unlink
    • 4. 命名管道代码案例

进程间通信:Inter-Process Communication,简写做 IPC

一、匿名管道

1. 创建管道 pipe

头文件:

#include 
#include 

int pipe(int pipefd[2])

输出型参数:

  • 带回两个 文件描述符
  • 0 下标为 读端,助记:0 - -> 嘴巴 - -> 读书
  • 1 下标为 写端,助记:1 - -> 笔 - -> 写东西

返回值

  • 成功返回 0,失败返回 -1

2. 管道的特点

  1. 单向通行(管道是半双工的一种特殊情况)

  2. 管道的本质是文件,因为 fd 的生命周期随进程,所以管道的生命周期也是随进程的

  3. 管道通信,通常用来进行具有 “血缘关系”的进程,进行进程件通信。常用于 父子间通信 - - 使用 pipe 打开管道(匿名管道,并不清楚管道的名字)

  4. 在管道通信中,写入的次数 和 读取的次数,不是严格匹配的。读写没有强相关 - - 字节流
    管道 是面向 字节流 的

  5. 管道具有一定的协同能力,让 reader 和 writer 能够按照一定的步骤进行通信(管道自带 同步机制

3. 四种场景

  1. 如果我们 read 读取完毕所有管道的数据,如果对方不发,我们就只能等待

  2. 如果我们 write 端将管道写满了,就不能写啦

  3. 如果关闭了写端,读取完毕管道数据,再读,就会 read 返回 0,表明读到了文件结尾

  4. 写端一直再写,读端关闭,没有意义。OS 不会维护无意义、低效率、或者浪费资源的事。故,OS 会通过信号,来终止进程 13)SIGPIPE

二、命名管道

让不同的进程通过 文件路径 + 文件名 看到同一个文件并打开,就是看到了同一个资源,也即具备了进程通信的前提。

1. Linux命令:mkfifo (命名管道的创建)

mkfifo [管道名]:创建命名管道
创建出来的文件类型以 p 开头

-:普通文件
d:目录文件(document)
l:链接文件(link)
p:管道文件(pipeline)

2. 函数 mkfifo

头文件:

#include 
#include 

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

参数 pathname:

  • 文件路径


参数 mode:

  • 创建文件的权限,这里的权限会被系统的umask影响,所以我们若不想被系统的默认掩码影响,可以使用 函数 umask(0)

返回值:

  • 成功返回 0,失败返回 -1

3. 函数 unlink

头文件:

#include 

int unlink(const char *path);

参数 pathname:

  • 需要删除的文件路径

返回值:

  • 成功返回 0,失败返回 -1

4. 命名管道代码案例

举例实现:利用命名管道,实现两个进程的连接。client 端输入,server 端同步实现接收。

makefile

.PHONY:all
all:server client

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

.PHONY:clean
clean:
	rm -f server client

comm.hpp

#pragma once

#include 
#include 

#define NUM 1024

const std::string fifoname = "./fifo";
uint32_t mode = 0666; 

server.cc

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


int main()
{
    // 1. 创建管道文件,一次创建即可
    umask(0); // 这个设置并不影响系统的默认配置,只会影响当前进程
    int n = mkfifo(fifoname.c_str(), mode);
    if(n != 0)
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 1;
    }
    std::cout << "管道文件已创建" << std::endl;
    
    // 2. 让服务端直接开启管道文件
    int rfd = open(fifoname.c_str(), O_RDONLY);
    if(rfd < 0 )
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 2;
    }
    std::cout << "成功打开管道文件,开始 ipc(进程间通信)" << std::endl;

    // 3. 正常通信
    char buffer[NUM];
    while(true)
    {
        buffer[0] = 0;
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
        if(n > 0)
        {
            buffer[n] = 0;
            //std::cout << "client# " << buffer << std::endl;
            printf("%c", buffer[0]);
            fflush(stdout);
        }
        else if(n == 0)
        {
            std::cout << "client 已经退出,我是 server,也退出了" << std::endl;
            break;
        }
        else 
        {
            std::cout << errno << " : " << strerror(errno) << std::endl;
            break;
        }
    }

    // 关闭不要的fd
    close(rfd);
	// 删除管道文件
    unlink(fifoname.c_str());

    return 0;
}

client.cc

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

using namespace std;

int main()
{
    //1. 不需创建管道文件,只需要打开对应的文件即可(文件就是两个进程看到的相同的资源)
    int wfd = open(fifoname.c_str(), O_WRONLY);
    if(wfd < 0)
    {
        cerr << errno << ":" << strerror(errno) << endl;
        return 1;
    }

    // 2. 开始通信
    char buffer[NUM];
    while(true)
    {
    	// version(1)回车发送
        // cout << "请输入你的消息# ";
        // char *msg = fgets(buffer, sizeof(buffer), stdin);
        // assert(msg);
        // (void)msg;

        // version(2)不用进行回车就可以确认输入
        system("stty raw");     // 使终端驱动处于一次一字符模式
        int c = getchar();
        system("stty -raw");    // 使终端驱动回到一次一行模式

        //buffer[strlen(buffer) - 1] = 0;   // 解决输入的\n
        //if(strcasecmp(buffer, "quit") == 0) break;    // 写端退出,读端就也一起退出了
                                                        // strcasecmp 忽略大小写的字符串比较(带 n 是strncasecmp)

        ssize_t n = write(wfd, (char*)&c, sizeof(char));
        assert(n >= 0);
        (void)n;
    }

    close(wfd);
    return 0;
} 

你可能感兴趣的:(Linux,linux,运维,服务器)