Linux进程间通信——命名管道应用实例

    命名管道克服了管道没有名字的限制,同时除了具有管道的功能外(也是半双工),它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令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

--------------------------------------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值