匿名管道要点与用法

12 篇文章 0 订阅

1.pipe 

int pipe(int fd[2])

         创建匿名管道

pipefd是个文件描述符数组,其中pipefd[0]代表读端的fd,pipefd[1]代表写端的fd。

2.匿名管道原理:

3. 管道是半双工通信,是可以选择方向的单向通信 

        注意是单向的,一段不可以边写边读。

4.管道是什么?
        管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,命名管道的文件虽然可见于文件系统,但是只是标识符,并非通信介质。

5.进程对管道的读写有可能会阻塞。

        缓冲区有具体大小,当缓冲区写满之后,写入就会堵塞。当缓冲区没有数据,读出就会阻塞。

6.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信。

7.若管道所有写段关闭,则从管道中读取完所有数据后,read会返回0。

   若所有读端关闭,则继续write写入会触发异常导致进程退出。

8.管道是面向字节流的

9.管道是文件,它的生命周期随进程的退出,而退出。

例子一:

        从键盘读取数据,写入管道,读取管道,写到屏幕

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;

int main()
{
    // 1.创建管道
    int pipefd[2];
    if (pipe(pipefd) != 0)
    {
        cerr << "pipe failed" << endl;
        return 1;
    }

    // 2.创建子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        //失败就打印
        cerr << "fork failed" << endl;
    }
    else if (pid == 0)
    {
        //子进程
        //读
        close(pipefd[1]);
        const int sz = 128;
        char buf[sz];
        while (1)
        {
            memset(buf, 0, sizeof(buf));
            //从匿名pipe中读取,放入buf中
            ssize_t s = read(pipefd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                //读取成功
                buf[s] = '\0';
                cout << "读取成功,结果是:" << buf << endl;
            }
            else if (s == 0)
            {
                //读取结束
                cout << "他结束,我也" << endl;
                break;
            }
            else
            {
                // 防止报错
            }
        }
        close(pipefd[0]);
        exit(123);
    }
    else
    {
        //父进程
        //写
        close(pipefd[0]);
        string str = "我是父进程,你慈祥的父亲,我的子进程你好!!!";
        int n = 0;
        while (n != 5)
        {
            //往pipefd[1]匿名管道中写
            write(pipefd[1], str.c_str(), str.size());
            sleep(1);
            n++;
        }
        close(pipefd[1]);
    }
    //等待子进程
    int status = 0;
    pid_t wt = waitpid(pid, &status, 0);
    if( WIFEXITED(status))
    {
        cout<<"等待成功"<<"退出码是:"<< WEXITSTATUS(status) <<endl;
    }

    return 0;
}

结果:

        至于最后的进程等待,有不懂的可以移步这一篇博客

例子二:

        父进程将任务下标写入管道,子进程读管道内容来执行任务。

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
#include <cassert>
#include <unordered_map>
using namespace std;

unordered_map<uint32_t, string> info;

typedef void (*fuc_pipe)();

vector<fuc_pipe> vfus;

void f1()
{
    cout << "自毁程序已启动,执行的进程为:[" << getpid() << "]"
         << "执行时间为:" << time(nullptr) << endl;
}
void f2()
{
    cout << "清理人类程序已启动,执行的进程为:[" << getpid() << "]"
         << "执行时间为:" << time(nullptr) << endl;
}
void f3()
{
    cout << "屠杀模式已启动,执行的进程为:[" << getpid() << "]"
         << "执行时间为:" << time(nullptr) << endl;
}

void loadfuc()
{
    info.insert({vfus.size(), "这是自毁程序"});
    vfus.push_back(f1);
    info.insert({vfus.size(), "这是清理人类程序"});
    vfus.push_back(f2);
    info.insert({vfus.size(), "这是屠杀模式"});
    vfus.push_back(f3);
}

