[Named Pipes] Named pipes (FIFO) communicate with processes

Table of contents

0. Named Pipes

1. Creation of famous pipes

2. Basic read and write operations on famous pipes

3. Named pipes implement inter-process communication

4. Reading and writing rules of famous pipes (blocking)

4.1 Both reading and writing ends exist, only reading but not writing

4.2 Both reading and writing ends exist, only writing but not reading

4.3 In the same process, there is only the reading end and no writing end.

4.4 In the same process, there are only write segments and no reading segments.

4.5 One process is read-only and one process is write-only.

 Summarize:


0. Named Pipes

Named pipes (FIFOs) and pipes are basically the same, but there are some significant differences.

Its characteristics are:

  • Half-duplex, data can only flow in one direction at a time.
  • Data written to the FIFO follows the first-in, first-out rule.
  • The data transmitted by FIFO is unformatted, which requires the reader and writer of FIFO to agree on the format of the data in advance, such as how many bytes count as a message, etc.
  • FIFO exists as a special file in the file system and is visible in the file system, so named pipes can implement unrelated inter-process communication, but the contents of the FIFO are stored in memory.
  • A pipe corresponds to a buffer in memory. Different systems are not necessarily the same size.
  • Reading data from the FIFO is a one-time operation. Once the data is read, it is discarded from the FIFO, freeing up space for writing more data.
  • When the process using FIFO exits, the FIFO file will continue to be saved in the file system for later use.
  • FIFOs have names, and unrelated processes can communicate by opening named pipes.

1. Creation of famous pipes

Method 1: Use the shell command mkfifo to create a named pipe

mkfifo file name

 

Method 2: Use function mkfifo

#include<sys/types.h>

#include<sys/stat.h>

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

Function: Create a named pipe and generate a file pathname visible to the local file system

parameter:

        pathname: the file generated by the famous pipe creation, which can be the code path

        mode: The permissions of the pipeline file, usually set by octal number, such as 0664

return value:

        Success: 0

        Failure: -1

Code example:

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

