精彩管道不会梦到深沉蓝调

如果上天开了眼

请多给我点蓝调

多给我点沙锤

多给我点甲壳

让我吃鸡!

星元自动机,新的版本之神 

给宁磕一个

完蛋

你说这不是问题吗

我这篇文章从我写开始,到写完

炉石都换赛季了!!!!!

伙伴没了我心碎

#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include"Task.hpp"

// void work(int rfd)
// {
//     while(true)
//     {
//         int command = 0;
//         int n = read(rfd, &command,sizeof(command)); 
//         if(n == sizeof(int))
//         {
//             std::cout << "pid is:" << getpid() << "handler task" << std::endl;
//             ExcuteTask(command);
//         }
//         else if(n == 0)
//         {
//             std::cout<<"Pipe closed"<<std::endl;
//             break;
//         }
//         else
//         {
//             perror("read");
//             break;
//         }
//     }
// }


//master

class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    :_wfd(wfd),_subprocessid(id),_name(name)
    {}
    int Getfd()const
    {
        return _wfd;
    }
    pid_t GetProcessId()const
    {
        return _subprocessid;
    }
    std::string GetName()const
    {
        return _name;
    }
    void CloseChannel()
    {
        close(_wfd);
    }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid,nullptr,0);
        if(rid > 0)
        {
            std::cout << "wait " <<rid << "success" << std::endl;
        }
    }
    ~Channel()
    {

    }
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

void CreateChannelAndSub(std::vector<Channel>* channels,int num1,task_t task)
{
    for(int i = 0; i < num1; i++)
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n < 0)
        {
            perror("pipe");
            exit(1);
        }

        //创建紫禁城
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            exit(1);
        }
        if(id == 0)
        {
            if(!channels->empty())
            {
                //第二次之后创建的管道
                for(auto &channel : *channels)
                {
                    channel.CloseChannel();
                }
            }
            //child
            close(pipefd[1]);
            dup2(pipefd[0],0);
            task();
            close(pipefd[0]);
            exit(0);
        }
       
        //父进程
        close(pipefd[0]);
        //构建名字
        std::string channel_name = "Channel  " + std::to_string(i);
        channels->push_back(Channel(pipefd[1],id,channel_name));
        //close(pipefd[1]);
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(const Channel &channel,int taskcommand)
{
    size_t n = write(channel.Getfd(),&taskcommand,sizeof(taskcommand));
    if(n != sizeof(taskcommand))
    {
        perror("write");
    }
}

void CtrlProcessOnce(std::vector<Channel> &channels)
{
    sleep(1);
    //选择任务
    int taskcommand = Select();
    //选择信道和进程
    int channel_index = NextChannel(channels.size());
    //发送任务
    SendTaskCommand(channels[channel_index],taskcommand);

    std::cout << "taskcommand:" << taskcommand << "  channel:"\
    <<channels[channel_index].GetName() << "  sub process:"\
    << channels[channel_index].GetProcessId() << std::endl;
}

//通过channel来控制紫禁城
void CtrlProcess(std::vector<Channel> &channels,int times = -1)
{
    if(times > 0)
    {
       while(times--)
        {
            CtrlProcessOnce(channels);
        }
    }
    else
    {
        while(true)
        {
            CtrlProcessOnce(channels);
        }
    }
}

//回收管道和子进程
void CleanUpChannels(std::vector<Channel> &channels)
{
    // for(auto &channel : channels)
    // {
    //     channel.CloseChannel();
    // }

    for(auto &channel : channels)
    {
        channel.Wait();
    }
}

// ./processpool 5
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<"processnum"<<std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();

    std::vector<Channel> channels;
    //创建信道和子进程
    CreateChannelAndSub(&channels,num,work);

    //通过channel控制子进程
    CtrlProcess(channels,10);

    CleanUpChannels(channels);
   
    // for(auto &channel : channels)
    // {
    //     std::cout << " ---------------------- " <<std::endl;
    //     std::cout << channel.GetName() << std::endl;
    //     std::cout << channel.Getfd() << std::endl;
    //     std::cout << channel.GetProcessId() << std::endl;
    // }
    // sleep(100);
    return 0;
}

青春版Shell中添加管道实现

管道读写规则

当没有数据可读时

O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止

O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN

当管道满的时候

O_NONBLOCK disable: write调用阻塞,直到有进程读走数据

O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,则read返回0

如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出

当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性

当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性

管道特点 

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

一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道

管道提供流式服务

进程退出,管道释放,所以管道的生命周期随进程

内核会对管道操作进行同步与互斥 管道是半双工的,数据只能向一个方向流动

需要双方通信时,需要建立起两个管道

命名管道

原理

有进程、磁盘上有文件,进程要打开对应的文件描述符表struct files_struct

里面包含数组struct file* fd_array[ ]

struct file里面要有属性集合和操作集合

通过文件内核级的缓冲区向磁盘文件中写数据

