进程通讯:管道

管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

1、特点:

  • 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
  • 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
  • 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

1). 当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图: 

要关闭管道只需将这两个文件描述符关闭即可。

2). 单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:

 

1. 测试代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int pipefd[2];
    int ret, nwrite, nread;
    pid_t pid;
    char writebuf[1024];
    char readbuf[1024];
    ret = pipe(pipefd);
    if (ret == -1)
    {
        perror("pipe");
        return -1;
    }
    pid = fork();
    if (pid == -1)
    {
        perror("fork");
        return -1;
    }
    else if (pid == 0)
    {
        close(pipefd[1]);
        while (1)
        {
            nread = read(pipefd[0], readbuf, sizeof(readbuf));
            if (nread == -1)
            {
                perror("read");
                exit(-1);
            }
            printf("read from pipe %s\n", readbuf);
            if (!strncmp(readbuf, "quit", 4))
                break;
        }
    }
    if (pid > 0)
    {
        close(pipefd[0]);
        while (1)
        {
            fgets(writebuf, sizeof(writebuf), stdin);
            nwrite = write(pipefd[1], writebuf, sizeof(writebuf));
            if (nwrite == -1)
            {
                perror("write");
                exit(-1);
            }
            if (!strncmp(writebuf, "quit", 4))
                break;
        }
    }
}

输出结果:

 

有名管道

1. 为何提出有名管道的说法,目的是为了克服无名管道的不足之处:

  • 无名管道只能是用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围。
  • 有名管道可以使互不相关的两个进程互相通信,有名管道可以通过路径名来指出。并在文件系统课件为了这种有名管道,Linux中专门设立了一个专门的特殊文件系统-管道文件,以FIFO的形式存在于文件系统中,这样,即使与FIFO的创建者不存在亲缘关系的进程,只要访问该路径,就能彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据,但在磁盘只是一个节点,而文件的数据只存在内存缓冲页面上,与普通管道一样。

2. 有名管道的创建

有名管道可以从命令行上创建,命令行方法是使用下面这个命令:

$ mkfifo myfifo

有名管道也可以从程序里创建,相关API有:

int mkfifo(cosnt char *path, mode_t mode);

参数:

  • 第一个参数是一个普通的路径名,也就是创建后FIFO名字。
  • 第二个参数与打开普通文件的open函数中的mode参数相同(文件的读写权限),如果mkfifo的一个参数是一个已经存在路径名时,会返回EEXIST错误,所以一般典型的调用代码会检查是否返回该错误,如果确实返回该错误,那么要调用打开FIFO的函数open就可以了。

3. FIFO的open函数打开规则:
O_RDONLY、O_WRONLY和O_NONBLOCK标志共有四种合法的组成方式:

flags = O_RDONLY:open将会调用阻塞,除非有另外一个进程以写的方式打开用一个FIFO,否则一直等待。
flags = O_WRONLY:open将会调用阻塞,除非有另外一个进程以读的方式打开同一个FIFO,否则一直等待。
flags = O_RDONLY | O_NONBLOCK:如果此时没有其他进程以写的方式打开FIFO,此时open也会成功返回,此时FIOF被读打开,而不会返回错误。
flag是= O_WRONLY | O_NONBLOCK:立即返回,如果此时没有其他进程以读的方式打开,open会失败打开,此时FIOF没有被打开,返回-1。
 

 

 程序1:
 

// writefifo.c
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, const char *argv[])
{
    int fd, nwrite;
    char buf[1024] = "\0";
    if (access(argv[1], F_OK) != 0)
    {
        int ret = mkfifo(argv[1], 0666);
        if (ret == -1)
        {
            perror("mkfifo");
            exit(-1);
        }
    }
    fd = open(argv[1], O_WRONLY);
    if (fd == -1)
    {
        perror("open");
        return -1;
    }
    while (1)
    {
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';
        nwrite = write(fd, buf, strlen(buf));
        if (nwrite == -1)
        {
            perror("write error");
            return -1;
        }
        if (!strncmp(buf, "quit", 4))
            break;
    }
    return 0;
}

程序2:

//readfifo.c
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int ret, fd, nread;
    char buf[1024] = "\0";
    if (access(argv[1], F_OK) != 0)
    {
        ret = mkfifo(argv[1], 0666);
        if (ret == -1)
        {
            perror("mkfifo");
            exit(-1);
        }
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
    {
        perror("open");
        exit(-1);
    }
    while (1)
    {
        nread = read(fd, buf, sizeof(buf));
        if (nread == -1)
        {
            perror("read errro");
            exit(-1);
        }
        printf("read from fifo is %s\n", buf);
        if (!strncmp(buf, "quit", 4))
            break;
        memset(buf, '\0', sizeof(buf));
    }
    return 0;
}

输出结果: 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值