• 正文
    • 相關(guān)要點(diǎn)
    • 優(yōu)缺點(diǎn)
    • 內(nèi)部實(shí)現(xiàn)
    • 應(yīng)用案例
    • 相關(guān)參考
    • 思考技術(shù),也思考人生
  • 推薦器件
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

Linux-C編程 進(jìn)程通信 以文件讀寫的方式和進(jìn)程通訊

2020/12/15
176
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

快速上手popen()

?

該函數(shù)用于運(yùn)行指定命令,并且讓剛啟動(dòng)的程序看起來(lái)像文件一樣可以被讀寫。

2 個(gè) demo

1) 從外部程序中讀數(shù)據(jù):

int?main(int?argc,?char?**argv)
{
????FILE?*fp;
????char?buf[100];
????int?i?=?0;

????fp?=?popen("ls?-1X",?"r");

????if?(fp?!=?NULL)?{
????????while(fgets(buf,?100,?fp)?!=?NULL)?{
????????????printf("%d:?%s",?i++,?buf);
????????}
????????pclose(fp);
????????return?0;
????}
????return?1;
}

運(yùn)行效果:

$?./001_popen_r?
0:?001_popen_r
1:?002_popen_w
2:?001_popen_r.c
3:?002_popen_w.c
4:?004_popen_intern.c

2) 寫數(shù)據(jù)到外部程序:

int?main(int?argc,?char?*argv)
{
????FILE?*fp?=?NULL;
????char?buffer[BUFSIZE];

????sprintf(buffer,?"hello?worldn");

????fp?=?popen("od?-tcx1",?"w");
????if?(fp?!=?NULL)?{
????????fwrite(buffer,?sizeof(char),?strlen(buffer),?fp);
????????pclose(fp);
????????return?0;
????}
????return?1;
}

運(yùn)行效果:

0000000???h???e???l???l???o???????w???o???r???l???d??n
?????????68??65??6c??6c??6f??20??77??6f??72??6c??64??0a
0000014

相關(guān)要點(diǎn)

函數(shù)原型

FILE?*popen(const?char?*command,?const?char?*type);

popen() 會(huì)先執(zhí)行 fork,然后調(diào)用 exec 執(zhí)行 command,并且返回一個(gè)標(biāo)準(zhǔn) I/O 文件指針。

type = "r":

  • 文件指針連接到 command 的標(biāo)準(zhǔn)輸出。

type = "w":

  • 文件指針連接到 command 的標(biāo)準(zhǔn)輸入。

點(diǎn)擊查看大圖

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 由于調(diào)用了 shell,所以可以支持通配符 (例如*.c) 等各種 shell 擴(kuò)展特性;減少了代碼量;

缺點(diǎn):

  • 要啟動(dòng) 2 個(gè)程序:shell 和 目標(biāo)程序,調(diào)用成本略高,比起直接 exec 某個(gè)程序來(lái)說(shuō)要慢一些;

內(nèi)部實(shí)現(xiàn)

popen() 的內(nèi)部實(shí)現(xiàn)思路如下:

