Interprocess Communication -- Pipeline, Message Queue

Posted by MarkR on Tue, 09 Jul 2019 01:58:52 +0200

We learn advanced process communication, which means that users can efficiently transmit large amounts of data using a set of communication commands provided by the operating system.
Several main means of interprocess communication: pipeline, message queue, shared memory, socket

The Conduit

Pipeline, a shared file used to connect a read process and a write process to communicate between them, exists only in memory and is a one-way communication mechanism between two processes. When creating a pipeline, the system assigns a page to the pipeline as a data buffer. Two processes that communicate through the pipeline communicate with each other by reading and writing the buffer, writing at the end of the buffer, and reading data from the head.

Limitations:

One-way data transfer, i.e. half-duplex; pipeline has no name and can only be used for inter-process communication with relatives; pipeline buffer size is limited; pipeline conveys unformatted byte streams.

Pipeline creation:

int pipe(int fd[2]) / / call returns 0 successfully, and the array contains two new fds, failing to return - 1

Both ends of the pipeline are described by fd[0] and fd[1]. The tasks at both ends of the pipeline are fixed, and fd[0] can only be used for reading, which is called the pipeline reading end; fd[1] can only be used for writing, which is called the pipeline writing end.

Once a pipeline is created successfully, it can be used as a general file, and I/O functions that operate on general files are also applicable to pipelines.

Pipeline Read-Write: If a process wants to read the data in the pipeline, the process should close fd[1], and the process that writes data to the pipeline should close fd[0]. Writing data to a pipe is similar.

In these two examples, pipe is called before fork, so that the child process can share the file descriptor of the parent process directly. But if the subprocess calls the exec function to execute another program, it cannot be shared. In this case, the file descriptor in the child process can be redirected to standard input. When the newly executed program retrieves data from standard input, it actually retrieves input data from the parent process.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
/*read pipe*/
void read_from_pipe(int fd)
{
    char message[100];
    read(fd,message,100);
    printf("read from pipe:%s\n",message);
}

/*write pipe*/
void write_from_pipe(int fd)
{
    char message[100];
    printf("input message: ");
    scanf("%s",message);
    write(fd,message,strlen(message)+1);
}

int main()
{
    int fd[2];
    pid_t pid;
    int stat_val;
    if(pipe(fd))
    {
        printf("create pipe failed\n");
        exit(1);
    }

    pid = fork();
    switch(pid)
    {
        case -1:
            printf("fork error!\n");
            exit(1);
        case 0:  /*Subprocess Closes Writer Side*/
            close(fd[1]);
            read_from_pipe(fd[0]);
            exit(0);
        default:/*The parent process closes the reader*/
            close(fd[0]);
            write_from_pipe(fd[1]);
            exit(0);
    }

    return 0;
}

Famous Pipeline: nameed pipe or FIFO

A named pipeline is a device file that provides a path name associated with it and is stored in the file system as a FIFO file. As long as the process can access the path, it can communicate with each other through FIFO. Similar to queues, FIFO, the first written data is first read from the pipeline.
Creation and Reading and Writing of Famous Pipeline
The first way: Create under the shell using mknod or mkfifo commands
The second way: system function

 #include <sys/types.h>
 #include <sys/stat.h>
 int mknod(const  char* path,mode_t mod, dev_t dev);
 int mkfifo(const char *path,mode_t mode);

The first parameter is the full path name of the named pipe created, and the second parameter is the pattern of the named pipe, indicating permissions.
After a famous pipe is created, it exists on the hard disk and must be opened with open(). The process of opening a well-known pipeline may be blocked, and if it is opened in both read and write mode, it will not be blocked. If opened read-only, the process calling open will be blocked until a writer opens the pipe.

/*fiford.c*/
#define FIFO_NAME "myfifo"
#define BUF_SIZE 1024

