歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> linux系統編程之管道(三) 命名管道FIFO和mkfifo函數

linux系統編程之管道(三) 命名管道FIFO和mkfifo函數

日期:2017/3/3 16:24:10   编辑:關於Linux

進程間通信必須通過內核提供的通道,而且必須有一種辦法在進程中標識內核提供的某個通道,前面講過的匿名管道是用打開的文件描述符來標識的。如果要互相通信的幾個進程沒有從公共祖先那裡繼承文件描述符,它們怎麼通信呢?內核提供一條通道不成問題,問題是如何標識這條通道才能使各進程都可以訪問它?文件系統中的路徑名是全局的,各進程都可以訪問,因此可以用文件系統中的路徑名來標識一個IPC通道。

FIFO和UNIX Domain Socket這兩種IPC機制都是利用文件系統中的特殊文件來標識的。

FIFO文件在磁盤上沒有數據塊,僅用來標識內核中的一條通道,如 prw-rw-r-- 1 simba simba 0 May 21 10:13 p2,文件類型標識為p表示FIFO,文件大小為0。各進程可以打開這個文件進行read/write,實際上是在讀寫內核通道(根本原因在於這個file結構體所指向的read、write函數和常規文件不一樣),這樣就實現了進程間通信。UNIX Domain Socket和FIFO的原理類似,也需要一個特殊的socket文件來標識內核中的通道,例如/run目錄下有很多系統服務的socket文件:

srw-rw-rw- 1 root root 0 May 21 09:59 acpid.socket

....................

文件類型s表示socket,這些文件在磁盤上也沒有數據塊。

一、命名管道(FIFO)

匿名管道應用的一個限制就是只能在具有共同祖先(具有親緣關系)的進程間通信。

如果我們想在不相關的進程之間交換數據,可以使用FIFO文件來做這項工作,它經常被稱為命名管道。

命名管道可以從命令行上創建,命令行方法是使用下面這個命令:

$ mkfifo filename

命名管道也可以從程序裡創建,相關函數有:

int mkfifo(const char *filename,mode_t mode);

二、命名管道和匿名管道

匿名管道由pipe函數創建並打開。

命名管道由mkfifo函數創建,打開用open。

FIFO(命名管道)與pipe(匿名管道)之間唯一的區別在它們創建與打開的方式不同,這些工作完成之後,它們具有相同的語義。

The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics.

三、命名管道的打開規則

如果當前打開操作是為讀而打開FIFO時

O_NONBLOCK disable:阻塞直到有相應進程為寫而打開該FIFO

O_NONBLOCK enable:立刻返回成功

如果當前打開操作是為寫而打開FIFO時

O_NONBLOCK disable:阻塞直到有相應進程為讀而打開該FIFO

O_NONBLOCK enable:立刻返回失敗,錯誤碼為ENXIO

需要注意的是打開的文件描述符默認是阻塞的,大家可以寫兩個很簡單的小程序測試一下,主要也就一條語句

int fd = open("p2", O_WRONLY);

假設p2是命名管道文件,把打開標志換成 O_RDONLY 就是另一個程序了,可以先運行RD程序,此時會阻塞,再在另一個窗口運行WR程序,此時兩個程序都會從open返回成功。非阻塞時也不難測試,open時增加標志位就可以了。

需要注意的是 命令管道與匿名管道的讀寫規則是一樣的,參見這裡:http://blog.csdn.net/simba888888/article/details/8952287

下面示例命名管道完成拷貝文件的功能:

/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
mkfifo("tp", 0644);
int infd = open("Makefile", O_RDONLY);
if (infd == -1)
ERR_EXIT("open error");
int outfd;
outfd = open("tp", O_WRONLY);
if (outfd == -1)
ERR_EXIT("open error");
char buf[1024];
int n;
while ((n = read(infd, buf, 1024)) > 0)
write(outfd, buf, n);
close(infd);
close(outfd);
return 0;
}

程序使用mkfifo函數創建一個命名管道文件tp,將Makefile 的文件都讀取到tp文件中。

/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
int outfd = open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outfd == -1)
ERR_EXIT("open error");
int infd;
infd = open("tp", O_RDONLY);
if (infd == -1)
ERR_EXIT("open error");
char buf[1024];
int n;
while ((n = read(infd, buf, 1024)) > 0)
write(outfd, buf, n);
close(infd);
close(outfd);
unlink("tp"); // delete a name and possibly the file it refers to
return 0;
}

可以看到跟上面的程序是相反的,即從tp讀取到Makefile2,完成拷貝文件的功能,這裡用到了一個unlink函數,屬於inode_operations系列(http://blog.csdn.net/simba888888/article/details/8806654)的一個函數,即inode引用計數減1,當引用計數為0且進程已經關閉文件描述符時,文件將被刪除。

Copyright © Linux教程網 All Rights Reserved