FIFO
FIFO常被稱爲命名管道,以區分管道(pipe)。管道(pipe)只能用於“有血緣關係”的進程間。但通過FIFO,不相關的進程也能交換數據。
FIFO是Linux基礎文件類型中的一種。但,FIFO文件在磁盤上沒有數據塊,僅僅用來標識內核中一條通道。各進程可以打開這個文件進行read/write,實際上是在讀寫內核通道,這樣就實現了進程間通信。
使用mkfifo函數創建FIFO
mkfifo函數就是用於創建一個FIFO文件
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
返回值說明:成功返回0; 失敗返回-1,設置errno
參數pathname:要創建的FIFO文件名且該文件必須不存在
參數mode:指定FIFO文件的權限(8進制數,如0664)
一旦使用mkfifo創建了一個FIFO,就可以使用open打開它,常見的文件I/O函數都可用於fifo。如:close、read、write、unlink等。
FIFO的打開規則
1)、如果當前打開操作是爲讀而打開FIFO時,若已經有相應進程爲寫而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程爲寫而打開該FIFO(當前打開操作設置了阻塞標誌);或者,成功返回(當前打開操作沒有設置阻塞標誌)。
2)、如果當前打開操作是爲寫而打開FIFO時,如果已經有相應進程爲讀而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程爲讀而打開該FIFO(當前打開操作設置了阻塞標誌);或者,返回ENXIO錯誤(當前打開操作沒有設置阻塞標誌)。
總之,一旦設置了阻塞標誌,調用mkfifo建立好之後,那麼管道的兩端讀寫必須分別打開,有任何一方未打開,則在調用open的時候就阻塞。對管道或者FIFO調用lseek,返回ESPIPE錯誤。
例子:
在當前目錄創建FIFO文件
mkfifo("./test", 0664);
fifo_w.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int main() {
int fd = 0;
int len = 0;
int ret;
char buf[1024] = { 0 };
#if 1
//只寫方式打開test
fd = open("./test", O_WRONLY);
if (fd < 0)
{
perror("open error");
exit(1);
}
#endif
#if 0
fd = open("./test", O_WRONLY | O_NONBLOCK);//設置成非阻塞
if (fd < 0)
{
//判斷是否爲對端沒打開而導致ENXIO錯誤
if (errno == ENXIO)
{
perror("open error");
}
exit(1);
}
#endif
puts("open fifo write");
//向FIFO寫入數據
while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0)
{
write(fd, buf, len);
}
close(fd);
return 0;
}
fifo_r.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int main() {
int fd = 0;
int len = 0;
char buf[1024] = { 0 };
//只讀打開test
fd = open("./test", O_RDONLY);
if (fd < 0)
{
perror("open error");
exit(1);
}
puts("open fifo read");
//從管道中讀取數據
while ((len = read(fd, buf, sizeof(buf))) > 0)
{
write(STDOUT_FILENO, buf, len);
}
//如果read返回0,說明讀到文件末尾或對端已關閉
if (len == 0)
{
puts("peer is close or file end");
}
else
{
puts("read error");
}
close(fd);
return 0;
}
可以看到,先打開寫端時,由於此時讀端沒有打開,所以寫端會阻塞:
然後打開讀端,就可以進行通信了:
最後,當寫端按下Ctrl+d後關閉,對應的讀端的read就會返回0。
使用非阻塞IO
如果在打開FIFO文件不希望阻塞時,在調用open函數可以指定O_NONBLOCK。
即在fifo_w.c中使用這段代碼,然後我們先執行./fifo_w
,此時並未執行./fifo_r
。
#if 1
fd = open("test", O_WRONLY | O_NONBLOCK);//設置成非阻塞
if (fd < 0)
{
//判斷是否爲對端沒打開而導致ENXIO錯誤
if (errno == ENXIO)
{
perror("open error");
}
exit(1);
}
#endif
出錯原因在於,當你打開FIFO寫入數據,但是對方沒打開讀端就會出現這樣的錯誤。