int main()
{
    int fd;
    char buf[BUF_SIZE];

    umask(0);
    fd = open(FIFO_NAME,O_RDONLY);
    read(fd,buf,BUF_SIZE);
    printf("Read content: %s\n",buf);
    close(fd);
    return 0;
}

/*fifowr.c*/
#define FIFO_NAME "myfifo"
#define BUF_SIZE 1024

int main()
{
    int fd;
    char buf[BUF_SIZE] = "hello fiford,I come from fifowr!";

    umask(0);
    if(mkfifo(FIFO_NAME,S_IFIFO | 0666) == -1)
    {
        perror("mkfifo error!");
        exit(1);
    }

    if((fd = open(FIFO_NAME,O_WRONLY)) == -1)
    {
        perror("open error!");
        exit(1);
    }

    write(fd,buf,strlen(buf)+1);
    close(fd);

    return 0;
}

Message queue

Message queue is a list of messages stored in the kernel. Each message queue is identified by a message queue identifier. Message queues are stored in the kernel and can only be deleted if the kernel restarts or explicitly deletes a message queue.

Creation of message queues:

 #incllude <sys/types.h>
 #include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id); // / Generate unique key values based on pathname and proj_id, and fail to return - 1
#include <sys/msg.h>
int msgget(key_t key,int msgflg); // Create a new message queue or access an existing message queue based on the key value passed from ftok
 Successful return of message queue descriptor, failed return - 1

Read/write message queue

#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflag);

int msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long ing msgtype, int msgflag);

Parameters:
msqid: Message queue descriptor
 msgp: The message read is stored in the structure pointed to by msg
 msgsz: message buffer size  
msgflag: Operational flag. Normally 0, when the message queue is full, the function will block.
msgtype: The type of message requested to be read gets the corresponding message from the message queue according to the type of message
 Successful return 0, failed return - 1
/*Write data to message queues*/
#define BUF_SIZE    256
#define PROJ_ID     32
#define PATH_NAME   "."

int main()
{
    /*User-defined message buffer*/
    struct mymsgbuf
    {
        long msgtype;
        char ctrlstring[BUF_SIZE];
    }msgbuffer;
    int qid;
    int msglen;
    key_t msgkey;

    /*Get the key value*/
    if((msgkey = ftok(PATH_NAME,PROJ_ID)) == -1)
    {
        perror("ftok error!");
        exit(1);
    }
    /*Create message queues*/
    if((qid = msgget(msgkey,IPC_CREAT | 0660)) == -1)
    {
        perror("msgget error!");
        exit(1);
    }

    /*Fill in the message structure and send it to the message queue*/
    msgbuffer.msgtype = 3;
    strcpy(msgbuffer.ctrlstring,"hello,message queue!");

    msglen = sizeof(struct mymsgbuf) - 4;
    if((msgsnd(qid,&msgbuffer,msglen,0) == -1))
    {
        perror("msgsng error!\n");
        exit(1);
    }

    return 0;
}


/*Get data from the message queue you just created*/
#define PATH_NAME   "."
#define PROJ_ID     32
#define BUF_SIZE    256

int main()
{
    key_t key;  //Key value
    int qid;    //queue ID
    int msglen; //Message size

    struct mymsgbuf
    {
        long msgtype; //Message type
        char ctrlstring[BUF_SIZE]; //Message content
    }msgbuffer; //Custom message buffer

    /*Get the key value*/
    if((key = ftok(PATH_NAME,PROJ_ID)) == -1)
    {
        perror("ftok error!\n");
        exit(1);
    }
    /*Access message queue*/
    if((qid = msgget(key,IPC_CREAT | 0660)) == -1)
    {
        perror("msgget error!\n");
        exit(1);
    }
    /*Read message queue content*/
    msglen = sizeof(struct mymsgbuf) - 4;
    if(msgrcv(qid,&msgbuffer,msglen,3,0) == -1)
    {
        perror("msgrcv error!\n");
        exit(1);
    }

    printf("Get message: %s\n",msgbuffer.ctrlstring);
    return 0;
}

Topics: socket shell