仿寫 | 的實作
-
- 關于IPC
-
- 為什麼需要IPC?
- IPC的方法?
- 關于管道
- 關于 " | "
- pipe實作 " | "
關于IPC
IPC,即Inter-Process Communication(
程序間通信
)。
我們都知道,程序是一段程式的執行過程,是系統程序資源配置設定和排程的基本機關。
那麼為什麼要引入程序間通信呢?或者說,程序間通信能帶來哪些好處?
為什麼需要IPC?
- 資料傳輸
- 共享資料
- 通知事件
- 資源共享
- 程序控制
上面幾點都很好了解,我們可以着重看一下最後一點:程序控制。
有一些程序希望能夠完全控制另一個程序的執行(比如
Debug
程序),此時控制程序希望能夠攔截到另一程序的所有異常,并能夠及時知道它的狀态改變。
這一點,相信程式設計過的大家都很有體會吧,我們在寫完一段代碼,調試程式的時候,
打斷點
、
分步執行
、
進入執行
、
跳出執行
等等,每執行一步,我們可以通過螢幕、或者檢視記憶體中的資料來檢查程式是否按照我們預想的那樣執行,進而發現錯誤,修正錯誤。
如果沒有程序間通信的話,程序與程序之間你執行你的,我執行我的,井水不犯河水,那麼,如何實作調試的功能呢?
IPC的方法?
- 無名管道(pipe)
- 有名管道(fifo)
- 共享記憶體
- 消息隊列
- 信号量
- Socket
今天介紹的重點就是無名管道–
pipe
。
關于管道
我們都知道每個程序都有自己的4G虛拟位址空間
對于每個程序來說,3G的使用者空間是獨有的,1G的核心空間是共享的。
基于此,管道
本質上就是核心空間中的一塊緩存
。
對于無名管道而言:
- 必須在關系程序中程序(父子程序或兄弟程序);
- 由
系統調用,管道由父程序建立;pipe
函數原型:
#include <unistd.h>
int pipe(int fields[2])
其中的
fields
是我們傳入的數組,也是一個傳出參數。
fields[0]
是讀端;
fields[1]
是寫端。
對于命名管道而言:
- 沒有任何關系的程序間也可以通信;
- 通過
系統調用建立。mkfifo
關于 " | "
在Linux系統中," | " 就代表了管道。
比如說我們想通過檢視Linux系統中的使用者資訊:
cat /etc/passwd
:
我們會看到非常多的資訊。
此時,我隻想看
root
使用者的資訊,該怎麼做呢?
cat /etc/passwd | grep root
:
對于該指令的解讀是:
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);
}
運作結果對比: