linux有名管道通信

有名管道的打开规则

有名管道比管道多了一个打开操作:open。

FIFO的打开规则:

如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

注意:

读取管道打开时需要等待写入管道的打开,同时写入打开管道又需要等待读取管道的打开 
Deadlock不会产生,因为这里“等待”的*不*是另一方的open是否*已经完成* 
举个例子, 
如果open FIFO时都不指定O_NONBLOCK, A进程首先 open FIFO for reading, 它会block. 
然后B进程 open FIFO for writing, 由于进程A正在open for reading, 因此B的open操作会 
立即返回,然后A的open 操作返回。

对打开规则的验证参见附2

2.4有名管道的读写规则

从FIFO中读取数据:

约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。

  • 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。
  • 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其它进程在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。
  • 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。
  • 如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。

注:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。

向FIFO中写入数据:

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

对于设置了阻塞标志的写操作:

  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:

  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;
聊天程序:
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    int i, rfd, wfd, len = 0, fd_in, vfd;
    int err, cnt;
    char str[32];
    int flag, stdinflag;
    fd_set write_fd, read_fd;
    struct timeval net_timer;
    
    if(access("fifoz", F_OK) < 0)
    {
        err = mkfifo("fifoz", S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
        if(err < 0)
        {
            perror("mkfifoz");
            //exit(EXIT_FAILURE);
        }
    }
    
    if(access("fifoy", F_OK) < 0)
    {
        err = mkfifo("fifoy", S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
        if(err < 0)
        {
            perror("mkfifoy");
            //exit(EXIT_FAILURE);
        }
    }
    vfd = open("fifoz", O_RDONLY|O_NONBLOCK);
    if((wfd = open("fifoz", O_WRONLY|O_NONBLOCK)) < 0)
    {
        perror("open fifoz");
        return -1;
    }
    if((rfd = open("fifoy", O_RDONLY|O_NONBLOCK)) < 0)
    {
        perror("open fifoy");
        return -1;
    }
    vfd = open("fifoy", O_WRONLY|O_NONBLOCK);
        

    if(rfd <= 0 || wfd <= 0)
        return 0;

    printf("\nzhengfang...\n");

    while(1)
    {
        FD_ZERO(&read_fd);
        FD_SET(rfd, &read_fd);
        FD_SET(fileno(stdin), &read_fd);
        net_timer.tv_sec = 5;
        net_timer.tv_usec = 0;
        memset(str, 0, sizeof(str));

        if(i = select(rfd+1, &read_fd, NULL, NULL, &net_timer) <= 0)
            continue;
        if(FD_ISSET(rfd, &read_fd))
        {
            cnt = read(rfd, str, sizeof(str));
            printf("yuhuanzhang%d:%s\n", cnt, str);
        }
        if(FD_ISSET(fileno(stdin), &read_fd))
        {
            printf("----------fifo1----------\n");
            fgets(str, sizeof(str), stdin);
            len = write(wfd, str, strlen(str));
        }
    }

    close(rfd);
    close(wfd);
}



#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    int i, rfd, wfd, len = 0, fd_in, vfd;
    int err, cnt;
    char str[32];
    int flag, stdinflag;
    fd_set write_fd, read_fd;
    struct timeval net_timer;

    if(access("fifoz", F_OK) < 0)
    {
        err = mkfifo("fifoz", S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
        if(err < 0)
        {
            perror("mkfifoz");
            //exit(EXIT_FAILURE);
        }
    }
    if(access("fifoy", F_OK) < 0)
    {
        err = mkfifo("fifoy", S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
        if(err < 0)
        {
            perror("mkfifoy");
            //exit(EXIT_FAILURE);
        }
    }
    //printf("sleep 2s...\n");
    //sleep(2);
    
    if((rfd = open("fifoz", O_RDONLY|O_NONBLOCK)) < 0)
    {
        perror("open fifoz");
        return -1;
    }
    vfd = open("fifoz", O_WRONLY|O_NONBLOCK);
    vfd = open("fifoy", O_RDONLY|O_NONBLOCK);
    if((wfd = open("fifoy", O_WRONLY|O_NONBLOCK)) < 0)
    {
        perror("open fifoy");
        return -1;
    }
    
    printf("\nyuhuanzhang...\n");
    
    while(1)
    {
        FD_ZERO(&read_fd);
        FD_SET(rfd, &read_fd);
        FD_SET(fileno(stdin), &read_fd);
        net_timer.tv_sec = 5;
        net_timer.tv_usec = 0;
        memset(str, 0, sizeof(str));

        if(i = select(rfd+1, &read_fd, NULL, NULL, &net_timer) <= 0)
            continue;
        if(FD_ISSET(rfd, &read_fd))
        {
            cnt = read(rfd, str, sizeof(str));
            
            printf("zhengfang%d:%s\n", cnt, str);
        }
        if(FD_ISSET(fileno(stdin), &read_fd))
        {
            printf("----------fifo2-----------\n");
            fgets(str, sizeof(str), stdin);
            len = write(wfd, str, strlen(str));
        }
    }

    close(rfd);
    close(wfd);
}

其实这两个程序基本上一样,就是换个名管道名而已。
刚开始程序就死那儿了,不动,当时我就纳闷了,开始还以为是自己没有给新创建的管道屏蔽所有的权限,于是就把权限给加了,结果还是不行,看看书上怎么写的,书上的就是上面的,而我就是把注释行的部分放在了上面,其实也不是下面上面的问题,可是我当时纳闷怎么就反了一下就不行呢?
翻了一下其他资料看了一下才恍然大悟了!
在读写有名管道之前需要用open函数打开该有名管道,打开有名管道操作与其他文件有一定的区别,如果希望打开管道的写端,则需要另一个进程打开该管道的读端,如果只打开有名管道的一端,则系统将暂时阻塞打开进程,知道另一个进程打开管道的另一端,当前进程才会继s续执行,因此,在使用有名管道时一定么使用两个进程分别打开其读端和写端!
现在知道程序死的原因了吧!

现在简要说明一下遇到的问题:
要通过FIFO来进行进程间通信,用阻塞模式打开的话,进程会block在open函数上。估计谁也不希望在一个ipc类的initialize函数里面打开fifo文件时被block住。所以解法就是用非阻塞模式打开(|O_NONBLOCK),打开的问题我们解决了。
但这样我们还是没有办法像操作普通文件一样用阻塞方式读取数据,就算是用fcntl函数clear掉O_NONBLOCK标识也不行,这是因为上面的一段话“If no process has the pipe open for writing, read() will return 0 to indicate end-of-file. ”,也就是说在没有进程用写模式打开fifo文件的时候,去掉不去掉O_NONBLOCK标识read函数都会直接返回。恰巧我是想用echo向fifo中去写数据。echo不是一个常驻进程,所以在echo还没执行或者执行完以后,我们是没有办法去阻塞读fifo的。(一般的解决方法有:1.用循环,这个办法太土,不予讨论。2.还可以用select或者poll去异步监控fd事件,但感觉很不值当,虽然网上都说要用这种方法。3.用O_ASYNC去触发信号的方法没试过,感觉太繁琐。)

山穷水尽啦,疑无路啦,可这句诗还有后半句,叫"柳暗花明又一村"。
那就是在用只读非阻塞方式打开fifo文件的同时,然后再用只写阻塞方式再次打开该fifo文件。
再来看上面绿色的部分:
A process that uses both ends of the connection in order to communicate with itself should be very careful to avoid deadlocks. 
既然man里面没说不许同一个进程同时打开fifo的两端,所以我们这么做一般也不会被人鄙视的。这样的具体原因也在上面的第二段绿色文字中:
When attempting to read from an empty pipe or FIFO:
If some process has the pipe open for writing and O_NONBLOCK is clear, read() will block the calling thread until some data is written or the pipe is closed by all processes that had the pipe open for writing. 
也就是说我们自己保持一个该fifo文件的只写阻塞fd,我们也不会通过这个只写阻塞fd做任何输入操作,只是为了让那个只读fd能在读取数据时block在read函数中。
同时有下面这句作保障:
It can be opened by multiple processes for reading or writing. 
从而保障了我们在用只写阻塞方式多打开了一次该fifo文件后,不会对外部的echo操作有任何影响。

但是。。。
process can open a FIFO in non-blocking mode.  In this case, opening
       for read only will succeed even if no-one has opened on the write  side
       yet;  opening  for  write  only will fail with ENXIO (no such device or
       address) unless the other end has already been opened.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值