天天看點

IPC - 管道(pipe) - 應用

仿寫 | 的實作

    • 關于IPC
      • 為什麼需要IPC?
      • IPC的方法?
    • 關于管道
    • 關于 " | "
    • pipe實作 " | "

關于IPC

IPC,即Inter-Process Communication(

程序間通信

)。

我們都知道,程序是一段程式的執行過程,是系統程序資源配置設定和排程的基本機關。

那麼為什麼要引入程序間通信呢?或者說,程序間通信能帶來哪些好處?

為什麼需要IPC?

  • 資料傳輸
  • 共享資料
  • 通知事件
  • 資源共享
  • 程序控制

上面幾點都很好了解,我們可以着重看一下最後一點:程序控制。

有一些程序希望能夠完全控制另一個程序的執行(比如

Debug

程序),此時控制程序希望能夠攔截到另一程序的所有異常,并能夠及時知道它的狀态改變。

這一點,相信程式設計過的大家都很有體會吧,我們在寫完一段代碼,調試程式的時候,

打斷點

分步執行

進入執行

跳出執行

等等,每執行一步,我們可以通過螢幕、或者檢視記憶體中的資料來檢查程式是否按照我們預想的那樣執行,進而發現錯誤,修正錯誤。

如果沒有程序間通信的話,程序與程序之間你執行你的,我執行我的,井水不犯河水,那麼,如何實作調試的功能呢?

IPC的方法?

  1. 無名管道(pipe)
  2. 有名管道(fifo)
  3. 共享記憶體
  4. 消息隊列
  5. 信号量
  6. Socket

今天介紹的重點就是無名管道–

pipe

關于管道

我們都知道每個程序都有自己的4G虛拟位址空間

IPC - 管道(pipe) - 應用

對于每個程序來說,3G的使用者空間是獨有的,1G的核心空間是共享的。

基于此,管道

本質上就是核心空間中的一塊緩存

對于無名管道而言:

  • 必須在關系程序中程序(父子程序或兄弟程序);
  • pipe

    系統調用,管道由父程序建立;

函數原型:

#include <unistd.h>
int pipe(int fields[2])
           

其中的

fields

是我們傳入的數組,也是一個傳出參數。

fields[0]

是讀端;

fields[1]

是寫端。

對于命名管道而言:

  • 沒有任何關系的程序間也可以通信;
  • 通過

    mkfifo

    系統調用建立。

關于 " | "

在Linux系統中," | " 就代表了管道。

比如說我們想通過檢視Linux系統中的使用者資訊:

cat /etc/passwd

IPC - 管道(pipe) - 應用

我們會看到非常多的資訊。

此時,我隻想看

root

使用者的資訊,該怎麼做呢?

cat /etc/passwd | grep root

IPC - 管道(pipe) - 應用

對于該指令的解讀是:

IPC - 管道(pipe) - 應用

pipe實作 " | "

通過對上面指令的解讀,我們會發現,需要兩個子程序,分别做

cat

grep

的工作,然後将

cat

的執行結果寫入管道,

grep

從管道中讀取内容并且過濾。

注意:

我們通過前面指令可以看到:

cat

的執行結果是預設輸出到螢幕(标準輸出)的,而

grep

的預設讀取也是标準輸入。

是以,我們為了實作通信,需要将标準輸入和标準輸出進行重定向(使用

dup2()

)。

代碼如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wait.h>

char *cmd1[3] = {"/bin/cat", "/etc/passwd", NULL};
char *cmd2[3] = {"/bin/grep", "root", NULL};

int main()
{
    int fd[2];
    if (pipe(fd) < 0)
    {
        perror("pipe error");
        exit(1);
    }
    int i = 0;
    pid_t pid;
    for (; i < 2; i++)
    {
        pid = fork();
        if (pid < 0)
        {
            perror("fork error");
            exit(1);
        }
        else if (pid == 0) // child process
        {
            if (i == 0) // 第一個子程序,負責向管道寫入資料
            {
                // 關閉讀端
                close(fd[0]);

                // 将标準輸出重定向到管道的寫端
                // 下面指令執行的結果會寫入到管道中
                // 而不是輸出到螢幕
                if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
                {
                    perror("dup2 error");
                }
                close(fd[1]);

                // 調用exec函數執行cat指令
                if (execvp(cmd1[0], cmd1) < 0)
                {
                    perror("execvp error");
                    exit(1);
                }
                break;
            }
            if (i == 1) // 第二個子程序,負責從管道讀取資料
            {
                // 關閉寫端
                close(fd[1]);

                /*
                 * 将标準輸入重定向到管道的讀端
                 * 下面指令grep的執行是從管道的讀端
                 * 讀取内容,而不是從标準輸入讀取
                 */
                if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
                {
                    perror("dup2 error");
                    exit(1);
                }
                close(fd[0]);

                // 調用exec函數執行grep指令
                if (execvp(cmd2[0], cmd2) < 0)
                {
                    perror("execvp error");
                    exit(1);
                }
                break;
            }
        }
        else // parent process
        {
            if (i == 1)
            {
                // 父程序要等到子程序全部建立完畢才會去回收
                close(fd[0]);
                close(fd[1]);
                wait(0);
                wait(0);
            }
        }
    }

    exit(0);
}
           

運作結果對比:

IPC - 管道(pipe) - 應用

繼續閱讀