首页 > 其他分享 >消息传递:管道和FIFO

消息传递:管道和FIFO

时间:2023-12-01 11:55:59浏览次数:44  
标签:read FIFO write int 管道 fd 消息传递 include buff

一、简介

管道是没有名字的,管道创建的资源由内核管理,单个程序中不同进程通过管道描述符fd进行通信,对于程序和程序之间是无法通信的。

FIFO是有名字的(也称为 有名管道),每一个FIFO都有一个文件与之关联,但仅限于同一主机程序与程序之间通信,无法通过在NFS上创建FIFO通信。

二、管道

所有管道都是半双工的即单向的,只提供一个方向的数据流。管道的经典用途是为两个不同的进程(一个父进程,一个子进程)提供进程间通信手段。

案例示意图:

pipe_example.c

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

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE 1024

void client(int read_fd, int write_fd)
{
    size_t len;
    ssize_t n;
    char buff[MAX_LINE];

    /* read pathname for user input */
    fgets(buff, MAX_LINE, stdin);
    len = strlen(buff);
    if (buff[len - 1] == '\n')
        len--;
    /* write pathname to IPC channel */
    write(write_fd, buff, len);

    /* read for IPC channel, write to standard output */
    while ((n = read(read_fd, buff, MAX_LINE)) > 0)
    {
        write(STDOUT_FILENO, buff, n);
    }
}

void server(int read_fd, int write_fd)
{
    int fd;
    ssize_t n;
    char buff[MAX_LINE];

    /* read pathname for IPC channel. if not any message arrived, block at here. */
    if ((n = read(read_fd, buff, MAX_LINE)) == 0)
    {
        printf("error: end-of-file while reading pathname\n");
        exit(EXIT_FAILURE);
    }
    buff[n] = '\0'; // null terminate pathname
    if ((fd = open(buff, O_RDONLY)) < 0) // error must tell client
    {
        snprintf(buff + n, sizeof(buff) - n, ":can't open, %s\n", strerror(errno));
        n = strlen(buff);
        write(write_fd, buff, n);
    }
    else
    {
        /* read file , then write to pipe channel.
         * if the last character of file is EOF, break while, exit server.
         */
        while ((n = read(fd, buff, MAX_LINE)) > 0)
        {
            write(write_fd, buff, n);
        }
        close(fd);
    }
}

int main(int argc, char **argv)
{
    int pipe1[2], pipe2[2];
    pid_t child_pid;

    /* create 2 pipes */
    if (pipe(pipe1) != 0)
    {
        printf("failed to create pipe1\n");
        exit(EXIT_FAILURE);
    }
    if (pipe(pipe2) != 0)
    {
        printf("failed to create pipe2\n");
        exit(EXIT_FAILURE);
    }

    child_pid = fork();
    if (child_pid == 0) /* child */
    {
        close(pipe1[1]);
        close(pipe2[0]);
        server(pipe1[0], pipe2[1]);
        exit(0);
    }
    else if (child_pid > 0) /* parent */
    {
        close(pipe1[0]);
        close(pipe2[1]);
        client(pipe2[0], pipe1[1]);
        waitpid(child_pid, NULL, 0);
    }
    else
    {
        printf("failed to fork process\n");
        exit(EXIT_FAILURE);
    }

    exit (0);
}

运行测试:

三、FIFO

3.1 进程间通信(FIFO)

对于管道案例进行改写为fifo,修改部分不涉及 server(int, int) and client(int, int)

程序(FIFO)示意图:

fifo_example.c

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

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE 1024
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"

/* default permission for new files.  */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)


void client(int read_fd, int write_fd)
{
    size_t len;
    ssize_t n;
    char buff[MAX_LINE];

    /* read pathname for user input */
    fgets(buff, MAX_LINE, stdin);
    len = strlen(buff);
    if (buff[len - 1] == '\n')
        len--;
    /* write pathname to IPC channel */
    write(write_fd, buff, len);

    /* read for IPC channel, write to standard output */
    while ((n = read(read_fd, buff, MAX_LINE)) > 0)
    {
        write(STDOUT_FILENO, buff, n);
    }
}

void server(int read_fd, int write_fd)
{
    int fd;
    ssize_t n;
    char buff[MAX_LINE];

    /* read pathname for IPC channel. if not any message arrived, block at here. */
    if ((n = read(read_fd, buff, MAX_LINE)) == 0)
    {
        printf("error: end-of-file while reading pathname\n");
        exit(EXIT_FAILURE);
    }
    buff[n] = '\0'; // null terminate pathname
    if ((fd = open(buff, O_RDONLY)) < 0) // error must tell client
    {
        snprintf(buff + n, sizeof(buff) - n, ":can't open, %s\n", strerror(errno));
        n = strlen(buff);
        write(write_fd, buff, n);
    }
    else
    {
        /* read file , then write to pipe channel.
         * if the last character of file is EOF, break while, exit server.
         */
        while ((n = read(fd, buff, MAX_LINE)) > 0)
        {
            write(write_fd, buff, n);
        }
        close(fd);
    }
}

int main(int argc, char **argv)
{
    int read_fd, write_fd;
    pid_t child_pid;

    /* create 2 FIFOs. */
    if ((mkfifo(FIFO1, FILE_MODE)) < 0 && (errno != EEXIST))
    {
        printf("error: can't create %s\n", FIFO1);
        exit(EXIT_FAILURE);
    }
    if ((mkfifo(FIFO2, FILE_MODE)) < 0 && (errno != EEXIST))
    {
        unlink(FIFO1);
        printf("error: can't create %s\n", FIFO2);
        exit(EXIT_FAILURE);
    }

    child_pid = fork();
    if (child_pid == 0) /* child */
    {
        read_fd = open(FIFO1, O_RDONLY, 0);
        write_fd = open(FIFO2, O_WRONLY, 0);
        server(read_fd, write_fd);
        
        exit(0);
    }
    else if (child_pid > 0) /* parent */
    {
        
        write_fd = open(FIFO1, O_WRONLY, 0);
        read_fd = open(FIFO2, O_RDONLY, 0);
        client(read_fd, write_fd);
        waitpid(child_pid, NULL, 0);
        close(read_fd);
        close(write_fd);
    }
    else
    {
        printf("failed to fork process\n");
        exit(EXIT_FAILURE);
    }

    unlink(FIFO1);
    unlink(FIFO2);
    exit (0);
}

运行测试:

3.2 程序间通信(FIFO)

FIFO更多使用在程序和程序间,例如程序A往FIFO写数据,程序B往FIFO读数据。

fifo_client.c

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

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE 1024
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"

/* default permission for new files.  */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void client(int read_fd, int write_fd)
{
    size_t len;
    ssize_t n;
    char buff[MAX_LINE];

    /* read pathname for user input */
    fgets(buff, MAX_LINE, stdin);
    len = strlen(buff);
    if (buff[len - 1] == '\n')
        len--;
    /* write pathname to IPC channel */
    write(write_fd, buff, len);

    /* read for IPC channel, write to standard output */
    while ((n = read(read_fd, buff, MAX_LINE)) > 0)
    {
        write(STDOUT_FILENO, buff, n);
    }
}

int main(int argc, char **argv)
{
    int read_fd, write_fd;

    write_fd = open(FIFO1, O_WRONLY, 0);
    read_fd = open(FIFO2, O_RDONLY, 0);
    client(read_fd, write_fd);

    close(read_fd);
    close(write_fd);
    unlink(FIFO1);
    unlink(FIFO2);

    exit(0);
}

fifo_server.c

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

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE 1024
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"

/* default permission for new files.  */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void server(int read_fd, int write_fd)
{
    int fd;
    ssize_t n;
    char buff[MAX_LINE];

    /* read pathname for IPC channel. if not any message arrived, block at here. */
    if ((n = read(read_fd, buff, MAX_LINE)) == 0)
    {
        printf("error: end-of-file while reading pathname\n");
        exit(EXIT_FAILURE);
    }
    buff[n] = '\0'; // null terminate pathname
    if ((fd = open(buff, O_RDONLY)) < 0) // error must tell client
    {
        snprintf(buff + n, sizeof(buff) - n, ":can't open, %s\n", strerror(errno));
        n = strlen(buff);
        write(write_fd, buff, n);
    }
    else
    {
        /* read file , then write to pipe channel.
         * if the last character of file is EOF, break while, exit server.
         */
        while ((n = read(fd, buff, MAX_LINE)) > 0)
        {
            write(write_fd, buff, n);
        }
        close(fd);
    }
}

int main(int argc, char **argv)
{
    int read_fd, write_fd;
    if ((mkfifo(FIFO1, FILE_MODE)) < 0 && (errno != EEXIST))
    {
        printf("error: can't create %s\n", FIFO1);
        exit(EXIT_FAILURE);
    }
    if ((mkfifo(FIFO2, FILE_MODE)) < 0 && (errno != EEXIST))
    {
        unlink(FIFO1);
        printf("error: can't create %s\n", FIFO2);
        exit(EXIT_FAILURE);
    }

    read_fd = open(FIFO1, O_RDONLY, 0);
    write_fd = open(FIFO2, O_WRONLY, 0);
    server(read_fd, write_fd);

    exit(0);
}

运行测试:
先启动 fifo_server程序,再启动 fifo_client程序。

3.3 单服务器-多客户端通信(FIFO)

FIFO的真正优势表现在服务器可以是一个长期运行的进程(例如 守护进程),而且与其客户可以无情缘关系。

如下例子:客户端通过 众所周知的路径名创建的FIFO 发送一个请求,服务器解析请求并通过创建一个与客户端pid关联的FIFO,将响应写入这个FIFO。客户端发送请求后,打开 一个与自己pid关联的FIFO ,等待服务器的响应。

服务端代码
server.c

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

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE 1024
#define SERV_FIFO "/tmp/fifo.serv"

/* default permission for new files.  */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

int read_cnt = 0;
char *read_ptr = NULL;
char read_buf[MAX_LINE] = {0};

static ssize_t my_read(int fd, char *ptr)
{
    if (read_cnt <= 0)
    {
    again:
        if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)
        {
            if (errno == EINTR)
                goto again;
            return -1;
        }
        else if (read_cnt == 0)
        {
            return 0;
        }
        read_ptr = read_buf;
    }

    read_cnt--;
    *ptr = *read_ptr++;
    return 1;
}

/*
 * @return if the return value greater than or equal to 0, result is successful.
 * if 0, read EOF. if -1, error occur.  
*/
ssize_t readline(int fd, void *vptr, size_t max_len)
{
   ssize_t i = 0; 
   ssize_t ret = 0;
   char ch = '\0'; 
   char *ptr = NULL;

   ptr = (char *)vptr;
   for (i = 1; i < max_len; i++)
   {
        if ((ret = my_read(fd, &ch)) == 1)
        {
            *ptr++ = ch;
            if (ch == '\n') // newline is stored, return
                break;
        }
        else if (ret == 0) // EOF, n - 1 bytes were read
        {
            *ptr = 0;
            return (i - 1);
        }
        else
        {
            return -1; // error, errno set by read()
        } 
   }

   *ptr = 0; // null terninate
    return i;
}

int main(int argc, char **argv)
{
    int read_fifo, write_fifo, dummy_fd, fd;
    char *ptr, buff[MAX_LINE + 1], fifo_name[MAX_LINE];
    pid_t pid;
    ssize_t n;

    /* create server's well-known FIFO, server read-only */
    if ((mkfifo(SERV_FIFO, FILE_MODE)) < 0 && errno != EEXIST)
    {
        printf("error: can't create %s\n", SERV_FIFO);
        exit(EXIT_FAILURE);
    }
    read_fifo = open(SERV_FIFO, O_RDONLY, 0);
    dummy_fd = open(SERV_FIFO, O_WRONLY, 0); // never use

    /* receive request from client, and respond to client */
    while ((n = readline(read_fifo, buff, MAX_LINE)) > 0)
    {
        printf("server read (%s) fifo msg:%s", SERV_FIFO, buff);
        if (buff[n - 1] == '\n')
            n--;
        buff[n] = '\0';
       
        if ((ptr = strchr(buff, ' ')) == NULL)
        {
            printf("bogus request: %s\n", buff);
            continue;
        }
        *ptr++ = 0; // null terminate PID, ptr = pathname
        pid = atol(buff);
        snprintf(fifo_name, sizeof(fifo_name), "/tmp/fifo.%ld", (long)pid);
        if ((write_fifo = open(fifo_name, O_WRONLY, 0)) < 0)
        {
            printf("error: can't open %s\n", fifo_name);
            continue;
        }
        if ((fd = (open(ptr, O_NDELAY))) < 0)
        {
            /* error must tell client */
            snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n", strerror(errno));
            n = strlen(ptr);
            write(write_fifo, ptr, n);
            close(write_fifo);
        }
        else
        {
            /* open succeeded: copy file to FIFO */
            while ((n = read(fd, buff, MAX_LINE)) > 0)
            {
                write(write_fifo, buff, n);
            }
            close(fd);
            close(write_fifo);
        }
    }

    exit(0);
}

客户端代码
client.c

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

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

#define MAX_LINE 1024
#define SERV_FIFO "/tmp/fifo.serv"

#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

int main(int argc, char **argv)
{
    int read_fifo, write_info;
    size_t len;
    ssize_t n;
    char *ptr, fifo_name[MAX_LINE], buff[MAX_LINE];
    pid_t pid;

    /* create FIFO with our PID as part of name */
    pid = getpid();
    snprintf(fifo_name, sizeof(fifo_name), "/tmp/fifo.%ld", (long)pid);
    if ((mkfifo(fifo_name, FILE_MODE) < 0) && (errno != EEXIST))
    {
        printf("error: can't create %s\n", fifo_name);
        exit(EXIT_FAILURE);
    }
    snprintf(buff, sizeof(buff), "%ld ", (long)pid);
    len = strlen(buff);
    ptr = buff + len;
    if (write_info = open(SERV_FIFO, O_WRONLY, 0) < 0)
    {
        printf("error: can't open %s\n", SERV_FIFO);
        unlink(fifo_name);
        exit(EXIT_FAILURE);
    }
    /* read pathname from stdin, and append buff string.
     * read string from stdin include '\n'
     */
    fgets(ptr, MAX_LINE - len, stdin);
    len = strlen(buff);
    write(write_info, buff, len);
    printf("client write (%s)fifo msg:%s", SERV_FIFO, buff);
    /* open our FIFO, blocks until server opens for writing */
    read_fifo = open(fifo_name, O_RDONLY, 0);
    while ((n = read(read_fifo, buff, MAX_LINE)) > 0)
    {
        write(STDOUT_FILENO, buff, n);
    }
    close(read_fifo);
    unlink(fifo_name);

    exit(0);
}

运行测试:

若服务端未启动,客户端启动时,无法发送请求。

启动服务端后,再启动客户端

通过 shell命令(echo) 给服务端发送请求,然后读取响应内容。

四、管道和FIFO限制

系统对于管道和FIFO的唯一限制为:

  • OPEN_MAX : 一个进程在任意时刻打开的最大描述符数(Posix要求至少为16)
  • PIPE_BUF : 可原子地写往管道或FIFO的最大数据量(Posix要求至少为512)

标签:read,FIFO,write,int,管道,fd,消息传递,include,buff
From: https://www.cnblogs.com/caojun97/p/17861297.html

