Linux学习笔记:进程间的通信.管道

什么是管道

管道是最简单的进程通信机制之一。它允许一个进程的输出直接成为另一个进程的输入。管道分为命名管道和命名管道(FIFO),匿名管道通常用于父子进程间通信,而命名管道也可以在非相关进程间通信。

管道的4种情况

  1. 正常情况(读写端正常)若管道中没有数据了,则读端必须等待,直到有数据为止
  2. 正常情况(读写端正常)若管道中被写满了,则写端必须等待,直到有空间为止,即数据被读走
  3. 写断关闭,读端一直读取,直到read返回值为0,即表示读到管道末尾
  4. 读端关闭,写端一直写入,此时写端的进程会直接被OS杀掉,因为写下去也就没有什么意义了,os通过向目标进程发送SIGPIPE(13)信号,终止目标进程

管道的5中特性

  1. 匿名管道:允许有血缘关系的进程之间进行进程通信 , 常用于父子进程 , 仅限于此
  2. 匿名管道,默认给读写端提供 同步机制(两个进程执行时具有一定的顺序性)
  3. 面相字节流,即无论写的时候是按多大写,按什么数据写, 都不影响读端想读多少就读多少
  4. 管道的生命周期是随进程的,进程结束,管道自动被操作系统回收
  5. 管道是单向通信的,半双工通信的一种特殊情况

匿名管道

含义

匿名管道通常通过 pipe() 系统调用创建,它返回两个文件描述符,一个用于读取管道数据,另一个用于写入管道数据。
然后,进程可以使用 read() 和 write() 等系统调用来实现数据的读取和写

特点

匿名管道有以下特点:

  • 单向通信:匿名管道是单向的,即数据只能从一个进程流向另一个进程,不能反向传输数据。
  • 半双工:匿名管道是半双工的,即数据只能在一个方向上进行传输,不能同时进行输入和输出。
  • 相关进程间通信:匿名管道通常用于父子进程或兄弟进程之间的通信,因为它们是通过 fork() 系统调用创建的进程共享同一个文件描述符。
  • 存储有限:匿名管道的数据传输是基于内存的,它的缓冲区是有限的,因此传输大量数据时需要注意防止缓冲区溢出。

代码示例

#include <iostream>
#include <cassert>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define MAX 1024

using namespace std;

int main()
{
    // 1. 建立管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0);

    (void)n; // 防止编译器报警
    cout << "pipefd[0]:" << pipefd[0] << "  " << "pipefd[1]" << pipefd[1] << endl;

    // 2. 创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        return 1;
    }

    // 3. 父子关闭不需要的读写fd,形成单向通信通道
    if (id == 0)
    {
        // child
        // 让子进程写入,关闭读端
        close(pipefd[0]);
        // 只向管道中写入
        int cnt = 10;
        while(cnt--)
        {
            //创建一个字符串池
            char message[MAX];
            snprintf(message,sizeof(message)-1, "hello father , i am child . pid : %d, cnt: %d \n" , getpid(), cnt);
            //将信息写入管道:
            write(pipefd[1],message , strlen(message));
            //sleep(1);
        }
        cout<<"write over , child close w"<<endl;
        close(pipefd[1]);
        exit(0);
    }

    // parent
    // 让父进程读取,关闭写端
    close(pipefd[1]);
    //接下来父进程开始读取管道中的内容:
    char buffer[MAX];
    while(true)
    {
        ssize_t n = read(pipefd[0],buffer,sizeof(buffer)-1);
        if(n>0)
        {
            buffer[n] = 0;
            cout<<getpid()<<": child say:"<< buffer <<"to me"<<endl;
        }
        else if(n == 0)
        {
            cout<<"child quit , me too!"<<endl; 
            break;
        }
    }


    pid_t rid = waitpid(id, nullptr, 0);
    if (rid == id)
    {
        cout << "wait success" << endl;
    }

    return 0;
}

命名管道

含义

在使用命名管道时,需要使用 mkfifo 命令来创建一个命名管道文件,然后可以像操作普通文件一样在进程间进行通信。
进程可以通过打开这个文件来实现数据的读取和写入。命名管道通常用于需要多个进程之间进行通信的场景,比如数据传输、进程协作等。

特点

命名管道有以下特点:

  • 基于文件系统:命名管道以文件的形式存在于文件系统中,通常位于 /tmp 或其他指定目录下。
  • 全双工:命名管道是全双工的,允许同时进行读取和写入操作。
  • 独立于进程:命名管道可以被多个进程访问,不像匿名管道那样限制在相关进程之间。
  • 持久性:命名管道在创建后可以持续存在,直到被显式删除。

代码示例

这个例子中,有两个可执行程序 client和server
这两个程序通过创建一个fifo文件,通过这个文件来进行通信
Makefile

.PHONY:all
all:client server

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

.PHONY:clean
clean:
	rm -f server client fifo

server.cc

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

#include "comm.h"


bool Makefifo()
{
    int n = mkfifo(FILENAME,0666);
    if(n < 0)
    {
        std::cerr << "errno" << errno << ",errstring" << strerror(errno) << std::endl;
        return false;
    }
    return true;
}

int main()
{
Start:
    int rfd = open(FILENAME,O_RDONLY);
    if(rfd < 0)
    {
        std::cerr << "errno" << errno << ",errstring" << strerror(errno) << std::endl;
        if(Makefifo()) goto Start;
        else return 1;
    }
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(rfd,buffer,sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            std::cout<<"client say:"<< buffer << std::endl;
        }
        else if(s == 0)
        {
            std::cout<<"client quit, server quit,too..."<<std::endl;
            break;
        }
    }


    close(rfd);
    return 0;
}

client.cc

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

#include "comm.h"

int main()
{
    int wfd = open(FILENAME,O_WRONLY);
    if(wfd < 0)
    {
        std::cerr << "errno" << errno << ",errstring" << strerror(errno) << std::endl;
        return 1;
    }
    std::string message;
    while(true)
    {
        std::cout<<"cin>>"<<std::endl;
        std::getline(std::cin , message);

        ssize_t s = write(wfd,message.c_str(),message.size());
        if(s < 0)
        {
            std::cerr << "errno" << errno << ",errstring" << strerror(errno) << std::endl;
            break;
        }
    }

    close(wfd);


    return 0;
}
  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值