int main()
{
    if (mkfifo("fifo_file", 0664) == -1)
    {
        //              printf("errno:%d\n",errno);
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    return 0;
}

2. Basic read and write operations on famous pipes

        Since the famous pipe creates a pipe file locally, the IO functions called by the system can basically operate on the famous pipe, but you cannot use lseek to modify the offset of the pipe file.

        Note: The local file created by the famous pipe only serves a representational purpose. The real famous pipe implements inter-process communication or opens up memory in the kernel space, so the locally generated file is just an identifier and has no other effect. The essence of the operation of the local pipe file is Operations on kernel space.

Code example:

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

#define FIFONAME "fifo_file"
int main()
{
    if (mkfifo(FIFONAME, 0664) == -1)
    {
        //              printf("errno:%d\n",errno);
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    int fd;
    //打开
    fd = open(FIFONAME, O_RDWR);
    if (fd == -1)
    {
        perror("fail to open");
        exit(1);
    }
    //写入数据
    if (write(fd, "hello world", strlen("hello world")) == -1)
    {
        perror("fail to write");
        exit(1);
    }
    //再次写数据
    write(fd, "nihao bejing", strlen("hello world"));
    char buf[32] = "";
    //读数据
    if (read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = [%s]\n", buf);

    /*     此时管道中无数据,再读则阻塞
    if (read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    */
    //关闭
    close(fd);
    return 0;
}

3. Named pipes implement inter-process communication

        Since the named pipe creates a pipe file locally, communication can also be achieved between all unrelated processes.

The following programs 1 and 2 are two different processes communicating through named pipes.

Procedure 1:

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

#define FIFO_01 "fifo_01"
#define FIFO_02 "fifo_02"


int main()
{
    if (mkfifo(FIFO_01, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    if (mkfifo(FIFO_02, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    int fd_w, fd_r;
    if ((fd_w = open(FIFO_01, O_WRONLY)) == -1)
    {
        perror("fail to oepn");
        exit(1);
    }
    if ((fd_r = open(FIFO_02, O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    //      printf("%d %d\n",fd_w,fd_r);
    char buf[128] = "";
    ssize_t bytes;
    pid_t pid;
    pid = fork();

    if (pid < 0)
    {
        perror("fail to fokr");
        exit(1);
    }
    else if (pid > 0)
    {
        while (1)
        {
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = '\0';

            if ((bytes = write(fd_w, buf, sizeof(buf))) == -1)
            {
                perror("fail to write");
                exit(1);
            }
        }
    }
    else
    {
        while (1)
        {
            if ((bytes = read(fd_r, buf, sizeof(buf))) == -1)
            {
                perror("fail to read");
                exit(1);
            }
            if (bytes)
            {
                buf[bytes] = '\0';
                printf("from fifo_02: %s\n", buf);
                memset(buf, 0, sizeof(buf));
            }
        }
    }
    return 0;
}

Procedure 2:

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

#define FIFO_01 "fifo_01"
#define FIFO_02 "fifo_02"


int main()
{
    if (mkfifo(FIFO_01, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    if (mkfifo(FIFO_02, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    int fd_w, fd_r;
    if ((fd_r = open(FIFO_01, O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }
    if ((fd_w = open(FIFO_02, O_WRONLY)) == -1)
    {
        perror("fail to oepn");
        exit(1);
    }

    //      printf("%d %d\n",fd_w,fd_r);
    char buf[128] = "";
    ssize_t bytes;
    pid_t pid;
    pid = fork();

    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        while (1)
        {
            if ((bytes = read(fd_r, buf, sizeof(buf))) == -1)
            {
                perror("fail to read");
                exit(1);
            }
            if (bytes)
            {
                buf[bytes] = '\0';
                printf("from fifo_01: %s\n", buf);
                memset(buf, 0, sizeof(buf));
            }
        }
    }
    else
    {
        while (1)
        {

            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf) - 1] = '\0';

            if ((bytes = write(fd_w, buf, sizeof(buf))) == -1)
            {
                perror("fail to write");
                exit(1);
            }
        }
    }
    return 0;
}

 Running screenshot:

4. Reading and writing rules of famous pipes (blocking)

4.1 Both reading and writing ends exist, only reading but not writing

    //Both reading and writing ends exist, only reading and not writing
    //If there is data in the original pipe, read normally
    //If there is no data in the pipe, the read function will block and wait

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

#define FIFONAME1 "myfifo"

int main()
{
    if (mkfifo(FIFONAME1, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    //读写端都存在,只读不写
    //如果原本管道中有数据,则正常读取
    //如果管道中没有数据,则read函数会阻塞等待
    int fd;
    if ((fd = open(FIFONAME1, O_RDWR)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    write(fd, "hello world", 11);

    char buf[128] = "";
    read(fd, buf, sizeof(buf));
    printf("buf = %s\n", buf);
    
    read(fd, buf, sizeof(buf));
    printf("buf = %s\n", buf);

    return 0;
}

Results of the:

4.2 Both reading and writing ends exist, only writing but not reading

    //Both reading and writing ends exist, only writing but not reading
    //When the buffer of the famous pipe is full, the write function will send a blocking
    //The default buffer of the famous pipe is 64K bytes

Code example:

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

#define FIFONAME "myfifo"

int main()
{
    if (mkfifo(FIFONAME, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    
    //读写端都存在,只写不读
    //当有名管道的缓冲区写满后,write函数会发送阻塞
    //默认有名管道的缓冲区为64K字节
    int fd;
    if ((fd = open(FIFONAME, O_RDWR) == -1)
    {
        perror("fail to open");
            exit(1);
    }

    int num = 0;
        while (1)
        {
            write(fd, "", 1024);
                num++;
                printf("num = %d\n", num);
        }
    return 0;
}

Results of the:

4.3 In the same process, there is only the reading end and no writing end.

    //In a process, there is only the reading end and no writing end.
    //It will block at the position of the open function.

Code example:

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

#define FIFONAME1 "myfifo"

int main()
{
    if (mkfifo(FIFONAME1, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    //在一个进程中,只有读端,没有写端
    //会在open函数的位置阻塞
    printf("********************************\n");
    int fd;
    if ((fd = open(FIFONAME1, O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }
    printf("___________________________\n");

    char buf[128] = "";
    ssize_t bytes;
    if ((bytes = read(fd, buf, sizeof(buf))) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = %s\n", buf);
    printf("bytes = %ld\n", bytes);

    return 0;
}

Results of the:

4.4 In the same process, there are only write segments and no reading segments.

    //In a process, there is only a writing end and no reading end.
    //It will block at the position of the open function.

Code example:

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

#define FIFONAME "myfifo"

int main()
{
    if (mkfifo(FIFONAME, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    //在一个进程中只有写端,没有读端
    //会在open函数的位置阻塞
    int fd;
    if ((fd = open(FIFONAME, O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    write(fd, "hello world", 11);
    printf("****************************\n");
    return 0;
}

Results of the:

4.5 One process is read-only and one process is write-only.

Run the above 4.3 and 4.4 codes together to ensure that both the read and write ends of the famous pipe exist.

law:

        As long as the read and write ends of the named pipe are guaranteed to exist, no matter how many processes there are, they will not be blocked in open.

        If one process is read-only and one process is write-only, after both are run, if the relationship is write-side, read-side read will return 0;

        If a process is read-only and a process is write-only, after both are running, if the read end is closed, the write end will immediately generate a SIGPIPE signal, and the default processing method is to exit the process.

5. Reading and writing rules of famous pipes (non-blocking)

        Specify O_NONBLOCK (i.e. open bit or O_NONBLOCK)

  • First open in read-only mode. If no process opens a FIFO for writing, the read-only open succeeds and the open does not block.
  • First open in write-only mode. If no process opens a FIFO for reading, write-only open will return -1 with an error.
  • read and write do not block when reading data in named pipes
  • During the communication process, after the reading process exits, when the writing process wants to read some data in the named pipe, the writing process will also exit (receiving the SIGPIPE signal).

Code example:

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

#define FIFONAME "myfifo"

int main()
{
    int  fd;

    if (mkfifo(FIFONAME, 0664) == -1)
    {
        if (errno != EEXIST)
        {
            perror("fail to mkfifoJ");
            exit(1);
        }
    }
#if 0
    //如果open标志位设置为非阻塞,并且以只读的方式打开管道文件
    //open函数和read函数都不会阻塞
    fd = open(FIFONAME, O_RDONLY | O_NONBLOCK);
    if (fd < 0)
    {
        perror("fail to open");
        exit(1);
    }

    while (1)
    {
        char recv[100] = "";
        memset(recv, 0, sizeof(recv));
        read(fd, recv, sizeof(recv));
        printf("read from myfifo buf = [%s]\n", recv);
        sleep(1);
    }
#endif

#if 0
    //如果open标志位设置为非阻塞,并且以只写方式打开管道文件
    //open函数会直接报错
    char send[100] = "Hello I love you";

    fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
    if (fd < 0)
    {
        perror("fail to open");
        exit(1);
    }
    write(fd, send, strlen(send));
    printf("write to myfifo buf = %s\n", send);

    char recv[100];
    read(fd, recv, sizeof(recv));
    printf("read from myfifo buf = [%s]\n", recv);
#endif
#if 1
    //如果open标志位设置为非阻塞,并且以读写方式打开管道文件
    //这样和阻塞是一样的效果。
    char send[100] = "Hello I love you";

    fd = open(FIFONAME, O_RDWR | O_NONBLOCK);
    if (fd < 0)
    {
        perror("fail to open");
        exit(1);
    }
    write(fd, send, strlen(send));
    printf("write to myfifo buf = %s\n", send);

    char recv[100];
    read(fd, recv, sizeof(recv));
    printf("read from myfifo buf = [%s]\n", recv);
#endif
    return 0;
}

Execution screenshot:

 

 Summarize:

        Named pipes (FIFO) provide us with a simple and effective way to transfer data between different processes. Learn how to use FIFO correctly and avoid common problems, such as permission setting and buffer management. Although it has its complexity, as long as we master these points, we can make good use of FIFO to facilitate communication between processes.

Guess you like

Origin blog.csdn.net/crr411422/article/details/131421645