Linux系统编程-管道入门
晚上好,繼續記錄我的學習心得。
當你厭倦了自己的目標時,怎樣繼續保持專注?
誤區:
成功人士說的都是自己如何“滿懷熱情”去努力實現他們的目標。不管是在商業、體育還是藝術界,我們聽到的都是“一切都歸結于激情”或者“你必須真的渴望得到它”之類的說法。
這讓我們認為成功人士會有無限的激情,如果我們感到自己激情消退了,仿佛說明了我們不如他們,這讓人很沮喪。
事實是:
他們沒把話說完整。成功人士也會和普通人一樣感到激情消退,這是任何人都無法逃脫的。熟能生巧。你練習的次數越多,它就變得越無聊,越像是機械地重復。
成功的最大威脅不是失敗,而是倦怠。
我們厭倦了好習慣,因為它們不再讓我們開心。就好像寫作一樣,長期來看會給我帶來好的結果,但是總會有那么幾天我也會感覺寫作是枯燥的。
成功人士更強大的地方在于:
盡管感到枯燥乏味,他們仍想辦法堅持下去。
對抗枯燥感的小技巧:
在開始做自己不不想做的事情時: 先一邊聽音樂一邊干,15分鐘后再摘下耳機。必須控制時間,因為聽得越多,音樂給你帶來的滿足感就會越少。
以下是正文,先明確一下正文的目的:
舉例說明 Linux 下父子進程如何使用管道進行通訊。
目錄:
一、概述-?1.?IPC?工具分類-?2.?什么是管道-?3.?管道的?2?個局限性-?4.?管道是最常用的?IPC-?5.?使用?pipe()?創建管道二、一個簡單的例子?(simple_pipe.c)-?1.?代碼分析-?2.?相關要點三、參考書籍四、歡迎加入我的微信群一、概述
1. IPC 工具分類
通信類 (communication):這類工具關注進程之間的數據交換。
同步類 (synchronization):這類工具關注進程和線程操作之間的同步。
信號類 (signal):盡管信號的主要作用并不在此,但在特定場景下仍然可以將它作為一種同步技術。更罕見的是信號還可以作為一種通信技術:信號編號本身是一種形式的信息,并且可以在實時信號上綁定數據。
2. 什么是管道
當從一個進程連接數據流到另一個進程時,我們使用術語管道 (pipe),也叫無名管道 (unnamed pipe)。通常是把一個進程的輸出通過管道連接到另一個進程的輸入。
管道是 UNIX 系統最古老的 IPC ,所有 UNIX 系統都提供這種通信機制。
3. 管道的 2 個局限性
歷史上,它是半雙工的 (即數據只能在一個方向上流動)。現在,某些系統提供全雙工管道,但是為了可移植性,我們不應預先假定系統支持全雙工管道。
管道只能在具有公共祖先的兩個進程之間使用。通常,一個管道由一個進程創建,在進程調用 fork 之后,這個管道就能在父進程和子進程之間使用了。
4. 盡管有這 2 個局限性,半雙工管道仍是最常用的 IPC
每當在管道中鍵入一個命令序列,讓 shell 執行時,shell 都會為每一條命令單獨創建一個進程,然后用管道將前一條命令進程的標準輸出與后一條命令的標準輸入相連接。shell 負責安排兩個命令的標準輸入和標準輸出:
cmd1 的標準輸入來自終端鍵盤;
cmd1 的標準輸出傳遞給 cmd2,作為它的標準輸入;
cmd2 的標準輸出連接到終端屏幕;
shell 所做的工作實際上是對標準輸入和標準輸出流進行了重新連接,使數據流從鍵盤輸入通過兩個命令最終輸出到屏幕上。
以 "$ ls | wc -l" 為例:
5. 使用 pipe() 創建管道
$?man?2?pipe NAMEpipe,?pipe2?-?create?pipe SYNOPSIS#include?<unistd.h>int?pipe(int?filedes[2]);成功的 pipe() 調用會在數組 filedes 中返回兩個打開的文件描述符:一個表示管道的讀取端 (filedes[0]),另一個表示管道的寫入端 (filedes[1])。
與所有文件描述符一樣,可以使用 read() 和 write() 在管道上執行 I/O 操作。一旦向管道的寫入端寫入數據之后立即就能從管道的讀取端讀取數據。
二、一個簡單的例子 (simple_pipe.c)
1. 代碼分析
下面的程序演示了如何將管道用于父進程和子進程之間的通信:
int?main(int?argc,?char?*argv[]) {int?pfd[2];char?buf[BUF_SIZE];ssize_t?numRead;if?(argc?!=?2?||?strcmp(argv[1],?"--help")?==?0)usageErr("%s?string\n",?argv[0]);if?(pipe(pfd)?==?-1)????/*?Create?the?pipe?*/errExit("pipe");switch?(fork())?{case?-1:errExit("fork");case?0:?????????????/*?Child??-?reads?from?pipe?*/if?(close(pfd[1])?==?-1)????????????/*?Write?end?is?unused?*/errExit("close?-?child");for?(;;)?{??????????????/*?Read?data?from?pipe,?echo?on?stdout?*/numRead?=?read(pfd[0],?buf,?BUF_SIZE);if?(numRead?==?-1)errExit("read");if?(numRead?==?0)break;??????????????????????/*?End-of-file?*/if?(write(STDOUT_FILENO,?buf,?numRead)?!=?numRead)fatal("child?-?partial/failed?write");}//?cleanup...default:????????????/*?Parent?-?writes?to?pipe?*/if?(close(pfd[0])?==?-1)????????????/*?Read?end?is?unused?*/errExit("close?-?parent");if?(write(pfd[1],?argv[1],?strlen(argv[1]))?!=?strlen(argv[1]))fatal("parent?-?partial/failed?write");if?(close(pfd[1])?==?-1)????????????/*?Child?will?see?EOF?*/errExit("close");wait(NULL);?????????????????????????/*?Wait?for?child?to?finish?*/exit(EXIT_SUCCESS);} }運行效果:
$?./simple_pipe?'msg?from?parent' msg?from?parent單個進程中的管道幾乎沒有任何用處。一般來講都是使用管道讓兩個進程進行通信。為了讓兩個進程通過管道進行連接,在調用完 pipe() 之后可以調用 fork(),子進程會繼承父進程的文件描述符的副本:
雖然父進程和子進程都可以從管道中讀取和寫入數據,但一般不這么使用管道。通常,在 fork() 之后,其中一個進程應該立即關閉管道的寫入端的描述符,另一個則應該關閉讀取端的描述符。在上面的例子中,父進程向子進程傳輸數據,所以它關閉了管道的讀取端的描述符 filedes[0],而子進程則關閉管道的寫入端的描述符 filedes[1]:
2. 相關要點
父子進程從同一個管道中讀取和寫入數據這種做法不好的原因是:
如果兩個進程同時試圖從管道中讀取數據,那么就無法確定哪個進程會首先讀取成功——兩個進程競爭數據了,即會產生競爭。
使用管道進行雙向通信更加簡單的方法是:創建兩個管道,在兩個進程之間發送數據的兩個方向上各使用一個。
管道可以用于任意兩個(或更多)相關進程之間的通信,只要在創建子進程的系列 fork() 調用之前通過一個共同的祖先進程創建管道即可。
關閉未使用管道文件描述符對于正確使用管道非常重要的:
當 read 一個寫入端 (write end) 已被關閉的管道時,在所有數據都被讀取后,read 會返回 0,這時讀取進程就知道文件已結束了。
如果讀取進程沒有關閉管道的寫入端,那么在其他進程關閉了寫入描述符之后,讀取進程也不會看到文件結束,即使它讀完了管道中的所有數據。
當 write 一個讀取端已被關閉的管道,內核會向寫入進程發送一個SIGPIPE 信號。在默認情況下,這個信號會殺死進程。但進程可以捕獲或忽略該信號,這時管道上的 write() 操作因 EPIPE 錯誤而失敗。收到SIGPIPE 信號 和獲得 EPIPE 錯誤對于可以起到標示管道狀態的作用。
在寫管道時,常量 PIPE_BUF 規定了內核的管道緩沖區大小。
如果對管道調用 write(),而且要求寫的字節數小于等于 PIPE_BUF,則此操作不會與其他進程對同一管道的 write 操作交叉進行。
如果有多個進程同時寫一個管道,且要求寫入的字節數超過 PIPE_BUF,則所寫的數據可能會與其他進程所寫的數據交錯在一起。
三、相關參考
《UNIX 環境高級編程 - 15.2 管道》
《Linux / UNIX 系統編程手冊 - 44.管道和FIFO》
《Linux 程序設計 - 13.進程間通信:管道》
《UNIX-Linux編程實踐教程 - 10.I/O 重定向和管道》
更多值得學習的知識點:
將管道作為一種進程同步的方法;
使用管道連接過濾器;
通過管道與 shell 命令進行通信:popen();
管道和 stdio 緩沖;
命名管道:FIFO; ...
鑒于大多數人的注意力無法在一篇文章里上集中太久,更多的內容請大家自行去閱讀吧,不是自己理解到的東西是消化不了的。有機會的話我會把更多的讀書心得放在后面的文章。
你和我各有一個蘋果,如果我們交換蘋果的話,我們還是只有一個蘋果。但當你和我各有一個想法,我們交換想法的話,我們就都有兩個想法了。如果你也對 嵌入式系統和開源軟件 感興趣,并且想和更多人互相交流學習的話,請關注我的公眾號:嵌入式 Hacker,一起來學習吧,無論是 關注或轉發 , 還是賞賜,都是對作者莫大的支持,感謝 各位的大拇指「在看」 ,,祝工作順利,家庭和睦~
? 推薦閱讀:
??專輯|Linux文章匯總
??專輯|程序人生
??專輯|C語言
嵌入式Linux
微信掃描二維碼,關注我的公眾號
總結
以上是生活随笔為你收集整理的Linux系统编程-管道入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用sklearn加载波士顿房价数据集
- 下一篇: 电脑wifi距离测试软件,wifi测速工