父子程序的雙向通訊簡明解讀(c程式)。

2021-08-01 09:02:33 字數 4426 閱讀 8938

你可以用system, popen,pipe 實現兩個程序的通訊!

1.system 呼叫的實現方式

//下面是uclibc-0.9.33 的實現,為方便閱讀,**有刪減。

//由以下**可知, system 就是呼叫 fork() 函式,

//子程序呼叫execl 執行command命令;

//主程序等待子程序完成。

int __libc_system(const char *command)

//主程序等待子程序結束

if (wait4(pid, &wait_val, 0, 0) == -1) return -1

return wait_val;

}

優點: system() 呼叫使用簡單,

缺點:當我們需要與程式互動時,system 就不能勝任了。

system() 函式呼叫相當於批命令呼叫或者執行程式命令

popen 函式可以按讀的方式或者寫的方式開啟管道,通過管道與command 程式進行通訊。

2.1 popen() 函式的實現方式

**有刪減, 英文是原注釋/**/,含中文行為本人注釋//。

由以下**可知,popen 就是建立乙個pipe, 父程序開啟pipe的一端,子程序開啟另一端。

子程序把pipe 端與stdin 或 stdout 關聯,執行 execl(command) 命令, 這樣通過pipe完成

了父子程序的通訊。

它就等價於linux-shell 中的重定向》 或者<

file *popen(const

char *command, const

char *modes)

}if (pipe(pipe_fd))

child_fd = pipe_fd[zeroorone]; //使得child_fd, parent_fd 為管道的兩端

parent_fd = pipe_fd[1-zeroorone]; //寫模式(父寫子讀),pipe_fd[0]->child_fd, pipe_fd[1]->parent_fd

//讀模式(父讀子寫),pipe_fd[0]->parent_fd,pipe_fd[1]->child_fd

if (!(fp = fdopen(parent_fd, modes)))

if ((pid = vfork()) == 0)

execl("/bin/sh", "sh", "-c", command, (char *)0);

_exit(127);

}if (pid > 0)

/* if we get here, vfork failed. */

fclose(fp); /* will close parent_fd. */

free_pi:

free(pi);

ret_null:

return

null;

}

補充: dup2(child_fd, 0); 就是把child_fd 複製到stdin上,這樣子程序從stdin讀取,實際上是讀取的child_fd, 就是所謂的輸入重定向.

同理:dup2(child_fd, 1); 就是把child_fd 複製到stdout上,這樣子程序向stdout輸出資訊,實際上是向child_fd輸出資訊.

哈哈哈!!! 使用了偷梁換柱之法. 由此騙過了子程序. 子程序以為向stdout列印,實際上打到了管道的一端, 另一端連線的是父程序的讀端,被父程序讀走了. 同理,父程序打管道的一端搭在了子程序的stdin端, 父程序向管道寫東西,子程序以為是從stdin讀進來的呢! 很有趣! 是嗎? 這是欺騙的手段,或者溝通的橋梁!

2.2 popen應用1: 將ls命令的輸出逐行讀出到記憶體,再顯示到螢幕上,

//本例演示了popen,pclose的使用

#include 

int main()

; fp = popen("ls","r");

if(null == fp)

while(fgets(buf, 20, fp) != null)

pclose(fp);

return

0;}

popen按讀方式開啟」ls」程式,已經將ls 輸出重定向到管道輸入端,我們的fp 是管道輸出端, 所以程式執行達到了目的.

2.3 popen應用2: 將應用程式的輸出儲存到乙個變數中。

//執行乙個shell命令,輸出結果逐行儲存在vecstr中,並返回行數

int readpipe(const

char *cmd, vector

&vecstr)

char buf[1024]; //設定乙個合適的長度,以儲存每一行輸出

while (fgets(buf, sizeof(buf), pp) != null)

vecstr.push_back(buf);

}pclose(pp); //關閉管道

return vecstr.size();

}

//但是,當我們即想向pipe 寫, 又想從pipe 讀, 現成的popen 就不能勝任了,

//popen 只能建立一條管道,或者是讀管道,或者是寫管道。

//要想同時與程式實現讀,寫操作(雙向互動), 需要我們自己書寫程序**.

//是的,通過兩個管道,乙個讀管道,乙個寫管道。下面給乙個範例.

3.1 範例1,重定向子程序的stdin,stdout

這個範例演示了我們的父程序與子程序通訊, 並列印了與子程序的通訊內容.

子程序並不知道與它通訊的到底是人通過鍵盤跟它下命令,還是程式在跟它下命令,它的描述符已經被接到管道上了.

#include 

#include

#include

#include

#include

#include

#define read 0

#define write 1

// 注,回車有非常重要的作用,否則對端會阻塞在讀上而不能繼續

// printf 沒有"\n"也不會顯示列印,除非用fflush或程式退出。不信可以試試。

// 這些都是緩衝若得禍!

int doubleinteract(const

char* cmdstring)

else

/* child process, 關閉寫管道的寫端,讀管道的讀端,與父程序正好相對 */

close(pipew[read]);

}if (piper[write] != stdout_fileno)

close(piper[write]);

}execl("/bin/sh", "sh", "-c", cmdstring, (char*)0);

exit(127);

}return0;}

int main()

//這裡給乙個myshell指令碼範例,從鍵盤輸入,向螢幕輸出.如下:

#!/bin/bash

echo

"hello! say something"

read

echo

"you say:$reply,bye bye!"

3.2另乙個簡單的父子程序雙向互動的例子,

該例沒有採用把管道端向輸入或輸出重定向的技術,

而是直接用pipe通訊, 父子程序協作,將數值從0加到10;

規則是從管道中拿到數,加1,再把數推出去。

$ cat main.cpp

#include

#include

#include

#include

#include

#define read 0

#define write 1

int main(void)

if(pid == 0) //子程序

while(x

<=9);

//讀寫完成後,關閉管道

close(pipe1[read]);

close(pipe2[write]);

exit(0);

} else

if(pid > 0) //父程序

while(x

<=9);

//讀寫完成後,關閉管道

close(pipe1[write]);

close(pipe2[read]);

waitpid(pid,null,0);

exit(0);

} }

範例均經過測試,可直接使用。

Linux程序通訊之訊息佇列的雙向通訊

include include include int msgget key t key,int msg 其中msg 表示生成訊息佇列的方式和許可權,一般使用的話,有三個常用引數,ipc creat ipc excl umask,ipc creat單獨使用時代表如果沒有此訊息佇列,那就生成乙個,如果...

通過匿名管道在程序間雙向通訊

由於匿名管道只能是單向的,因此要建立雙向通訊必須建立2個管道。父程式 view plaincopy to clipboardprint?int main closehandle hreadpipe1 closehandle hreadpipe2 closehandle hwritepipe1 clo...

IPC下的雙向通訊 訊息佇列

訊息佇列 訊息佇列提供了一種從乙個程序向另乙個程序傳送乙個資料塊的方法。每個資料塊都被認為是有乙個型別,接收者程序接收的資料塊可以有不同的型別值。我們可以通過傳送訊息來避免命名管道的同步和阻塞問題。訊息佇列與管道不同的是,訊息佇列是基於訊息的,而管道是基於位元組流的,且訊息佇列的讀取不一定是先 入先...