另一个进程要打同一文件,要有对应的PCB和文件描述符表,有相对应的文件对象,新文件的属性集操作集文件内核缓冲区都差不多,没必要再创建一份,操作系统不会做浪费时间浪费空间的事

至此两份进程可以看到同一文件了

怎么保证两个毫不相关的进程打开的是同一文件呢?

每一个文件都有文件路径(这个文件路径具有唯一性)

这就是命名管道,依旧是内存级别的进行通信的方案

文件需要是特殊文件,文件打开后不会将数据刷新到磁盘,而是在内存级别进行文件通信

通过路径标识保证唯一性的叫命名管道

代码

这是个接口,可以制作一个FIFO

使用呢是这样用:

mkfifo myfifo

 可以这样进行读写:

如果想让左侧的终端不断向右侧写入则可以:

while :;do sleep 1;echo "hello named pipe"; done >> myfifo

 

那怎样让我们写的进程实行代码级别的通信呢?

还得是这个库函数

 这是一个unlink:

可以删除指定目录下的文件

 这是两个接口:创建一个管道、移除一个管道

namedPiped.hpp:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>

const std::string comm_path = "./myfifo";

int CreateNamedPipe(const std::string &path)
{
    int res = mkfifo(path.c_str(),0666);
    if(res != 0)
    {
        perror("mkfifo");
    }
    return res;
}

int RemoveNamedPipe(const std::string &path)
{
    int res = unlink(path.c_str());
    if(res != 0)
    {
        perror("unlink");
    }
    return res;
}

 用cpp对代码封装:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>

const std::string comm_path = "./myfifo";

class NamePiped
{
public:
    NamePiped(const std::string &path):_fifo_path(path)
    {
        int res = mkfifo(path.c_str(),0666);
        if(res != 0)
        {
            perror("mkfifo");
        }
    }
    ~NamePiped()
    {
        int res = unlink(_fifo_path.c_str());
        if(res != 0)
        {
            perror("unlink");
        }
    }
private:
    const std::string _fifo_path;
};

 相应的我们要对其进行身份的识别,是创建者才需要执行对应的操作:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>

#define Creater 1
#define User 2

const std::string comm_path = "./myfifo";

class NamePiped
{
public:
    NamePiped(const std::string &path,int who):_fifo_path(path), _id(who)
    {
        if(_id == Creater)
        {
            int res = mkfifo(path.c_str(),0666);
            if(res != 0)
            {
              perror("mkfifo");
            }
        }
    }
    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
        }
    }
private:
    const std::string _fifo_path;
    int _id;
};

创建管道为了读写:

#pragma once

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

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY

const std::string comm_path = "./myfifo";

class NamePiped
{
private:
    //打开文件的模式
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(),mode);
        if(_fd < 0)
        {
            return 0;
        }
        return true;
    }
public:
    NamePiped(const std::string &path,int who)
    :_fifo_path(path), _id(who),_fd(DefaultFd)
    {
        if(_id == Creater)
        {
            int res = mkfifo(path.c_str(),0666);
            if(res != 0)
            {
              perror("mkfifo");
            }
        }
    }
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }
    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
        }
        if(_fd != DefaultFd)
        {
            close(_fd);
        }
    }
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

于是服务器端和客户端的调用方式也出来了

client.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,User);
    fifo.OpenForWrite();
    return 0;
}

server.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,Creater);
    fifo.OpenForRead();
    return 0;
}

还要有相应的读写管道的操作:

#pragma once

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

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

const std::string comm_path = "./myfifo";

class NamePiped
{
private:
    //打开文件的模式
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(),mode);
        if(_fd < 0)
        {
            return 0;
        }
        return true;
    }
public:
    NamePiped(const std::string &path,int who)
    :_fifo_path(path), _id(who),_fd(DefaultFd)
    {
        if(_id == Creater)
        {
            int res = mkfifo(path.c_str(),0666);
            if(res != 0)
            {
              perror("mkfifo");
            }
        }
    }
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }
    
    //输出:const &:const std::string &XXX
    //输入:*      std::string *
    //输入输出:&   std::string &

    int ReadNamedPipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd,buffer,sizeof(buffer));
        if(n > 0)
        {
            //读取成功
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }
    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd,in.c_str(),in.size());
        
    }
    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
        }
        if(_fd != DefaultFd)
        {
            close(_fd);
        }
    }
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

接下来就是实现服务器端和客户端进行通信:

client.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,User);
    if(fifo.OpenForWrite())
    {
        std::cout << "Please Enteer -> ";
        std::string message;
        std::getline(std::cin,message);     //从标准输入中获取信息到message中
        fifo.WriteNamedPipe(message);
    }
    return 0;
}

 server.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,Creater);
    if(fifo.OpenForRead())
    {
        while(true)
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if(n > 0)
            {
                std::cout << "Client Say " << message << std::endl;
            }
        }
    }
    return 0;
}

 

对于读端而言,如果我们打开文件,但是写端还没来,会阻塞在open调用中,直到对方打开

命名管道是通过文件路径让不同进程看到同一份资源的~

下篇说共享内存捏

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值