新聞中心
淺談Android之Linux pipe/epoll
管道
創(chuàng)新互聯(lián)建站主營墊江網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都App定制開發(fā),墊江h(huán)5重慶小程序開發(fā)公司搭建,墊江網(wǎng)站營銷推廣歡迎墊江等地區(qū)企業(yè)咨詢
管道的概念:
管道是一種最基本的IPC機(jī)制,作用于有血緣關(guān)系的進(jìn)程之間,完成數(shù)據(jù)傳遞。調(diào)用pipe系統(tǒng)函數(shù)即可創(chuàng)建一個(gè)管道。有如下特質(zhì):
1. 其本質(zhì)是一個(gè)偽文件(實(shí)為內(nèi)核緩沖區(qū))
2. 由兩個(gè)文件描述符引用,一個(gè)表示讀端,一個(gè)表示寫端。
3. 規(guī)定數(shù)據(jù)從管道的寫端流入管道,從讀端流出。
管道的原理: 管道實(shí)為內(nèi)核使用環(huán)形隊(duì)列機(jī)制,借助內(nèi)核緩沖區(qū)(4k)實(shí)現(xiàn)。
管道的局限性:
① 數(shù)據(jù)自己讀不能自己寫。
② 數(shù)據(jù)一旦被讀走,便不在管道中存在,不可反復(fù)讀取。
③ 由于管道采用半雙工通信方式。因此,數(shù)據(jù)只能在一個(gè)方向上流動(dòng)。
④ 只能在有公共祖先的進(jìn)程間使用管道。
常見的通信方式有,單工通信、半雙工通信、全雙工通信。
簡單來說這個(gè)管道是一個(gè)文件,但又和普通文件不通:管道緩沖區(qū)大小一般為1頁,即4K字節(jié),管道分讀端和寫端,讀端負(fù)責(zé)從管道拿數(shù)據(jù),當(dāng)數(shù)據(jù)為空時(shí)則阻塞;寫端向管道寫數(shù)據(jù),當(dāng)管道緩存區(qū)滿時(shí)則阻塞。
pipe函數(shù)
創(chuàng)建管道
int pipe(int pipefd[2]); 成功:0;失敗:-1,設(shè)置errno
函數(shù)調(diào)用成功返回r/w兩個(gè)文件描述符。無需open,但需手動(dòng)close。規(guī)定:fd[0] → r; fd[1] → w,就像0對(duì)應(yīng)標(biāo)準(zhǔn)輸入,1對(duì)應(yīng)標(biāo)準(zhǔn)輸出一樣。向管道文件讀寫數(shù)據(jù)其實(shí)是在讀寫內(nèi)核緩沖區(qū)。
管道創(chuàng)建成功以后,創(chuàng)建該管道的進(jìn)程(父進(jìn)程)同時(shí)掌握著管道的讀端和寫端。如何實(shí)現(xiàn)父子進(jìn)程間通信呢?通常可以采用如下步驟:
1.?父進(jìn)程調(diào)用pipe函數(shù)創(chuàng)建管道,得到兩個(gè)文件描述符fd[0]、fd[1]指向管道的讀端和寫端。
2.?父進(jìn)程調(diào)用fork創(chuàng)建子進(jìn)程,那么子進(jìn)程也有兩個(gè)文件描述符指向同一管道。
3.?父進(jìn)程關(guān)閉管道讀端,子進(jìn)程關(guān)閉管道寫端。父進(jìn)程可以向管道中寫入數(shù)據(jù),子進(jìn)程將管道中的數(shù)據(jù)讀出。由于管道是利用環(huán)形隊(duì)列實(shí)現(xiàn)的,數(shù)據(jù)從寫端流入管道,從讀端流出,這樣就實(shí)現(xiàn)了進(jìn)程間通信。
管道的讀寫行為
使用管道需要注意以下4種特殊情況(假設(shè)都是阻塞I/O操作,沒有設(shè)置O_NONBLOCK標(biāo)志):
1.?如果所有指向管道寫端的文件描述符都關(guān)閉了(管道寫端引用計(jì)數(shù)為0),而仍然有進(jìn)程從管道的讀端讀數(shù)據(jù),那么管道中剩余的數(shù)據(jù)都被讀取后,再次read會(huì)返回0,就像讀到文件末尾一樣。
2.?如果有指向管道寫端的文件描述符沒關(guān)閉(管道寫端引用計(jì)數(shù)大于0),而持有管道寫端的進(jìn)程也沒有向管道中寫數(shù)據(jù),這時(shí)有進(jìn)程從管道讀端讀數(shù)據(jù),那么管道中剩余的數(shù)據(jù)都被讀取后,再次read會(huì)阻塞,直到管道中有數(shù)據(jù)可讀了才讀取數(shù)據(jù)并返回。
3.?如果所有指向管道讀端的文件描述符都關(guān)閉了(管道讀端引用計(jì)數(shù)為0),這時(shí)有進(jìn)程向管道的寫端write,那么該進(jìn)程會(huì)收到信號(hào)SIGPIPE,通常會(huì)導(dǎo)致進(jìn)程異常終止。當(dāng)然也可以對(duì)SIGPIPE信號(hào)實(shí)施捕捉,不終止進(jìn)程。具體方法信號(hào)章節(jié)詳細(xì)介紹。
4.?如果有指向管道讀端的文件描述符沒關(guān)閉(管道讀端引用計(jì)數(shù)大于0),而持有管道讀端的進(jìn)程也沒有從管道中讀數(shù)據(jù),這時(shí)有進(jìn)程向管道寫端寫數(shù)據(jù),那么在管道被寫滿時(shí)再次write會(huì)阻塞,直到管道中有空位置了才寫入數(shù)據(jù)并返回。
總結(jié):
① 讀管道: 1. 管道中有數(shù)據(jù),read返回實(shí)際讀到的字節(jié)數(shù)。
2. 管道中無數(shù)據(jù):
(1) 管道寫端被全部關(guān)閉,read返回0 (好像讀到文件結(jié)尾)
(2) 寫端沒有全部被關(guān)閉,read阻塞等待(不久的將來可能有數(shù)據(jù)遞達(dá),此時(shí)會(huì)讓出cpu)
② 寫管道: 1. 管道讀端全部被關(guān)閉, 進(jìn)程異常終止(也可使用捕捉SIGPIPE信號(hào),使進(jìn)程不終止)
2. 管道讀端沒有全部關(guān)閉:
(1) 管道已滿,write阻塞。
(2) 管道未滿,write將數(shù)據(jù)寫入,并返回實(shí)際寫入的字節(jié)數(shù)。
Epoll的概念
Epoll可以使用一次等待監(jiān)聽多個(gè)描述符的可讀/可寫狀態(tài).等待返回時(shí)攜帶了可讀的描述符或者自定義的數(shù)據(jù).不需要為每個(gè)描述符創(chuàng)建獨(dú)立的線程進(jìn)行阻塞讀取,
Linux系統(tǒng)中的epoll機(jī)制為處理大批量句柄而作了改進(jìn)的poll,是Linux下多路復(fù)用IO接口select/poll的增強(qiáng)版本,它能顯著減少程序在大量并發(fā)連接中只有少量活躍的情況下的系統(tǒng)CPU利用率
(01) pipe(wakeFds),該函數(shù)創(chuàng)建了兩個(gè)管道句柄。
(02) mWakeReadPipeFd=wakeFds[0],是讀管道的句柄。
(03) mWakeWritePipeFd=wakeFds 1 ,是寫管道的句柄。
(04) epoll_create(EPOLL_SIZE_HINT)是創(chuàng)建epoll句柄。
(05) epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, eventItem),它的作用是告訴mEpollFd,它要監(jiān)控mWakeReadPipeFd文件描述符的EPOLLIN事件,即當(dāng)管道中有內(nèi)容可讀時(shí),就喚醒當(dāng)前正在等待管道中的內(nèi)容的線程。
回到Android中的epoll大致流程如下:
Looper.loop?- MessageQueue.nativePollOnce
epoll_create() ? epoll_ctl() 注冊事件的回調(diào)
looper.pollInner() - epoll_wait() 等待接受事件喚醒的回調(diào)
MessageQueue.enqueueMessage(Message msg, long when) ? ?- ?MessageQueue.nativeWake(long ptr)
參考鏈接如下
鏈接:
鏈接:
Linux管道命令(pipe)
管道命令就是用來連接多條指令的,前一條指令的輸出流向會(huì)作為后一條指令的操作對(duì)象。
管道命令的操作符是:|,它只能處理由前面一條指令傳出的正確輸出信息,對(duì)錯(cuò)誤信息是沒有直接處理能力的。然后,傳遞給下一條指令,作為操作對(duì)象。
基本格式:
指令1 | 指令2 | …
【指令1】正確輸出,作為【指令2】的輸入,然后【指令2】的輸出作為【指令3】的輸入,如果【指令3】有輸出,那么輸出就會(huì)直接顯示在屏幕上面了。通過管道之后【指令1】和【指令2】的正確輸出是不顯示在屏幕上面的。
【提醒注意】
管道命令只能處理前一條指令的正確輸出,不能處理錯(cuò)誤輸出;
管道命令的后一條指令,必須能夠接收標(biāo)準(zhǔn)輸入流命令才能執(zhí)行。
使用示例
1、分頁顯示/etc目錄中內(nèi)容的詳細(xì)信息
$ ls -l /etc | more
2、將一個(gè)字符串輸入到一個(gè)文件中
$ echo “hello world” | cat hello.txt
linux pipe函數(shù)怎么用
pipe函數(shù)的原型是:
int pipe(int file_descriptor[2]);
它需要的頭文件是:#include unistd.h
pipe函數(shù)的參數(shù)是一個(gè)有兩個(gè)文件描述符(整型)元素的數(shù)組,pipe函數(shù)在數(shù)組中填入兩個(gè)新的文件描述符后返回0,失敗則返回1。
怎么用呢,成功的調(diào)用pipe函數(shù)后,向file_descriptor[1]寫入的所有數(shù)據(jù),可以從file_descriptor[0]讀出來。使用情景:在一個(gè)調(diào)用fork的程序中,利用pipe從子進(jìn)程和父進(jìn)程之間傳遞數(shù)據(jù)。
注意:pipe函數(shù)的數(shù)組參數(shù)是文件描述符,不是文件流,所以不能用C標(biāo)準(zhǔn)庫函數(shù),而是應(yīng)該用read和write系統(tǒng)調(diào)用來讀寫數(shù)據(jù)。
示例:
int file_pipes[2];
if(pipe(file_pipes)==0) {
// 向file_pipes[1]寫數(shù)據(jù)
// 從file_pipes[0]讀數(shù)據(jù)
}
Linux系統(tǒng)編程—管道
Linux 實(shí)現(xiàn) IPC 其中的一種方式——管道
管道又分:
1、無名管道:無名管道只能用于有親緣關(guān)系的進(jìn)程。
2、有名管道:有名管道用于任意兩進(jìn)程間通信。
你就可以把管道理解成位于進(jìn)程內(nèi)核空間的“文件”。
給文件加引號(hào),是因?yàn)樗臀募_實(shí)很像,因?yàn)樗灿忻枋龇5撬_實(shí)又不是普通的本地文件,而是一種抽象的存在。
當(dāng)進(jìn)程使用 pipe 函數(shù),就可以打開位于內(nèi)核中的這個(gè)特殊“文件”。同時(shí) pipe 函數(shù)會(huì)返回兩個(gè)描述符,一個(gè)用于讀,一個(gè)用于寫。如果你使用 fstat函數(shù)來測試該描述符,可以發(fā)現(xiàn)此文件類型為 FIFO。
而無名管道的無名,指的就是這個(gè)虛幻的“文件”,它沒有名字。本質(zhì)上,pipe 函數(shù)會(huì)在進(jìn)程內(nèi)核空間申請一塊內(nèi)存(比如一個(gè)內(nèi)存頁,一般是 4KB),然后把這塊內(nèi)存當(dāng)成一個(gè)先進(jìn)先出(FIFO)的循環(huán)隊(duì)列來存取數(shù)據(jù),這一切都由操作系統(tǒng)幫助我們實(shí)現(xiàn)了。
pipe 函數(shù)打開的文件描述符是通過參數(shù)(數(shù)組)傳遞出來的,而返回值表示打開成功(0)或失敗(-1)。
它的參數(shù)是一個(gè)大小為 2 的數(shù)組。此數(shù)組的第 0 個(gè)元素用來接收以讀的方式打開的描述符,而第 1 個(gè)元素用來接收以寫的方式打開的描述符。也就是說,pipefd[0] 是用于讀的,而 pipefd[1] 是用于寫的。
打開了文件描述符后,就可以使用 read(pipefd[0]) 和 write(pipefd[1]) 來讀寫數(shù)據(jù)了。
注意事項(xiàng)
1、這兩個(gè)分別用于讀寫的描述符必須同時(shí)打開才行,否則會(huì)出問題。
2、如果關(guān)閉讀 (close(pipefd[0])) 端保留寫端,繼續(xù)向?qū)懚?(pipefd[1]) 端寫數(shù)據(jù)(write 函數(shù))的進(jìn)程會(huì)收到 SIGPIPE 信號(hào)。
3、如果關(guān)閉寫 (close(pipefd[1])) 端保留讀端,繼續(xù)向讀端 (pipefd[0]) 端讀數(shù)據(jù)(read 函數(shù)),read 函數(shù)會(huì)返回 0。
當(dāng)在進(jìn)程用 pipe 函數(shù)打開兩個(gè)描述符后,我們可以 fork 出一個(gè)子進(jìn)程。這樣,子進(jìn)程也會(huì)繼承這兩個(gè)描述符,而且這兩個(gè)文件描述符的引用計(jì)數(shù)會(huì)變成 2。
如果你需要父進(jìn)程向子進(jìn)程發(fā)送數(shù)據(jù),那么得把父進(jìn)程的 pipefd[0] (讀端)關(guān)閉,而在子進(jìn)程中把 pipefd[1] 寫端關(guān)閉,反之亦然。為什么要這樣做?實(shí)際上是避免出錯(cuò)。傳統(tǒng)上 pipe 管道只能用于半雙工通信(即一端只能發(fā),不能收;而另一端只能收不能發(fā)),為了安全起見,各個(gè)進(jìn)程需要把不用的那一端關(guān)閉(本質(zhì)上是引用計(jì)數(shù)減 1)。
步驟一:fork 子進(jìn)程
步驟二:關(guān)閉父進(jìn)程讀端,關(guān)閉子進(jìn)程寫端
父進(jìn)程 fork 出一個(gè)子進(jìn)程,通過無名管道向子進(jìn)程發(fā)送字符,子進(jìn)程收到數(shù)據(jù)后將字符串中的小寫字符轉(zhuǎn)換成大寫并輸出。
有名管道打破了無名管道的限制,進(jìn)化出了一個(gè)實(shí)實(shí)在在的 FIFO 類型的文件。這意味著即使沒有親緣關(guān)系的進(jìn)程也可以互相通信了。所以,只要不同的進(jìn)程打開 FIFO 文件,往此文件讀寫數(shù)據(jù),就可以達(dá)到通信的目的。
1、文件屬性前面標(biāo)注的文件類型是 p
2、代表管道文件大小是 0
3、fifo 文件需要有讀寫兩端,否則在打開 fifo 文件時(shí)會(huì)阻塞
通過命令 mkfifo 創(chuàng)建
通過函數(shù) mkfifo創(chuàng)建
函數(shù)返回 0 表示成功,-1 失敗。
例如:
cat 命令打印 test文件內(nèi)容
接下來你的 cat 命令被阻塞住。
開啟另一個(gè)終端,執(zhí)行:
然后你會(huì)看到被阻塞的 cat 又繼續(xù)執(zhí)行完畢,在屏幕打印 “hello world”。如果你反過來執(zhí)行上面兩個(gè)命令,會(huì)發(fā)現(xiàn)先執(zhí)行的那個(gè)總是被阻塞。
有兩個(gè)程序,分別是發(fā)送端 send 和接收端面 recv。程序 send 從標(biāo)準(zhǔn)輸入接收字符,并發(fā)送到程序 recv,同時(shí) recv 將接收到的字符打印到屏幕。
發(fā)送端
接收端
編譯
運(yùn)行
因?yàn)?recv 端還沒打開test文件,這時(shí)候 send 是阻塞狀態(tài)的。
再開啟另一個(gè)終端:
這時(shí)候 send 端和 recv 端都在終端顯示has opend fifo
此時(shí)在 send 端輸入數(shù)據(jù),recv 打印。
網(wǎng)頁題目:linux命令pipe linux命令pidof sshd輸入沒反應(yīng)
網(wǎng)站鏈接:http://www.ef60e0e.cn/article/dodchji.html