Use of Two Modes of Famous Pipeline

Preface

Interprocess Communication (IPC) refers to the dissemination or exchange of information between different processes.
The main ways are pipes (including nameless pipes, advanced pipes and named pipes), message queues, semaphores, shared memory, sockets, etc. Socket can be used for inter-process communication on different hosts.
The main purposes of process communication are as follows:

  • Data transmission: A process needs to send its data to another process, and the amount of data sent is between one byte and several M bytes.

  • Shared data: Multiple processes want to operate on shared data, one process changes the shared data, and other processes should see it immediately.

  • Notification events: A process needs to send a message to another or a group of processes to inform them that something has happened (e.g., notify the parent process when the process terminates).

  • Resource sharing: Multiple processes share the same resource. To do this, the kernel needs to provide lock and synchronization mechanisms.

  • Process control: Some processes want complete control over the execution of another process (e.g. Debug process), in which case the control process wants to intercept all traps and exceptions of another process and be able to know its state changes in time.

IPC mode

  • Pipeline: Pipeline is a half-duplex communication mode, data can only flow in one direction, and can only be used between processes with affinity. The relationship of process usually refers to the relationship between father and son.

  • Advanced Pipeline: If another program is started as a new process in the current process, then it is a sub-process of the current program. This way we become the advanced pipeline mode.

  • Named pipe: Well-known pipe is also a half-duplex communication mode, but it allows communication between unrelated processes.

  • Message queue: A message queue is a linked list of messages stored in the kernel and identified by the message queue identifier. Message queue overcomes the shortcomings of less signal transmission information, pipeline can only carry unformatted byte stream and buffer size limitation.

  • Semophore: A semophore is a counter that can be used to control access to shared resources by multiple processes. It is often used as a lock mechanism to prevent a process from accessing a shared resource while other processes are accessing the resource. Therefore, it is mainly used as a means of synchronization between processes and between different threads in the same process.

  • Signal (sinal): Signal is a complex way of communication to inform the receiving process that an event has occurred.

  • Shared memory: Shared memory is the mapping of a piece of memory that can be accessed by other processes. This shared memory is created by one process, but can be accessed by multiple processes. Shared memory is the fastest IPC mode. It is specially designed for the low efficiency of other inter-process communication modes. It is often used in conjunction with other communication mechanisms, such as signal two, to achieve synchronization and communication between processes.

  • Socket: Socket is also an inter-process communication mechanism. Unlike other communication mechanisms, socket can be used for process communication between different machines.

name pipes

This article mainly explains the command pipeline.
Named pipes exist as a special device file in the file system. Data can be shared between processes of different ancestors through pipes. When all processes accessing pipes are finished, named pipes will continue to exist for future use.

Creation of Named Pipeline

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

mkfifo is used to create a FIFO file, and the parameter mode l determines the file permissions.
The file creation mask of the process (see umask()) modifies the file permission bits of mode. The owner ID of the FIFO is set to the effective user ID of the process. The FIFO group ID is set to the effective group ID of the process.

If successful, mkfifo() returns 0; if unsuccessful, it returns - 1, and errno is assigned the following value:

  • EACCES: A level path has no access rights, or a directory pointed to by pathname has no write rights

  • EEXIST: The file with the same name already exists

  • EINVAL: Describing paths on NuTCRCKER platform is not recorded in fifos

  • ENAMETOOLONG: pathname name is too long

  • ENOENT: A level directory does not exist in the path

  • ENOSPC: Disk full

  • ENOTDIR: A level in the path is not a directory

  • EROFS: File System Read-Only

