1 命名管道(FIFO)
管道应用的一个重大缺陷就是没有名字,因此只能用于亲缘进程之间的通信。 后来从管道为基础提出命名管道(named pipe,FIFO)的概念,该限制得到了克服。 FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。 值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。
2 命名管道的创建
#include <sys/types.h>
#include <sys/stat.h>
int
mkfifo(
const
char
*pathname,
//
文件的路径
mode_t mode
//和open函数mode一样
);
如果pathname路径下的文件已经存在,则mkfifo返回-1,errono将会返回EEXIST。
3 命名管道操作
FIFO在文件系统中表现为一个文件,大部分的系统文件调用都可以在FIFO上面,比如:read,open,write,close,unlink,stat等函数,但是seek函数不能对FIFO调用。
可以调用open打开FIFO,请注意以下方面:
A 当以阻塞(未指定O_NONBLOCK)方式只读打开FIFO的时候,则将会被阻塞,直到有其他进程以写方式打开该FIFO。B 类似的,当以阻塞(未指定O_NONBLOCK)方式只写打开FIFO的时候,则将会被阻塞,直到有其他进程以读方式打开该FIFO
C 当以非阻塞方式(指定O_NONBLOCK)方式只读打开FIFO的时候,则立即返回。当只写open时,如果没有进程为读打开FIFO,则返回-1,其errno是ENXIO
4 应用实例
本例为一个client-server模式,服务器端将会创建一个闻名fifo文件(本例中为“/tmp/server”),并读取该fifo文件。客户端则打开该fifo文件,并把其请求写入该FIFO文件。服务器端读取该命令并执行之。
服务器端的代码为:
#include <unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<
string
.h>
#include
<unistd.h>
#include
<fcntl.h>
#include
<limits.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<ctype.h>
#define
SERVER_FIFO_NAME "/tmp/serv_fifo"
#define
CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
#define
BUFFER_SIZE 20
struct
data_to_pass_st{
pid_t client_pid;
char
some_data[BUFFER_SIZE-
1
];
};
int
main(){
int
server_fifo_fd,client_fifo_fd;
struct
data_to_pass_st mydata;
int
read_res;
char
client_fifo[
256
];
char
*
tmp_char_ptr;
mkfifo(SERVER_FIFO_NAME,
0777
);
server_fifo_fd
=
open(SERVER_FIFO_NAME,O_RDONLY);
if
(server_fifo_fd==-
1
){
fprintf(stderr,
"
Server fifo failure\n
"
);
exit(EXIT_FAILURE);
}
sleep(
10
);
do
{
read_res
=read(server_fifo_fd,&mydata,
sizeof
(mydata));
if
(read_res>
0
){
tmp_char_ptr
=
my_data.some_data;
while
(*tmp_char_ptr>
0
){
//
把some_data中的所有字符全部转换为大写
*tmp_char_ptr=toupper(*
tmp_char_ptr);
tmp_char_ptr
++
;
}
sprintf(client_fifo,CLIENT_FIFO_NAME,my_data.client_pid);
client_fifo_fd
=
open(client_fifo,O_WRONLY);
if
(client_fifo_fd!=-
1
){
write(client_fifo_fd,
&my_data,
sizeof
(my_data));
close(client_fifo_fd);
}
}
}
while
(read_res>
0
);
close(server_fifo_fd);
unlink(SERVER_FIFO_NAME);
exit(EXIT_SUCCESS);
}
客户端代码为:
#include <unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<
string
.h>
#include
<unistd.h>
#include
<fcntl.h>
#include
<limits.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<ctype.h>
#define
SERVER_FIFO_NAME "/tmp/serv_fifo"
#define
CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
#define
BUFFER_SIZE 20
struct
data_to_pass_st{
pid_t client_pid;
char
some_data[BUFFER_SIZE-
1
];
};
int
main(){
int
server_fifo_fd,client_fifo_fd;
struct
data_to_pass_st my_data;
int
times_to_send;
char
client_fifo[
256
];
server_fifo_fd
=
open(SERVER_FIFO_NAME,O_WRONLY);
if
(server_fifo_fd==-
1
){
fprintf(stderr,
"
Sorry,no server\n
"
);
exit(EXIT_FAILURE);
}
my_data.client_pid
=
getpid();
sprintf(client_fifo,CLIENT_FIFO_NAME,my_data.client_pid);
if
(mkfifo(client_fifo,
0777
)==-
1
){
fprintf(stderr,
"
Sorry,can't make %s\n
"
,client_fifo);
exit(EXIT_FAILURE);
}
for
(times_to_send=
0
;times_to_send<
5
;times_to_send++
){
sprintf(my_data.some_data,
"
Hello from %d
"
,my_data.client_pid,my_data.some_data);
write(server_fifo_fd,
&my_data,
sizeof
(my_data));
client_fifo_fd
=
open(client_fifo,O_RDONLY);
if
(client_fifo_fd!=-
1
){
if
(read(client_fifo_fd,&my_data,
sizeof
(my_data))>
0
){
printf(
"
received:%s\n
"
,my_data.some_data);
}
close(client_fifo_fd);
}
}
close(server_fifo_fd);
unlink(client_fifo);
exit(EXIT_SUCCESS);
}
5 总结
与管道相比,FIFO最大的特点就是其在文件系统中有fifo文件存在,这样就可以做到进程间通信。
6 FIFO的缺点
当然FIFO也有它的局限性。客户端可以发请求到服务器,但前提是要知道一个公共的FIFO通道,
对于实现服务器回传应答到客户端的问题,可以通过为每一个客户端创建一个专用的FIFO,来实现回传应答
。但也有不足,
服务器会同时应答成千上万个客户端,创建如此多的FIFO是否会使系统负载过大,相应的如何判断客户端是否因意外而崩溃成为难题,或者客户端不读取应答直接退出,所以服务器必须处理SIGPIPE信号,并做相应处理。(当没有进程为读打开FIFO文件时,write函数进行写操作会产生SIGPIPE信号)