int main()
{
    // 0.load fuc
    loadfuc();

    // 1.make pipe
    int pipefd[2];
    if (pipe(pipefd) != 0)
    {
        cout << "pipe error" << endl;
        exit(1);
    }

    pid_t pid = fork();
    if (pid < 0)
    {
        cout << "fork error" << endl;
        exit(2);
    }
    else if (pid == 0)
    {
        // child read
        close(pipefd[1]);
        while (true)
        {
            //子进程从缓冲区中读
            uint32_t opsb = 0;
            ssize_t s = read(pipefd[0], &opsb, sizeof(uint32_t));
            // 写端关闭,read返回值为0
            if (s == 0)
            {
                cout << "父进程派发任务结束,地球毁灭人类灭亡,子进程退出" << endl;
                close(pipefd[0]);
                exit(123);
            }

            assert(s == sizeof(uint32_t));
            (void)s;
            // assert在debug模式下有效,如果在release模式下无效
            //则s变量只被定义未被使用,(void)s 目的是防止warning

            //下达任务
            if (opsb < vfus.size())
            {
                vfus[opsb]();
            }
            else
            {
                cout << "opsb error" << endl;
            }
        }

    }
    else
    {
        // parent wirte
        close(pipefd[0]);

        // deliver task through rand
        int n = vfus.size();
        srand((long long)time(nullptr));

        int cnt = 1;
        while (cnt != 10)
        {
            //通过随机数下达命令
            uint32_t taskcode = rand() % n;
            cout << "父进程正在下达第" << cnt << "次任务,任务是:" << info[taskcode] << endl;
            //父进程写入缓冲区
            write(pipefd[1], &taskcode, sizeof(uint32_t));
            sleep(1);
            cnt++;
        }
        close(pipefd[1]);

        // wait child
        int status = 0;
        pid_t ret = waitpid(pid, &status, 0);
        if (ret)
        {
            cout << "wait child process " <<"exit code :"<<WEXITSTATUS(status)<< endl;
        }
    }

    return 0;
}

结果是:

例子三:

        一个进程派发任务给多个进程

代码:

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
#include <cassert>
#include <unordered_map>
using namespace std;

unordered_map<uint32_t, string> info;

typedef void (*fuc_pipe)();

vector<fuc_pipe> vfus;

void f1()
{
    cout << "自毁程序已启动,执行的进程为:[" << getpid() << "]"
         << "执行时间为:" << time(nullptr) <<endl<<endl;
}
void f2()
{
    cout << "清理人类程序已启动,执行的进程为:[" << getpid() << "]"
         << "执行时间为:" << time(nullptr) <<endl<<endl;
}
void f3()
{
    cout << "屠杀模式已启动,执行的进程为:[" << getpid() << "]"
         << "执行时间为:" << time(nullptr) <<endl<<endl;
}

void loadfuc()
{
    info.insert({vfus.size(), "这是自毁程序"});
    vfus.push_back(f1);
    info.insert({vfus.size(), "这是清理人类程序"});
    vfus.push_back(f2);
    info.insert({vfus.size(), "这是屠杀模式"});
    vfus.push_back(f3);
}


//第一个uint32_t存放:进程pid,第二个uint32_t存放:该进程对应的管道写端fd
typedef pair<uint32_t,uint32_t> gather;
const int ChdProNus = 8;

void Dothing(int fd)
{
    //拿到读端fd
    while(1)
    {
        cout<<"进程:["<<getpid()<<"]准备读取任务"<<endl;
        uint32_t opsb = 0;
        ssize_t sz = read(fd,&opsb,sizeof(uint32_t));
        if(sz == 0)
        {
            break;
        }
        assert(sz == sizeof(opsb));
        (void)sz;

        if(opsb < vfus.size())
        {
            vfus[opsb]();
        }

        //cout<<"进程:["<<getpid()<<"]任务执行结束"<<endl;
    }
}

void DeliveryTasks(const vector<gather> &assignMap)
{
    srand((long long)time(nullptr));
    int cnt = 25;
    while(cnt--)
    {
        sleep(1);
        //select which child process
        uint32_t child = rand() % assignMap.size();
        //select which task
        uint32_t task = rand() %  vfus.size();

        //写入管道
        write(assignMap[child].second,&task,sizeof(task));

        cout<<"派发任务成功"<<endl;
        
    }
}

int main()
{
    vector<gather> assignMap;
    loadfuc();
    //创建ChdProNus个子进程
    for(int i =0; i < ChdProNus; i++)
    {
        //创建管道
        int pipefd[2];
        pipe(pipefd);

        //child read create child
        pid_t pid = fork();
        if(pid == 0)
        {
            //child
            close(pipefd[1]);
            Dothing(pipefd[0]);
            close(pipefd[0]);
            exit(1);
        }

        //parent
        close(pipefd[0]);
        gather g(pid,pipefd[1]);
        assignMap.push_back(g);
    }
    //走到此处只有parent
    //派发任务
    cout<<"准备派发任务"<<endl;
    DeliveryTasks(assignMap);

    //回收资源
    for(int i = 0; i < ChdProNus; i++)
    {
        if(waitpid(assignMap[i].first,nullptr,0))
        {
            cout<<"wait child succes"<<"its pid is :"<<assignMap[i].first<<"it is"<< i <<"th"<<endl;
            close(assignMap[i].second);
        }

    }
}

结果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值