加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 相關(guān)要點(diǎn)
    • 優(yōu)缺點(diǎn)
    • 內(nèi)部實(shí)現(xiàn)
    • 應(yīng)用案例
    • 相關(guān)參考
    • 思考技術(shù),也思考人生
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

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

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

快速上手popen()

?

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

2 個 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() 會先執(zhí)行 fork,然后調(diào)用 exec 執(zhí)行 command,并且返回一個標(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):

  • 要啟動 2 個程序:shell 和 目標(biāo)程序,調(diào)用成本略高,比起直接 exec 某個程序來說要慢一些;

內(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)建一個管道,用于父子進(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)有一些不足的地方,例如:

為了便于閱讀,省略了錯誤檢查;

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

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

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

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

應(yīng)用案例

開源軟件 MJPG-steamer 為例。

MJPG-streamer 是什么?

簡單地說,就是一個開源的流媒體服務(wù)器

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

通過 mjpg-streamer,你可以通過 PC 瀏覽器訪問到板子上的攝像頭圖像。

MJPG-streamer 就是通過 popen() 來支持 CGI 功能的:

CGI 是早期出現(xiàn)的一種簡單、流行的服務(wù)端應(yīng)用程序執(zhí)行接口,http server 通過運(yùn)行 CGI 程序來完成更復(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;
????????}
????}

}

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

相關(guān)參考

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

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

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

HTTP 權(quán)威指南

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

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

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

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

覺得文章對你有價值,還請多多 轉(zhuǎn)發(fā)。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
XC7A200T-2FBG676I 1 AMD Xilinx Field Programmable Gate Array, 16825 CLBs, 1286MHz, 215360-Cell, CMOS, PBGA676, FBGA-676

ECAD模型

下載ECAD模型
$356.55 查看
XC6SLX9-2CPG196I 1 AMD Xilinx Field Programmable Gate Array, 715 CLBs, 667MHz, 9152-Cell, CMOS, PBGA196, 8 X 8 MM, 0.50 MM PITCH, LEAD FREE, BGA-196
$50.98 查看
XC6SLX45-2CSG324I 1 AMD Xilinx Field Programmable Gate Array, 3411 CLBs, 667MHz, 43661-Cell, CMOS, PBGA324, 15 X 15 MM, 0.80 MM PITCH, LEAD FREE, BGA-324
$65.17 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