相关文章

  • 环形缓冲区FIFO
    -------------------------------------------------------------------------------------------------------------------------------------最近学习一个LwRB开源环形缓冲区FIFO设计,即先入先出缓冲区。LwRB是一个开源、通用环形缓冲区库。  1、只有单个任务写和单个任务......
  • I/O的重新定向与管道
    1.I/O重定向文件描述符:0表示标准输入;1表示标准输出,输出到终端;2表示标准错误,输出到终端;3及以上为常规文件的描述符。(1)输出重定向”>“表示覆盖,默认情况下,该重定向会覆盖已有文件,这个在有时候可能不经意间丢失重要数据。”>>“表示追加,可以避免覆盖文件。”2>“:表示错误输出覆......
  • Linux基本命令之I/O重定向和管道
    I/O重定向,即输入/输出重定向,是指把命令行的输入重定向为从文件中获取内容,也可以把命令行的输出结果重定向到文件中。1.标准输入、标准输出和标注错误输出主要有两种:一种是程序运行的结果,即该程序生成的数据;一种是状态和错误信息。我们知道,UNIX的思想是“一切都是文件”,类似ls的程......
  • I/O重定向与管道
    1.I/O重定向有两种基本的方法可以用来重定向标准输入(stdin)、标准输出(stdout)和标准错误(stderr)。可以利用管道把这些数据流之一输送给另外一条命令;也可以利用I/O重定向把这些数据流之一重定向到一个文件。管道及I/O重新定向是Linux系统中特有的概念。所谓管道是指将某个命令的输出......
  • 第6章 I/O重定向与管道
     1.I/O重定向1.1标准输入重定向标准输入是从键盘读取数据,使用 < 可以将标准输入重定向为文件。解释:cat 命令用于将文件内容输出到标准输出。<input.txt 表示将文件 input.txt 的内容作为标准输入。 1.2标准输出重定向标准输出是将结果输出到屏幕,使用 >......
  • Linux I/O 重定向与管道
    学习笔记:LinuxI/O重定向与管道引言在Linux中,I/O重定向与管道是强大的工具,用于处理输入输出流,使命令行操作更加灵活和高效。本文将深入讨论Linux中的I/O重定向和管道,介绍其基本概念、用法以及在实际应用中的示例。I/O重定向1.标准输入重定向(<)标准输入重定向允许从文......
  • java.io.IOException: 断开的管道
      2023-10-0911:45:51.834ERROR[http-nio-8080-exec-33]com.zkh360.gbb.admin.component.GlobalExceptionHandler82-[TxId:,SpanId:][,,][e979a4ac16968231501251001f1577]【Exception】异常处理,org.apache.catalina.connector.ClientAbortException:java.io.IO......
  • Linux管道符
    管道符(`|`)是在Unix-like操作系统中常用的重要工具,它用于将一个命令的输出传递给另一个命令的输入,从而实现两个或多个命令之间的数据流通。以下是一些管道符的应用场景:1. **组合多个命令:** 可以使用管道将多个命令组合在一起,以执行复杂的任务。2. **使用awk或sed进行文本处理:**......
  • Linux I\O重定向与管道
    一:I\O重定向 linu系统中的三种I/O设备所代表的编号分别是:标准输入(STDIN),文件描述符为0,默认从键盘获取输入;标准输出(STDOUT),文件描述符为1,默认输出到显示屏;标准错误(STDERR),文件描述符为2,默认输出到显示屏。 I/O重定向就是为了改变默认输入、输出的位置:>:表示标准输出覆盖重定向;>>......
  • Linux I/O重定向与管道
    1.I/O重定向命令用法cat:查看文件内容sort:对文本内容排序uniq:用于报告或忽略文件中的重复行,一般与sort命令结合使用grep:找出匹配的行wc:打印文件中换行符,字,和字节个数head:输出文件开头部分tail:输出文件结尾部分tee:从标准输入读取数据,并同时写到标准输出和文件输出重定向......