zoukankan      html  css  js  c++  java
  • 进程间通信(一)——管道和FIFO

    1. 概述

    管道没有名字,适用于有亲缘关系的进程间。

    FIFO指first in first out,有一个路径名与之关联,从而允许无亲缘关系的进程间使用。亦称:命名管道named pipe。

    两者都是单向数据流(半双工管道),具有随进程的持续性,数据都是先进先出,在进程间通信不需要某种形式的同步。

     

    2.管道

    #include <unistd.h>

    int pipe(int fd[2]);

    返回:成功0,出错-1;

    返回两个文件描述符:fd[0]打开来读,fd[1]打开来写。

    典型使用:

    (1) 单向的

    在父子进程间提供通信手段。

    父进程创建管道,然后fork出自身的副本。

    父进程关闭读端,用于向管道中写。

    子进程关闭写端,用于从管道中读。

    (2) 双向的

    pipe(pipe1[2]);        pipe1[0], pipe1[1]

    pipe(pipe2[2]);        pipe2[0], pipe2[1]

    fork() == 0 {//子进程

    close(pipe1[1]);

    close(pipe2[0]);
    }

    //父进程

    close(pipe1[0]);

    close(pipe2[1]);

     

    3.pipe例子

    下面讲述一个客户——服务器程序。

    客户发送路径名,服务器发送文件内容给客户。

    #include "unp.h"
    
    void client(int, int), server(int, int);
    
    int main(void)
    {
        int pipe1[2], pipe2[2];
        //创建两个管道
        //也会在子进程中创建
    Pipe(pipe1); Pipe(pipe2); int childid = Fork(); if (childid == 0) { //child Close(pipe1[1]); //关闭写 Close(pipe2[0]); //关闭读 server(pipe1[0], pipe2[1]); exit(0); } Close(pipe1[0]); Close(pipe2[1]); client(pipe2[0], pipe1[1]); //等待子进程终止 waitpid(childid, NULL, 0); exit(0); }

    server.c代码:

    #include "unp.h"
    //从管道中读取文件名
    //通过管道把文件发送给客户端
    void server(int readfd, int writefd)
    {
        char buff[MAXLINE + 1];
    
        ssize_t n = Read(readfd, buff, MAXLINE);
        if (n == 0) {
            err_quit("end-of-file while reading pathname");
        }
        buff[n] = '';
    
        int fd = open(buff, O_RDONLY);
        if (fd < 0) {
            snprintf(buff + n, sizeof(buff) - n, ": can't open, %s
    ", strerror(errno));
            n = strlen(buff);
            Write(writefd, buff, n);
        } else {
            while ((n = Read(fd, buff, MAXLINE)) > 0) {
                Write(writefd, buff, n);
            }
            Close(fd);
        }
    }

    client.c源码:

    #include "unp.h"
    
    //从标准输入读入文件路径名,
    //通过管道发送给服务器,
    //在从管道接受文件的内容写到标准输出
    void client(int readfd, int writefd)
    {
        char buff[MAXLINE];
    
        Fgets(buff, MAXLINE, stdin);
        size_t len = strlen(buff);
        if (buff[len - 1] == '
    ') {
            --len;
        }
    
        Write(writefd, buff, len);
    
        ssize_t n;
        while((n = Read(readfd, buff, MAXLINE)) > 0) {
            Write(STDOUT_FILENO, buff, n);
        }
    }

    4.FIFO

    与管道最大差别是有名字,适用于无亲缘进程间。

    #include <sys/types.h>

    #include <sys/stat.h>

    int mkfifo(const char *pathname, mode_t mode);

    mkfilo隐含指定O_CREAT | O_EXCL, 要是路径已存在返回EEXIST

    不想创建一个新的FIFO,可以调用open

    mode类似于open的第二个参数,默认定义FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)。

    5.无亲缘关系的客户与服务器

    就是上面的客户端发送文件名,服务器端读取文件内容给客户端的在两个独立进程中的写法。

    服务器端源码:

    #include "unp.h"
    
    void server(int, int);
    
    //服务端创建命名两个管道,
    //在一个上open用于读,在另一个上open用于写
    int main(void)
    {
        //创建时默认O_CREAT | O_EXCL
        //要检测路径是否已经存在,即EEXIST
        if ((mkfifo("/tmp/fifo.1", FILE_MODE)) && (errno != EEXIST)) {
            err_sys("can't create /tmp/fifo.1");
        }
        if ((mkfifo("/tmp/fifo.2", FILE_MODE)) && (errno != EEXIST)) {
            //从文件系统中删除路径,也就时FIFO的名字
            //文件的链接计数减1,
            //等到为0时,且没有进程在使用文件时,删除文件
            unlink("tmp/fifo.1");
            err_sys("can't create /tmp/fifo.2");
        }
    
        int readfd = Open("/tmp/fifo.1", O_RDONLY, 0);
        int writefd = Open("/tmp/fifo.2", O_WRONLY, 0);
    
        server(readfd, writefd);
    
        exit(0);
    }

    客户端源码:

    #include "unp.h"
    
    void client(int, int);
    
    //打开服务器端已经创建好的管道,
    //一个用于读,一个用于写,与服务器端相反
    int main(void)
    {
        int writefd = Open("/tmp/fifo.1", O_WRONLY, 0);
        int readfd = Open("/tmp/fifo.2", O_RDONLY, 0);
    
        client(readfd, writefd);
    
        Close(readfd);
        Close(writefd);
    
        //由客户端删除管道
        Unlink("/tmp/fifo.1");
        Unlink("/tmp/fifo.2");
    
        exit(0);
    }

    server.c 和 client.c参见上面。

    注意:

    使用FIFO时,必须注意open的顺序。

    在没有进程打开FIFO来写,那么打开该FIFO来读的进程将阻塞

    所以在打开FIFO时必须时在一个上读在另一个上写,不能同时打开读然后写。

    open(读);open(写);

       open(读);open(写);

    会造成死锁,都阻塞在读上。

    6. 管道和FIFO额外属性

    参见UNPv2 P44,4.7节。

     

    7.一些特性

    (1) 向FIFO中write是原子性的操作,一次性写入的东西作为一个整体来看待。

    (2) FIFO只能在单台主机上使用的IPC形式。不能用在NFS安装的文件系统中。

    (3) 管道是字节流,系统不对它进行解释。

  • 相关阅读:
    HTML中使用Vue+Dhtmlxgantt制作任务进度图
    Vue中使用js-xlsx导出Data数据到Excel
    Vue生命周期
    ajax调用免费的天气API
    maven无法自动下载依赖包settings.xml文件配置
    idea打开项目没有src目录
    java jdk idea maven安装及配置
    CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://conda.anaconda.org/pytorch
    The procedure entry point OPENSSL_sk_new_reserve could not be located in the dynamic link library
    Nuget打包没有注释显示
  • 原文地址:https://www.cnblogs.com/hancm/p/3885822.html
Copyright © 2011-2022 走看看