linux之管道

1. 進程間通訊概述

  進程是一個獨立的資源分配單元,不一樣進程之間的資源是獨立的,沒有關聯,不能在一個進程中直接訪問另外一個進程的資源。進程不是孤立的,不一樣的進程須要進行信息的交互和狀態的傳遞等,所以須要進程間通訊。linux

1.1 進程間通訊功能

(1)數據傳輸:一個進程須要將它的數據發送給另外一個進程。ios

(2)資源共享:多個進程之間共享一樣的資源。數組

(3)通知事件:一個進程須要向另外一個或一組進程發送消息,通知它們發生了某種事件。安全

(4)進程控制:有些進程但願徹底控制另外一個進程的執行,此時控制進程但願可以攔截另外一個進程的全部操做,並可以及時知道它的狀態改變服務器

1.2 主要進程間通訊的通訊機制

2. 管道

2.1 管道特色

  管道(pipe)又稱無名管道,是一種特殊類型的文件,在應用層體現爲兩個打開的文件描述符ide

(1)半雙工,數據在同一時刻只能在一個方向上流動函數

(2)管道不是普通的文件,不屬於某個文件系統,其只存在於內存中測試

(3)管道沒有名字,只能在具備公共祖先的進程之間使用ui

(4)管道的緩衝區大小是有限的,在linux中,該緩衝區的大小固定爲4kspa

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("建立管道成功\n");

    int pid = fork();
    if(pid < 0)
    {
        perror("建立進程失敗");
    }
    else if(pid == 0)
    {
        printf("子進程:\n");
        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\n」);
}
執行
/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旗標會有影響
    一、當使用O_NONBLOCK 旗標時,打開FIFO 文件來讀取的操做會馬上返回,可是若尚未其餘進程打開FIFO 文件來讀取,則寫入的操做會返回ENXIO 錯誤代碼。
    二、沒有使用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
相關文章
相關標籤/搜索