• linux之管道


    1. 进程间通信概述

      进程是一个独立的资源分配单元,不同进程之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源。进程不是孤立的,不同的进程需要进行信息的交互和状态的传递等,因此需要进程间通信。

    1.1 进程间通信功能

    (1)数据传输:一个进程需要将它的数据发送给另一个进程。

    (2)资源共享:多个进程之间共享同样的资源。

    (3)通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。

    (4)进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变

    1.2 主要进程间通信的通信机制

    2. 管道

    2.1 管道特点

      管道(pipe)又称无名管道,是一种特殊类型的文件,在应用层体现为两个打开的文件描述符

    (1)半双工,数据在同一时刻只能在一个方向上流动

    (2)管道不是普通的文件,不属于某个文件系统,其只存在于内存中

    (3)管道没有名字,只能在具有公共祖先的进程之间使用

    (4)管道的缓冲区大小是有限的,在linux中,该缓冲区的大小固定为4k

    2.2 管道数据传输

    2.3 函数

    #include <unistd.h>
    int pipe(int filedes[2]);
    
    功能:经由参数filedes返回两个文件描述符
    
    参数:
    
    filedes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1]。
    
    filedes[0]为读而打开,filedes[1]为写而打开
    
    管道,filedes[0]的输出是filedes[1]的输入。
    
    返回值:成功:返回 0 失败:返回-1

    2.4 例子

    #include "intf.h"
    
    #include <iostream>
    #include <string.h>
    using namespace std;
    
    #define BUFFSIZE 1024
    
    int main(int argc, char *argv[]) 
    { 
        int ParentFd[2];
        
        cout << "程序开始" << endl;
        if(pipe(ParentFd) < 0)
        {
            perror("创建管道失败");
        }
        printf("创建管道成功
    ");
    
        int pid = fork();
        if(pid < 0)
        {
            perror("创建进程失败");
        }
        else if(pid == 0)
        {
            printf("子进程:
    ");
            close(ParentFd[0]);
            cout << "请输入要写入的字符" << endl;
            string s;
            while(getline(cin, s))
            {
                write(ParentFd[1], s.c_str(), s.length());
                if(s == "quit")
                {
                    exit(0);
                }
            }
            
        }
        else
        {
            cout << "父进程:" << endl;
            close(ParentFd[1]);
            while(1)
            {
                char msg[BUFFSIZE] = {0}; 
                read(ParentFd[0], msg, BUFFSIZE);
                cout << "父进程显示:" << msg << endl;
                if(!strcmp(msg, "quit"))
                {
                    exit(0);
                }
            }
        }
    
        cout << "程序结束" << endl;
    
        return 0;
    } 

    3. 命名管道

      命名管道(FIFO)和管道(PIPE)基本相同,FOFO有名字,不同的进程可以通过该命名管道进行通信

    3.1 函数介绍

    (1)access

    access():判断是否具有存取文件的权限

    相关函数
        stat,open,chmod,chown,setuid,setgid
    表头文件
        #include<unistd.h>
    定义函数
        int access(const char * pathname, int mode);
    函数说明
        access()会检查是否可以读/写某一已存在的文件。参数mode有几种情况组合, R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK与X_OK用来检查文件是否具有读取、写入和执行的权限。F_OK则是用来判断该文件是否存在。由于access()只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。例如,你会发现DOS的文件都具有“可执行”权限,但用execve()执行时则会失败。
    返回值
        若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回-1
    错误代码
        EACCESS 参数pathname 所指定的文件不符合所要求测试的权限。
        EROFS 欲测试写入权限的文件存在于只读文件系统内。
        EFAULT 参数pathname指针超出可存取内存空间。
        EINVAL 参数mode 不正确。
        ENAMETOOLONG 参数pathname太长。
        ENOTDIR 参数pathname为一目录。
        ENOMEM 核心内存不足    
        ELOOP 参数pathname有过多符号连接问题。
        EIO I/O 存取错误。
    附加说明
        使用access()作用户认证方面的判断要特别小心,例如在access()后再做open()的空文件可能会造成系统安全上的问题。

    范例

    #include<unistd.h>
    int main()
    {
        if (access(“/etc/passwd”,R_OK) = =0)
            printf(“/etc/passwd can be read
    ”);
    }
    执行
    /etc/passwd can be read 

    (2)mkfifo

    mkfifo(建立实名管道)
    相关函数
        pipe,popen,open,umask
    表头文件
        #include<sys/types.h>
        #include<sys/stat.h>
    定义函数
        int mkfifo(const char * pathname,mode_t mode);
    函数说明
        mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响
        1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。
        2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。
    返回值
        若成功则返回0,否则返回-1,错误原因存于errno中。
    错误代码
        EACCESS 参数pathname所指定的目录路径无可执行的权限
        EEXIST 参数pathname所指定的文件已存在。
        ENAMETOOLONG 参数pathname的路径名称太长。
        ENOENT 参数pathname包含的目录不存在
        ENOSPC 文件系统的剩余空间不足
        ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
        EROFS 参数pathname指定的文件存在于只读文件系统内。

    4. 命名管道实现服务器和客户端双向通信

    4.1 封装命令管道类NamedPipe

    enum OpenMode
    {
        ReadOnly = 1,
        WriteOnly,
        READWRITE
    };
    
    class NamedPipe
    {
    public:
        // 每次从管道最多读取的字节数
        static const int PIPE_BUFF = 1024;
    
        NamedPipe();
        NamedPipe(const string& strPath, OpenMode mode);
        ~NamedPipe();
        string read(int nSize);
        string read();
        void write(const string& content);
    
    private:
        int m_fd;
        int m_mode;
    };

    NamedPipe实现

    NamedPipe::NamedPipe()
    {
    
    }
    NamedPipe::NamedPipe(const string& strPath, OpenMode mode)
    :m_fd(-1), m_mode(mode)
    {
        int nOpenMode = 0;
        if(mode == ReadOnly)
        {
            nOpenMode = O_RDONLY;
        }
        else if(mode == WriteOnly)
        {
            nOpenMode = O_WRONLY;
        }
        else if(mode == READWRITE)
        {
            nOpenMode = O_RDWR;
        }
        cout << "检查管道:" << endl; 
       if(access(strPath.c_str(), F_OK) < 0)
       {
           cout << "管道不存在创建管道" << endl;
           int ret = mkfifo(strPath.c_str(), 0777 );
           if(ret < 0)
           {
               cout << "无法创建管道" << endl; 
           }
           else{
               cout << "创建管道成功" << endl;
           }
       }
        cout << "管道存在打开管道" << endl; 
        m_fd = open(strPath.c_str(), nOpenMode);
    }
    
    NamedPipe::~NamedPipe()
    {
        if(m_fd && m_fd != -1)
        {
            close(m_fd);
        }
    }
    string NamedPipe::read(int nSize)
    {
        if(m_fd == -1)
        {
            cout << "打开文件失败!" << endl;
        }
       
        char buff[PIPE_BUFF] = {0};
        int nReadSize = 0;
        string strContent = "";
        do{
            int nBytesToRead = 0;
            if(nReadSize + PIPE_BUFF < nSize)
            {
                nBytesToRead = PIPE_BUFF;
            }
            else 
            {   
                nBytesToRead = nSize - nReadSize;
            }
      
            nBytesToRead = ::read(m_fd, buff, nBytesToRead);
            if(nBytesToRead == -1)
            {
                cout << "读取失败" << endl; 
            }
    
            nReadSize += nBytesToRead;
            strContent += string(buff, nBytesToRead);
    
        }while(nReadSize < nSize);
    
        return strContent;
    }
    string NamedPipe::read()
    {
        if(m_fd == -1)
        {
            cout << "打开文件失败!" << endl;
        }
       
        char buff[PIPE_BUFF] = {0};
        
        int nBytesToRead = ::read(m_fd, buff, PIPE_BUFF);
        if(nBytesToRead == -1)
        {
            cout << "PipeReadException" << endl; 
        }
    
        return string(buff);
    }
    void NamedPipe::write(const string& content)
    {
        if(m_fd == -1)
        {
            cout << "打开文件失败!" << endl;
        }
        int nWriteBytes = ::write(m_fd, content.c_str(), content.length());
      
        if(nWriteBytes == -1)
        {
            cout << "PipeWriteException" << endl; 
        }
    }

    4.2 服务器和客户端实现

    #include "namepipe.h"
    
    #define SERVER_W "serverWrite"
    #define SERVER_R "serverRead"
    #define RED_SIZE 1024
    int main()
    {
        NamedPipe ReadPipe(SERVER_R, READWRITE);
        NamedPipe WritePipe(SERVER_W, READWRITE);
    
        int pid = fork();
    
        if(pid < 0)
        {
            cout << "创建服务器子进程失败!" << endl;
        }
        else if(pid == 0)
        {
            cout << "服务器子进程创建成功,用于读客户端信息" << endl;
            string msg = "";
            while(1)
            {
                msg = ReadPipe.read();
                cout << msg.length() << endl;
                if(msg.length() > 0)
                {
                    cout << "服务器接收到信息:" << msg << endl;
    
                    if(msg == "EOF")
                    {
                        cout << "客户端请求断开连接" << endl;
                        break;
                    }
                }
            }
            cout << "服务器子进程没有资源可读,断开连接" << endl;
            exit(0);
        }
        else{
            cout << "服务器父进程用于发送内容给客户端" << endl;
            string msg = "";
            while(getline(cin, msg))
            {
                 WritePipe.write(msg);
    
                if(msg == "EOF")
                {
                    cout << "服务器请求断开连接"<< endl;
                    break;
                }
               
            }
    
            wait(NULL);
        }
    
        return 0;
    }
    pipeserver.cpp
    #include "namepipe.h"
    
    #define SERVER_W "serverWrite"
    #define SERVER_R "serverRead"
    #define RED_SIZE 64
    int main()
    {
        NamedPipe ReadPipe(SERVER_W, READWRITE);
        NamedPipe WritePipe(SERVER_R, READWRITE);
    
        int pid = fork();
    
        if(pid < 0)
        {
            cout << "创建客户端子进程失败!" << endl;
        }
        else if(pid == 0)
        {
            cout << "客户端子进程创建成功,用于写客户端信息" << endl;
            string msg = "";
            while(getline(cin, msg))
            {
                 WritePipe.write(msg);
    
                if(msg == "EOF")
                {
                    cout << "客户端子进程请求断开连接"<< endl;
                    break;
                }
               
            }
            
            cout << "服客户端子进程断开连接" << endl;
            exit(0);
        }
        else{
            cout << "客户端父进程用于读取服务器内容" << endl;
            string msg = "";
            while(1)
            {
                msg = ReadPipe.read();
                if(msg.length() > 0)
                {
                    cout << "客户端父进程接收到信息:" << msg << endl;
    
                    if(msg == "EOF")
                    {
                        cout << "服务器请求断开连接" << endl;
                        break;
                    }
                }
            }
    
            wait(NULL);
        }
    
        return 0;
    }
    pipeclient.cpp
  • 相关阅读:
    RBAC权限管理模型 产品经理 设计
    Redisson 分布式锁
    Jenkins下载历史Build版本的归档文件
    Java JPA @Transient 在Hibernate中应用
    Solving the Top ERP and CRM Metadata Challenges with erwin & Silwood
    MySQL 字符串 分割 多列
    MySQL CONCAT opposite
    Web并发页面访问量统计实现
    UNIX网络编程读书笔记:基本UDP套接口编程
    UNIX网络编程调试工具:tcpdump、netstat和lsof
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/9202339.html
Copyright © 2020-2023  润新知