1.加拿大創新、科學和經濟發展部 (ISED) 於 2022 年 9 月 9 日發佈了第 2022-CEB001 號通知。 該通知包括關於無線電標準規範 RSS-195 “無線通信服務 (WCS) 設備在 2305-2320 MHz 和 2345-2360 MHz 頻段”第 2 版的指南,旨在重申 ...
進程間通信之有名管道
進程間通信有多種方式實現,本文主要講解有名管道的通信方式。
一, 有名管道簡介
匿名管道由於沒有名字,只能用於具有親緣關係的進程間通信。
為了剋服這個缺點,就提出了有名管道(FIFO
),也稱為命名管道
、FIFO文件
。
有名管道(FIFO)提供了一個路徑名與之關聯,以FIFO的文件形式存在於文件系統中,且打開方式與打開一個普通文件是一樣的,即使與FIFO的創建進程不存在親緣關係的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信。因此,通過FIFO不相關的進程也能交換數據。
FIFO文件被打開,就可以使用與操作匿名管道和其它文件的系統調用一樣的I/O系統調用,如使用 read()讀數據
,write()寫數據
,close()關閉FIFO
等。
與管道一樣,FIFO也有一個寫入端和讀取端,且從管道中讀取數據的順序與寫入數據的順序是一樣的。FIFO的名稱也由此而來:先入先出。也是一個環形隊列。
有名管道和匿名管道大部分是相同的,不同在於:
- FIFO在文件系統中作為一個特殊文件存在,FIFO的內容存放在記憶體中;
- 當使用FIFO的進程退出後,FIFO文件將繼續保存在文件系統中以便以後使用;
二, 有名管道的使用
1. 創建有名管道
- 方式1:可以使用命令創建有名管道:
mkfifo 名字
- 方式2:使用函數
mkfifo()
函數創建有名管道
使用mkfifo創建了FIFO後,就可以使用open打開它,常見的文件I/O函數都可以用於FIFO。
FIFO嚴格遵循先進先出,對FIFO的讀總是從開始處返回數據,對FIFO的寫則是把數據添加到末尾,FIFO不支持lseek()等文件定位的函數。
函數mkkfifo()
聲明:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
- 參數說明:
- pathname:管道名稱的路徑;
- mode:FIFO的許可權,和open是一樣的;如0664;
- 返回值:成功返回0,失敗返回-1,並設置對應的errno;
創建管道示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main()
{
// 創建有名管道
int ret = mkfifo("fifo1",0664);
if(ret == -1)
{
perror("mkfifo");
return -1;
}
return 0;
}
有名管道使用示例,有兩個文件,read.c
用來讀管道中的數據,write.c
用來向管道寫數據。read.c和write.c的內容如下:
read.c
// 從管道中讀取數據
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
// 1. 打開管道文件
int fd = open("test",O_RDONLY); // 阻塞,如果沒有寫端打開那麼會一直阻塞在這裡
if(fd == -1){
perror("open");
exit(0);
}
// 2. 讀數據
while (1)
{
char buf[1024]={0};
int len = read(fd,buf,sizeof(buf));
if(len == -1){
perror("read");
exit(0);
}
else if(len == 0){
// len = 0表示已經讀到管道末尾,寫端斷開連接了
break;
}
printf("receive buf: %s\n",buf);
}
// 3. 關閉FIFO
close(fd);
return 0;
}
write.c:
// 向管道中寫數據
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main()
{
// 創建有名管道
// 1. 先判斷文件是否存在
if(-1 == access("test",F_OK)) // 判斷文件是否存在
{
printf("管道不存在,創建管道\n");
// 2. 創建管道文件
int ret = mkfifo("test",0664);
if(ret == -1)
{
perror("mkfifo");
return -1;
}
}
// 3. 打開管道,以只寫方式打開管道
int fd = open("test",O_WRONLY); // 阻塞方式,如果沒有讀端打開,那麼會一直阻塞在這裡
if(fd == -1)
{
perror("open");
exit(0);
}
// 4. 向管道中寫入數據
for(int i=0;i<100;i++){
char buf[1024]={0};
sprintf(buf,"hello, %d\n",i);
printf("write data : %s\n",buf);
write(fd,buf,strlen(buf));
sleep(1);
}
// 5. 關閉FIFO
close(fd);
return 0;
}
生成可執行文件 read 和 write,當只執行read或只執行write時,會一直阻塞;當兩個文件都執行時,會進行數據傳遞;
2. 有名管道的註意事項
- 一個進程以只讀打開管道會阻塞,直到另外一個進程以只寫打開管道;
- 一個進程以只寫打開管道會阻塞,直到另外一個進程以只讀打開管道;
有名管道的讀寫特性:
- 讀管道
- 管道中有數據,read返回實際讀到的位元組數;
- 管道中無數據:
- 管道寫端被全部關閉,read返回0,相當於讀到文件末尾;
- 管道寫端沒有被全部,read阻塞等待;
- 寫管道
- 管道讀端全部關閉,進行異常終止,收到信號SIGPIPE;
- 管道讀端沒有被全部關閉:
- 管道已經滿了,write阻塞等待
- 管道沒有滿,write將數據寫入,並返回實際寫入的實際位元組數