FILE?*_popen(const?char?*command,?const?char?*type)
{
????pipe()
????fork();
????if?(pid?>?0)
????????close()?child's?fd
????????return?fdopen()?parent's?fd
????else
????????close(parent's?fd)
????????dup2()?child's?data?fd?to?stdin?or?stdout
????????close()?child's?fd
????????exec("/bin/sh?-c")?command
}
  1. 創(chuàng)建一個(gè)管道,用于父子進(jìn)程間的通訊;父進(jìn)程:
    • 關(guān)閉未使用的管道端;返回父進(jìn)程數(shù)據(jù)管道端的 FILE *, 它可能連接父進(jìn)程的 stdin / stdout;

子進(jìn)程:

  • 關(guān)閉未使用的管道端;重定位子進(jìn)程的數(shù)據(jù)管道端到 stdin / stdout;執(zhí)行目標(biāo)命令;

初步的代碼實(shí)現(xiàn):

FILE?*_popen(const?char?*command,?const?char?*type)
{
????int?pfp[2];
????int?parent_end,?child_end;
????int?pid;

????if?(*type?==?'r')?{
????????parent_end?=?READ;
????????child_end?=?WRITE;
????}?else?if?(*type?==?'w')?{
????????parent_end?=?WRITE;
????????child_end?=?READ;
????}?else?{
????????return?NULL;
????}

????pipe(pfp);
????pid?=?fork();
????if?(pid?>?0?)?{
????????close(pfp[child_end]);
????????return?fdopen(pfp[parent_end],?type);
????}?else?{
????????close(pfp[parent_end]);
????????dup2(pfp[child_end],?child_end);
????????close(pfp[child_end]);
????????execl("/bin/sh",?"sh",?"-c",?command,?NULL);
????????exit(0);
????}
????return?NULL;
}

這里的實(shí)現(xiàn)有一些不足的地方,例如:

為了便于閱讀,省略了錯(cuò)誤檢查;

沒(méi)有保存子進(jìn)程的 pid,后續(xù)無(wú)法使用 wait() 進(jìn)行收尸;

一個(gè)進(jìn)程可能調(diào)用 popen() 多次,需要用數(shù)組 / 鏈表來(lái)存儲(chǔ)所有子進(jìn)程的 pid;

更完善的實(shí)現(xiàn)可以參考:

https://android.googlesource.com/platform/bionic/+/3884bfe9661955543ce203c60f9225bbdf33f6bb/libc/unistd/popen.c

應(yīng)用案例

開源軟件 MJPG-steamer 為例。

MJPG-streamer 是什么?

簡(jiǎn)單地說(shuō),就是一個(gè)開源的流媒體服務(wù)器

https://github.com/jacksonliam/mjpg-streamer

通過(guò) mjpg-streamer,你可以通過(guò) PC 瀏覽器訪問(wèn)到板子上的攝像頭圖像。

MJPG-streamer 就是通過(guò) popen() 來(lái)支持 CGI 功能的:

CGI 是早期出現(xiàn)的一種簡(jiǎn)單、流行的服務(wù)端應(yīng)用程序執(zhí)行接口,http server 通過(guò)運(yùn)行 CGI 程序來(lái)完成更復(fù)雜的處理工作,在 MJPG-streamer . 里的相關(guān)代碼如下:

plugins/output_http/httpd.c

void?execute_cgi(int?id,?int?fd,?char?*parameter,?char?*query_string)
{
????//?prepare

????//?執(zhí)行瀏覽器指定的?CGI?程序
????f?=?popen(buffer,?"r");

????//?獲得?CGI?程序的輸出
????while((i?=?fread(buffer,?1,?sizeof(buffer),?f))?>?0)?{
????????if?(write(fd,?buffer,?i)?<?0)?{
????????????fclose(f);
????????????free(buffer);
????????????close(lfd);
????????????return;
????????}
????}

}

這里只是簡(jiǎn)單地了解一下 MJPG-Streamer,有興趣的小伙伴們自行閱讀更多的代碼吧。

相關(guān)參考

Unix-Linux 編程實(shí)踐教程 / 11.4 popen: 讓進(jìn)程看似文件

Linux 程序設(shè)計(jì)(第 4 版) / 13.3 將輸出送往 popen

Unix 環(huán)境高級(jí)編程第 3 版 / 15.3 函數(shù) popen 和 pclose

HTTP 權(quán)威指南

思考技術(shù),也思考人生

要學(xué)習(xí)技術(shù),更要學(xué)習(xí)如何生活。

你和我各有一個(gè)蘋果,如果我們交換蘋果的話,我們還是只有一個(gè)蘋果。但當(dāng)你和我各有一個(gè)想法,我們交換想法的話,我們就都有兩個(gè)想法了。

對(duì) 嵌入式系統(tǒng) (Linux、RTOS、OpenWrt、Android) 和 開源軟件 感興趣,關(guān)注公眾號(hào):嵌入式 Hacker。

覺(jué)得文章對(duì)你有價(jià)值,還請(qǐng)多多 轉(zhuǎn)發(fā)。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
XC7A75T-2FGG676C 1 AMD Xilinx Field Programmable Gate Array, 5900 CLBs, 1286MHz, 75520-Cell, CMOS, PBGA676, FBGA-676

ECAD模型

下載ECAD模型
$1103.58 查看
LFE3-70EA-8FN484C 1 Lattice Semiconductor Corporation Field Programmable Gate Array, 500MHz, 67000-Cell, PBGA484, 23 X 23 MM, LEAD FREE, FPBGA-484

ECAD模型

下載ECAD模型
$73.57 查看
A3P400-FGG256 1 Microsemi FPGA & SoC Field Programmable Gate Array, 400000 Gates, CMOS, PBGA256, 1 MM PITCH, GREEN, FBGA-256
$291.51 查看

相關(guān)推薦