命名管道克服了管道没有名字的限制,同时除了具有管道的功能外(也是半双工),它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
下面程序fifo_write.c周期的往管道中写入数据:
/*=============================================================================
# FileName: fifo_write.c
# Desc: write data to name pipe
# Author: Licaibiao
# Version:
# LastChange: 2017-01-10
# History:
=============================================================================*/
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
static int i = 0;
const int open_mode = O_WRONLY;
char buffer[PIPE_BUF + 1];
char test_data[PIPE_BUF];
if(access(fifo_name, F_OK) == -1)
{
printf ("Create the fifo pipe.\n");
res = mkfifo(fifo_name, 0777);
if(res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(fifo_name, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd);
if(pipe_fd !=-1)
{
while(1)
{
sprintf(test_data,"test number %d \n",i++);
printf("Process %d write data to name pipe: %s",getpid(),test_data);
res = write(pipe_fd, test_data, PIPE_BUF);
if(res == -1)
{
printf("write error on name pipe !\n");
exit(EXIT_FAILURE);
}
else
{
sleep(2);
}
}
}
else
{
exit(EXIT_FAILURE);
}
close(pipe_fd);
}
下面程序fifo_read.c周期的从管道中读取数据:
/*=============================================================================
# FileName: fifo_read.c
# Desc: read data from name pipe
# Author: Licaibiao
# Version:
# LastChange: 2017-01-10
# History:
=============================================================================*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int res = 0;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
int bytes_write = 0;
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(fifo_name, open_mode);
printf("Process %d result %d\n",getpid(), pipe_fd);
if(pipe_fd != -1)
{
while(1)
{
res = read(pipe_fd, buffer, PIPE_BUF);
if(res>0)
{
printf("Process %d read data from name pipe :%s",getpid(),buffer);
}
sleep(2);
}
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
编译运行结果为:
root@ubuntu:/home/share/pipe# vim fifo_write.c
root@ubuntu:/home/share/pipe# vim fifo_read.c
root@ubuntu:/home/share/pipe# gcc fifo_read.c -o fifo_read
root@ubuntu:/home/share/pipe# gcc fifo_write.c -o fifo_write
root@ubuntu:/home/share/pipe#
root@ubuntu:/home/share/pipe# ls
fifo_read fifo_read.c fifo_write fifo_write.c
root@ubuntu:/home/share/pipe# ./fifo_write &
[1] 88963
root@ubuntu:/home/share/pipe# Process 88963 opening FIFO O_WRONLY
root@ubuntu:/home/share/pipe#
root@ubuntu:/home/share/pipe# ./fifo_read
Process 88964 opening FIFO O_RDONLY
Process 88963 result 3
Process 88963 write data to name pipe: test number 0
Process 88964 result 3
Process 88964 read data from name pipe :test number 0
Process 88963 write data to name pipe: test number 1
Process 88964 read data from name pipe :test number 1
Process 88963 write data to name pipe: test number 2
Process 88964 read data from name pipe :test number 2
Process 88963 write data to name pipe: test number 3
Process 88964 read data from name pipe :test number 3
Process 88963 write data to name pipe: test number 4
Process 88964 read data from name pipe :test number 4
Process 88963 write data to name pipe: test number 5
Process 88964 read data from name pipe :test number 5
^C
root@ubuntu:/home/share/pipe# Process 88963 write data to name pipe: test number 6
[1]+ Broken pipe ./fifo_write
root@ubuntu:/home/share/pipe# ls /tmp
at-spi2 my_fifo pulse-PKdhtXMmr18n ssh-fbQHGxSG2651 vgauthsvclog.txt.0 vmware-licaibiao
keyring-x9fH8E pulse-2L9K88eMlGn7 pulse-TIYtyXUn5vZV unity_support_test.0 VMwareDnD vmware-root
root@ubuntu:/home/share/pipe#
先在后台运行write函数,我们可以看到pipe被阻塞了,直到有一个进程读取pipe它才得意继续运行。最后当没有进程读取管道数据时,写入端写入数据将返回错误。另外,我们可以在tmp目录下看到我们创建的管道 my_fifo:
这里总结一下name pipe 需要注意的事项:
1、就是程序不能以O_RDWR(读写)模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。
2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。(如:const char *fifo_name = "/tmp/my_fifo"; )
3、第二个参数中的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。
4、关于阻塞问题:
- 对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
- 对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
5、管道安全问题:
有一种情况是:一个FIFO文件,有多个进程同时向同一个FIFO文件写数据,而只有一个读FIFO进程在同一个FIFO文件中读取数据时,会发生数据块的相互交错。不同进程向一个FIFO读进程发送数据是很普通的情况。这个问题的解决方法,就是让写操作的原子化。系统规定:在一个以O_WRONLY(即阻塞方式)打开的FIFO中, 如果写入的数据长度小于等待PIPE_BUF,那么或者写入全部字节,或者一个字节都不写入。如果所有的写请求都是发往一个阻塞的FIFO的,并且每个写记请求的数据长度小于等于PIPE_BUF字节,系统就可以确保数据决不会交错在一起。
2019.06.24更新
命名管道的一些读写规则:
- 以非阻塞只写方式打开时,在多进程中同时写入数据,注意写入的大小和写入的原子性。
- 以非阻塞只写方式打开时,如果没有一个进程在读管道,打开会失败。
- 以非阻塞只写方式打开时,如果所有读管道进程关闭,写进程会收到SIGPIPE信号如果写进程不对SIGPIPE信号进行处理,会导致写进程退出。
---------------------------------------------2022.08.21日更新-----------------------------------------------
由于各种原因,后续文章内容将更新到公众号,本平台将不再做更新。
CSDN上相关文章的测试工程代码,也统一放到了公众号上,可以免费免积分下载
可以通过主页上的二维码,也可以通过搜索微信公众号 liwen01 进入公众号
liwen01 2022.08.21
--------------------------------------------------------------------------------------------------------------------