Details can be viewed: [here]( http://man7.org/linux/man-pag...)

The file mode is as follows:

  • S_IRWXU: 00700 read, write, execute/search by owner

  • S_IRUSR: 00400 read permission, owner

  • S_IWUSR: 00200 write permission, owner

  • S_IXUSR: 00100 execute/search permission, owner

  • S_IRWXG: 00070 read, write, execute/search by group

  • S_IRGRP: 00040 read permission, group

  • S_IWGRP: 00020 write permission, group

  • S_IXGRP: 00010 execute/search permission, group

  • S_IRWXO: 00007 read, write, execute/search by others

  • S_IROTH: 00004 read permission, others

  • S_IWOTH: 00002 write permission, others

  • S_IXOTH: 00001 execute/search permission, others

  • S_ISUID: 0004000 set-user-ID on execution

  • S_ISGID: 0002000 set-group-ID on execution

  • S_ISVTX: 0001000 on directories, restricted deletion flag

System I/O functions such as open, close, read, write can operate FIFO files.

Read/write, blocking/non-blocking

block

Blocking mode when O_NONBLOCK is not specified by default

  • When open opens FIFO in read-only mode, it blocks a process to open FIFO for writing

  • When open opens FIFO in write-only mode, it blocks a process to open the FIFO for reading.

  • When open opens FIFO in read-only + write-only mode, read blocks when calling read function

  • When the write function is called to write data to FIFO, the write will block when the buffer is full

  • In the process of communication, if the write process exits first, it will not block when calling read function to read data from FIFO; if the write process runs again, it will resume blocking when calling read function to read data from FIFO.

  • If the reading process exits first in the communication process, the writing process writes data to FIFO, and receives SIGPIPE signal and exits.

  • open does not block when FIFO is opened in read-write mode

Non blocking

  • open opens FIFO in a read-only manner. If no process opens FIFO for writing, read-only opens successfully and opens without blocking.

  • Open opens FIFO in a write-only manner, and if no process opens FIFO for reading, writing open only returns an error to - 1 (ENXIO: No Such device or address)

  • read, write read and write FIFO data without blocking

  • In the communication process, when the read process exits and the write process writes data to the named pipeline, the write process will receive SIGPIPE signal and exit.

Examples

Non-Blocking

reader

//
// Created by         : Harris Zhu
// Filename           : fifo_read.cpp
// Author             : Harris Zhu
// Created On         : 2017-08-17 16:46
// Last Modified      :
// Update Count       : 2017-08-17 16:46
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================


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

#define FIFO_SERVER "myfifo"
#define OPEN_MODE O_RDONLY | O_NONBLOCK
#define FIFO_MODE O_CREAT|O_RDWR|O_NONBLOCK

int main(int argc, char** argv) {
    char buf_r[100];
    int fd;
    int nread;
    int res;
    if (((res=mkfifo(FIFO_SERVER, FIFO_MODE)) < 0) && (errno != EEXIST))
    {
        printf("can not creat fifoserver %d :\n", res, errno);
        exit(1);
    }
    printf("preparing for reading bytes...\n");
    char cmd[100];
    sprintf(cmd, "chmod 704 %s", FIFO_SERVER);
    system(cmd);
    fd = open(FIFO_SERVER, OPEN_MODE);
    if (fd == -1) {
        perror("error in openning fifo server");
        exit(1);
    }
    int i=0;
    int len;
    while (i++<21)
    {
        memset(buf_r, 0, sizeof(buf_r));
        if ((nread = read(fd, buf_r, sizeof(buf_r))) < 0) {
            if (errno == EAGAIN)
            {
                printf("no data yet\n");
                sleep(1);
            }
        } else {
            if(nread > 0)
            {
                printf("read %s from FIFO %d \n", buf_r, i);
            }
            sleep(1);
        }
    }
//    pause();
    close(fd);
    unlink(FIFO_SERVER);
    return 0;
}

writer

//
// Created by         : Harris Zhu
// Filename           : fifo_write.c
// Author             : Harris Zhu
// Created On         : 2017-08-17 18:04
// Last Modified      :
// Update Count       : 2017-08-17 18:04
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

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

#define FIFO_SERVER "myfifo"
#define FIFO_MODE O_CREAT|O_NONBLOCK|O_RDWR
#define FILE_MODE O_WRONLY | O_NONBLOCK

int main(int argc, char** argv)
{
    int fd;
    char w_buf[100];
    char w_t_buf[50];
    const char *hstr = "hello world";
    if(mkfifo(FIFO_SERVER, FIFO_MODE<0)&& (errno != EEXIST))
    {
        perror("failed to create fifo server");
        exit(1);
    }

    char cmd[100];
    sprintf(cmd, "chmod 704 %s", FIFO_SERVER);
    system(cmd);

    int nwrite;
    fd = open(FIFO_SERVER, FILE_MODE);
    if (fd == -1)
    {
        if (errno == ENXIO)
        {
            printf("open errop;no reading process\n");
        } else {
            perror("open error");
            exit(EXIT_FAILURE);
        }
    }

    if (argc >= 2)
    {
        strcpy(w_t_buf, argv[1]);
    } else {
        strcpy(w_t_buf, hstr);
    }
    int i=0;
    int n;
    time_t tp;
    while(i++<20)
    {
        time(&tp);
        n=sprintf(w_buf, "Process %d is sending %s at %s", getpid(), w_t_buf, ctime(&tp));
        if ((nwrite = write(fd, w_buf, n)) < 0)
        {
            if (errno == EAGAIN)
            {
                printf("the fifo has not been read yet.Please try later\n");
            } else {
                exit(1);
            }
        }
        printf("Send Message to FIFO: %s \n", w_buf);
        sleep(1);
    }
    close(fd);
    return 0;
}

Run reader first, then writer
Makefile reads as follows

build:
    gcc -g read.c -o read
    gcc -g write.c -o write

run:
    xterm -e ./read &
    sleep 1
    xterm -e ./write &

clean:
    rm -rf read write

Because the reader starts reading first, when the writer starts, the reader has already passed through the while and the individual loop, so the reader will end first, when the writer also wants to write data to FIFO, it will encounter the "Broken pipe" problem.
If we reduce the waiting time of writer s, the receivers will be slower, so there will be more data in fifo, and the reader will not receive one line at a time.

harriszh Fri 17:30@ ~/trunk/cpp/fifo1$ ./fifo_read
preparing for reading bytes...
read Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello worl from FIFO
read d at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Proce from FIFO
read ss 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at  from FIFO
read Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11 from FIFO
read 863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri A from FIFO
read ug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 i from FIFO
read s sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 from FIFO
read  17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sen from FIFO
read ding hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:3 from FIFO
read 1:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending  from FIFO
read hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57  from FIFO
read 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello from FIFO
read  world at Fri Aug 18 17:31:57 2017
Process 11863 is sending hello world at Fri Aug 18 17:31:57 2017
 from FIFO

Blocking

It's simpler to look at the blockage after seeing the non-blockage
The code is as follows
Reader

//
// Created by         : Harris Zhu
// Filename           : write.c
// Author             : Harris Zhu
// Created On         : 2017-08-17 22:05
// Last Modified      :
// Update Count       : 2017-08-17 22:05
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(int argc, char *argv[])
{

    int outfd = open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (outfd == -1)
        ERR_EXIT("open Makefile2 error");

    int infd;
    infd = open("tp", O_RDONLY );
    if (infd == -1)
        ERR_EXIT("open tp error");

    char buf[1024];
    int n;
    while ((n = read(infd, buf, 1024)) > 0)
        write(outfd, buf, n);

    close(infd);
    close(outfd);
    unlink("tp"); // delete a name and possibly the file it refers to
    return 0;
}

Writer

//
// Created by         : Harris Zhu
// Filename           : read.c
// Author             : Harris Zhu
// Created On         : 2017-08-17 22:05
// Last Modified      :
// Update Count       : 2017-08-17 22:05
// Tags               :
// Description        :
// Conclusion         :
//
//=======================================================================

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(int argc, char *argv[])
{
    mkfifo("tp", 0644);
    int infd = open("Makefile", O_RDONLY );
    if (infd == -1)
        ERR_EXIT("open Makefile error");

    int outfd;
    outfd = open("tp", O_WRONLY );
    if (outfd == -1)
        ERR_EXIT("open tp error");

    char buf[1024];
    int n;
    while ((n = read(infd, buf, 1024)) > 0)
        write(outfd, buf, n);
    printf("Makefile2 is created successfully!\n")

    close(infd);
    close(outfd);

    return 0;
}

Run writer first, then reader, because FIFO was created by writer
Makefile

build:
    gcc -g read.c -o read
    gcc -g write.c -o write

clean:
    rm -rf Makefile2
    rm -rf ./read ./write

run:
    xterm -e ./write &
    sleep 0.1
    xterm -e ./read &

.PHONY: clean

After running, the reader saves the content written from the writer to Makefile 2

Keywords: Linux socket Makefile less

Added by Ascendancy on Mon, 03 Jun 2019 02:30:11 +0300