[Linux learning notes] 11 - process communication

Posted by kyoru on Thu, 03 Mar 2022 12:42:50 +0100

Primary directory

Secondary directory

Tertiary directory

1. Pipeline

1. Anonymous pipeline

Anonymous pipes communicate with blood related processes

Anonymous pipeline communication steps:
1. A process opens the same file in read-write mode respectively, so there are two file descriptors, one pointing to the file in read mode and the other pointing to the file in write mode
2. The parent process fork creates a child process. Because the child process is created with the parent process as the template, the file of the child process_ FD of struct_ Array is the same as the parent process
3. Let the parent process read and the child process write, then close the write performed by the parent process and keep the read of the parent process open; Close the read of the subprocess and keep the write of the subprocess open

Test 1

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main() {
  5     int pipe_fd[2] = {0};
  6     if (pipe(pipe_fd) < 0) {
  7         perror("pipe");
  8         return 1;
  9     }
 10     printf("%d %d\n", pipe_fd[0], pipe_fd[1]);                                                                                       
 11     return 0;
 12 }
~


Test 2

    1 #include <stdio.h>
    2 #include <unistd.h>
    3 #include <string.h>
    4 #include <sys/types.h>
    5 #include <sys/stat.h>
    6 #include <stdlib.h>
    7                                                                                                                                    
    8 
    9 int main() {
   10     int pipe_fd[2] = {0};
   11     if (pipe(pipe_fd) < 0) {
   12         perror("pipe");
   13         return 1;
   14     }
   15     printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
   16     pid_t id = fork();
   17     if (id < 0) {
   18         perror("fork");
   19     }
   20     if (id == 0) { //write
   21         //child
   22         close(pipe_fd[0]);
W> 23         char *msg = "hello world, i am child process";
   24         int count = 5;
   25         while (count) {
   26             write(pipe_fd[1], msg, strlen(msg));
   27             sleep(1);
   28             count--;
   29         }
   30         close(pipe_fd[1]);
   31         exit(0);
   32     }else {     // read
   33         //parent
   34         close(pipe_fd[1]);
   35         ssize_t size = 0;
   36         char buf[64] = {0};
   37         while(1) {
   38             size = read(pipe_fd[0], buf, sizeof(buf)-1);
   39             if (size > 0) {
   40                 buf[size] = 0;
   41                 printf("parent get message from child#: %s\n", buf);
   42             } else if (size == 0) {
   43                 printf("pipe file close, child quit!\n");
   44                 break;
   45             }
   46         }
   47         close(pipe_fd[0]);
   48         int status = 0;
   49         pid_t ret = waitpid(id, &status, 0);
   50         if (ret == -1) {
   51             perror("waitpid");
   52             return 1;
   53         }
   54         printf("child is waited, the exit code is %d\n", WEXITSTATUS(status));
   55     }
   56     return 0;
   57 }   


Test 3: what happens to the write side when the read side does not read and the read side is turned off?
The system directly kill s the writer, because no one reads, writing is useless work, and the system does not allow any useless work

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


int main() {
    int pipe_fd[2] = {0};
    if (pipe(pipe_fd) < 0) {
        perror("pipe");
        return 1;
    }
    printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
    }
    if (id == 0) { //write
        //child
        close(pipe_fd[0]);
        char *msg = "hello world, i am child process";
        int count = 5;
        while (count) {
            write(pipe_fd[1], msg, strlen(msg));
            sleep(1);
            count--;
        }
        close(pipe_fd[1]);
        exit(0);
    }else {     // read
        //parent
        close(pipe_fd[1]);
        ssize_t size = 0;
        char buf[64] = {0};
        while(1) {
            size = read(pipe_fd[0], buf, sizeof(buf)-1);
            if (size > 0) {
                buf[size] = 0;
                printf("parent get message from child#: %s\n", buf);
            } else if (size == 0) {
                printf("pipe file close, child quit!\n");
                break;
            }else{
                break;
            }
            close(pipe_fd[0]);
        }
        //close(pipe_fd[0]);
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret == -1) {
            perror("waitpid");
            return 1;
        }
        printf("child is waited, the sig code is %d, the exit code is %d\n", (status & 0x7F), WEXITSTATUS(status));
    }
    return 0;
}


The life cycle of the pipeline follows the life cycle of the process, because if a process is closed, the files opened by the process will also be closed

2. Named pipes

Used for communication between two processes that are not related by blood
mkfifo: command for creating a named pipe. mkfifo filename can only create a named pipe file named filename on the command line.
However, when writing data to filename, our data will not be written to the disk, but will be written to the buffer of struct file structure. (ordinary files need to be refreshed to disk for persistent storage)

Test 1: after creating the named pipe file of myfifo
In bash1:

while :; do echo "hello world"; sleep 1; done > myfifo 

In bash2:

cat < myfifo 

You can see that "hello world" is printed in bash2

Test 2: code level named pipeline communication

int mkfifo(const char *filename, mode_t mode)
0 is returned for success and - 1 is returned for failure

Create a servre and a client. The client sends messages and the server receives messages

