3 命名管道(FIFO/named PIPE)
- 匿名管道(pipe):這個(gè)方式的一個(gè)缺陷,就是這些就進(jìn)程都是由一個(gè)共同的祖先進(jìn)程啟動(dòng),這給我們?cè)诓幌嚓P(guān)的進(jìn)程之間交換數(shù)據(jù)帶來(lái)了不方便。
無(wú)名管道只能用于具有親緣關(guān)系的進(jìn)程之間,這就大大限制了管道的使用。而有名管道可以解決這個(gè)問(wèn)題,他可以實(shí)現(xiàn)任意兩個(gè)進(jìn)程之間的通信。
有名管道的創(chuàng)建可以使用mkfifo函數(shù),函數(shù)的使用類(lèi)似于open函數(shù)的使用,可以指定管道的路徑和打開(kāi)的模式。
3.1 什么是命名管道
- 命名管道也被稱(chēng)為FIFO或者named pipe,它是一種特殊類(lèi)型的文件,它在文件系統(tǒng)中以文件名的形式存在,但是它的行為卻和之前所講的匿名管道類(lèi)似。
- FIFO(First in, First out) 為一種特殊的文件類(lèi)型,它在文件系統(tǒng)中有對(duì)應(yīng)的路徑。
- 當(dāng)一個(gè)進(jìn)程以讀(r)的方式打開(kāi)該文件,而另一個(gè)進(jìn)程以寫(xiě)(w)的方式打開(kāi)該文件,那么內(nèi)核就會(huì)在兩個(gè)進(jìn)程之間建立管道,所以FIFO實(shí)際上也由內(nèi)核管理,不與硬盤(pán)打交道。
- 叫FIFO,因?yàn)楣艿辣举|(zhì)上是一個(gè)先進(jìn)先出的隊(duì)列數(shù)據(jù)結(jié)構(gòu) ,最早放入的數(shù)據(jù)被最先讀出來(lái),從而保證信息交流的順序。
- 寫(xiě)模式的進(jìn)程向FIFO中寫(xiě)入,而讀模式的進(jìn)程從FIFO文件中讀出。當(dāng)刪除FIFO文件時(shí),管道連接也隨之消失。FIFO的好處在于我們可以通過(guò) 文件的路徑來(lái)識(shí)別管道,從而讓沒(méi)有親緣關(guān)系的進(jìn)程之間建立連接 **
3.2 命名管道的讀寫(xiě)規(guī)則
- 1、從FIFO中讀取數(shù)據(jù)的約定:如果一個(gè)進(jìn)程為了從FIFO中讀取數(shù)據(jù)而阻塞打開(kāi)了FIFO,那么該進(jìn)程內(nèi)的讀操作 為設(shè)置了阻塞標(biāo)志的讀操作。
- 2、從FIFO中寫(xiě)入數(shù)據(jù)的約定:如果一個(gè)進(jìn)程為了想FIFO中寫(xiě)入數(shù)據(jù)而阻塞打開(kāi)了FIFO,那么該進(jìn)程內(nèi)的寫(xiě)操作 為設(shè)置了阻塞標(biāo)志的寫(xiě)操作。
3.3 命名管道的安全問(wèn)題
- 讓寫(xiě)操作原子化,怎么才能使寫(xiě)操作原子化呢?
- 系統(tǒng)規(guī)定,在一個(gè)以O(shè)_WRONLY(即阻塞方式)打開(kāi)的FIFO中,如果寫(xiě)入的數(shù)據(jù)長(zhǎng)度小于等于PIPE_BUF,那么或者寫(xiě)入全部字節(jié),或者一個(gè)字節(jié)都不寫(xiě)入。如果所有的寫(xiě)請(qǐng)求都是發(fā)往一個(gè)阻塞的FIFO的,并且每個(gè)寫(xiě)請(qǐng)求的數(shù)據(jù)長(zhǎng)度小于等于PIPE_BUF字節(jié),系統(tǒng)就可以確保數(shù)據(jù)絕不會(huì)交錯(cuò)在一起。
相比TCP通信方式
命名管道常常用于應(yīng)用程序之間的通迅,由于不需要進(jìn)行序列化和反序列化操作,效率是非常高的。相比TCP通信方式,效率更高,但比共享內(nèi)存要低點(diǎn)。
Android JNI之命名管道(FIFO/mkfifo)的使用
場(chǎng)景描述:在JNI層使用mkfifo,并且讀寫(xiě)數(shù)據(jù),同時(shí)在A(yíng)ndroid Java層讀寫(xiě)fifo數(shù)據(jù)。
Android Java層讀寫(xiě)fifo數(shù)據(jù),獲取fifo路徑后,new File能夠成功,說(shuō)明路徑是對(duì)的,但是然后用各種Stream,都無(wú)法成功獲得可用的流,進(jìn)而無(wú)法繼續(xù)操作。
暫時(shí)的結(jié)論,Java層讀寫(xiě)fifo數(shù)據(jù)比較有困難,new 各種Stream時(shí)會(huì)阻塞住出不來(lái),有空了再試,可能的原因可以看完本文,可能與底層申請(qǐng)只讀權(quán)限不會(huì)成功有關(guān)系。
先說(shuō)說(shuō)在JNI層使用mkfifo,首先,F(xiàn)IFO的路徑怎么確定,不是隨便給個(gè)路徑就能mkfifo成功的,經(jīng)過(guò)試驗(yàn),路徑可以這樣確定,在A(yíng)NDROID JAVA層寫(xiě)如下代碼:
String fifoName="my_test_fifo";
String seprator = File.separator;
//get full fifo auto
String dataDir=context.getApplicationInfo().dataDir;
String fullFifoPath=dataDir+seprator+fifoName;//this is the full path you want!!!
獲取到完整的FIFO路徑(fullFifoPath)后,便可以通過(guò)JNI接口傳入底層C/C++代碼,底層利用這個(gè)完整的路徑進(jìn)行mkfifo,open,write,read等操作:
//fifo操作自己本身需要的幾個(gè)頭文件,其它需要的頭文件自己看著加
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
int fd_fifo;//fifo handle
char *fifoPath="your fifo path";//不要hard code,換成你自己的fifo路徑
if (mkfifo(fifoPath, 0666)<0 && errno != EEXIST)
{
//LOGI("can't create fifo ");
return;
}
//mkfifo success
fd_fifo=open(fifoPath, O_RDONLY | O_NONBLOCK);
if( fd_fifo<0)
{
// LOGI("fd=%d,can't open fifoPath%s",fd_fifo,fifoPath);
return;
}
else
{
// LOGI("fd=%d,Success open fifoPath=%s",fd_fifo,fifoPath);
}
需要額外提醒大家一點(diǎn)的就是,在進(jìn)行open操作的時(shí)候,關(guān)于權(quán)限問(wèn)題,這是在A(yíng)NDROID上調(diào)用底層代碼特有的一個(gè)詭異的地方,還沒(méi)找到具體的原因,大家注意一下就行了:
如果打開(kāi)時(shí)權(quán)限參數(shù)為只讀,會(huì)失敗,如下代碼:
fd_fifo=open(fifoPath, O_WRONLY | O_NONBLOCK);
返回值fd_fifo會(huì)是-1,代表失敗,如果把O_NONBLOCK去掉,則會(huì)一直阻塞住出不來(lái)。但是如果打開(kāi)權(quán)限是可讀寫(xiě)(O_RDWR)和只讀(O_RDONLY)都會(huì)成功,同時(shí),一個(gè)詭異的地方,一旦申請(qǐng)過(guò)O_RDWR,后面再用O_WRONLY就會(huì)成功了,下面是我依次以不同權(quán)限open的返回值結(jié)果:
2020-12-10 19:00:30.005 5327-5383/com.example.myapplication I/log_c: testfd_wo=-1 //O_WRONLY | O_NONBLOCK,失敗
2020-12-10 19:00:30.005 5327-5383/com.example.myapplication I/log_c: testfd_wr=94//O_RDWR | O_NONBLOCK,成功
2020-12-10 19:00:30.005 5327-5383/com.example.myapplication I/log_c: testfd_ro=95//O_RDONLY | O_NONBLOCK,成功
2020-12-10 19:00:30.005 5327-5383/com.example.myapplication I/log_c: testfd_w=96//O_WRONLY | O_NONBLOCK,成功,因?yàn)榍懊嬉設(shè)_RDWR的權(quán)限open過(guò),所以這里成功了!
JAVA層new各種stream會(huì)阻塞住出不來(lái)的現(xiàn)象,暫時(shí)能得到的原因是:java里面new stream的時(shí)候默認(rèn)是以阻塞方式去new 的,雖然路徑存在,但是申請(qǐng)不到,就一直阻塞住。
參考
命名管道文件的使用
使用命名管道實(shí)現(xiàn)進(jìn)程間通信
Android JNI之命名管道(FIFO/mkfifo)的使用(2)