//server.c
  1 #include "stdio.h"
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8 
  9 #define FIFO "./fifo"
 10 
 11 int main() {
 12     int ret = mkfifo(FIFO, 0644);
 13     if (ret < 0) {
 14         perror("mkfifo");
 15     }
 16     int fd = open(FIFO, O_RDONLY);
 17     if (fd < 0) {
 18         perror("open");
 19         return 1;
 20     }
 21 
 22     char buf[64] = {0};
 23 
 24     while (1) {
 25         ssize_t s = read(fd, buf, sizeof(buf)-1);
 26         if (s > 0) {
 27             buf[s] = 0;
 28             printf("parent get message from client#: %s", buf);                                                                      
 29         }else if(s == 0) {
 30             printf("client quit\n");
 31             break;
 32         }else {
 33             break;
 34         }
 35     }
 36     close(fd);
 37     return 0;
 38 }
//client.c
  1 #include "stdio.h"
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8 
  9 #define FIFO "./fifo"
 10 
 11 int main() {
 12     int fd = open(FIFO, O_WRONLY);
 13     if (fd < 0) {
 14         perror("open");
 15         return 1;
 16     }
 17 
 18     char buf[128];
 19 
 20     while (1) {
 21         printf("please enter# ");
 22         fflush(stdout);
 23         buf[0] = 0;
 24         ssize_t s = read(0, buf, sizeof(buf)-1);                                                                                     
 25         if (s > 0) {
 26             buf[s] = 0;
 27             write(fd, buf, strlen(buf));
 28         }else if(s == 0) {
 29             break;
 30         }else {
 31             break;
 32         }
 33     }
 34     close(fd);
 35     return 0;
 36 }


2. Shared memory

There is no need for read/write interfaces, because they have been mapped to memory
Shared memory: a space applied in physical memory. The OS maps it to their virtual memory through the page tables of two processes (both processes see the same resource), and then returns the address to the user, so that communication can be carried out through this space.
The system will manage all shared memory: first describe and then organize

1. Create shared memory (man shmget)


shmflg can also be followed by permission: 0644, which is separated by |

grep -ER ‘IPC_CREAT '/ usr/include /: View IPC_CREAT
You can see that it is defined in VIM / usr / include / bits / IPC H medium

2. Get the unique key (man get)


As long as the ftok parameters of the two processes are filled in the same, the returned value key obtained is also the same, which can indicate the same shared memory (there is a key field identifying itself in the struct managing shared memory)

3. Delete shared memory (man shmctl)


In * ctl, you can view the structure describing its corresponding resources (shared memory / message queue / semaphore)

The first parameter is the shared memory identification code, and the second parameter is set to IPC_RMID means to delete the shared memory, and the last one is temporarily set to NULL

4. Associate process to shared memory / disassociate shared memory (man shmat)


The second and third parameters are temporarily set to NULL and 0

// Used to continuously view shared memory information
while :; do ipcs -m | head -3 && ipcs -m | grep "xupeng";echo "###########"; sleep 1; done

5. Get the contents of shared memory

(because the address of the virtual memory has been obtained through shmat, it can be printed directly like the contents of the buffer, that is, the array)

6. Write content to shared memory

Shared memory life cycle varies with OS
Shared memory does not provide any synchronization and mutual exclusion and is independent of each other
Shared memory communication is the fastest; There is no operation to copy to the file, and there is no conversion from user state to kernel state
shim is the concept of user layer operation
key is the uniqueness of the system representation

grep -ER ‘struct ipc_perm '/ usr/include /: find ipc_perm

3. Message queue

1. Get message queue (man msgget)


The parameters are the same as shmget

2. Delete message queue (man msgctl)


The prefix of the signal is sem, semget/semctl
The type in the structure when sending a message. You can specify two processes to communicate. Only those with the same sending type and receiving type can communicate (type=0 means that any process can receive the message).

//client.c
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/ipc.h"
#include "sys/msg.h"

#define SIZE 100

struct Buf{
    long type;
    char text[SIZE];
};


int main() {
    key_t key = ftok("/home/xupeng", 10);
    if (key < 0) {
        perror("ftok");
        return 2;
    }

    int msgid = msgget(key, IPC_CREAT | 0644);
    if (msgid < 0) {
        perror("msgid");
        return 2;
    }

    struct Buf bf;
    bzero(&bf, sizeof(bf));
    bf.type = 1;
    bf.text[0] = 0;
    strcpy(bf.text, "hello world");

    msgsnd(msgid, &bf, strlen(bf.text), 1);


    return 0;
}
//server.c
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/ipc.h"
#include "sys/msg.h"

#define SIZE 100

struct Buf{
    long type;
    char text[SIZE];
};


int main() {
    key_t key = ftok("/home/xupeng", 10);
    if (key < 0) {
        perror("ftok");
        return 2;
    }

    int msgid = msgget(key, IPC_CREAT);
    if (msgid < 0) {
        perror("msgid");
        return 2;
    }

    struct Buf bf;

    size_t size = msgrcv(msgid, &bf, SIZE, 1, 0);
    printf("msgrcv return : %d", size);
    bf.text[size] = 0;
    printf("i get meg from client : %s\n", bf.text);
    

    return 0;
}

Topics: Linux git bash