[003 Operating System] What are the inter-process communication methods? What are the pros and cons?

1. Inter-process communication methods and their advantages and disadvantages

1. Anonymous pipeline Pipe

Principle: Based on the buffer provided by the operating system kernel, it implements inter-process communication by connecting the output of one process to the input of another process. When a process writes data to a pipe, the data will be stored in the pipe's buffer, waiting for another process to read data from the pipe. The writing process will not block before reading, unless the buffer is full. When another process reads data from the pipe, the data is read from the buffer and passed to the process, and when no data comes, it blocks and waits (hence synchronous communication) . (Summary: half-duplex communication, one-way data flow; can only be used between processes with kinship)

Advantages: simple and convenient;

Disadvantages: Limited to one-way communication; can only be created between its process and its related processes; limited buffer area.

2. Named pipe FIFO

Principle: The principle of named pipes is similar to anonymous pipes. It also implements inter-process communication based on the buffer provided by the operating system kernel. When a process writes data to a named pipe, the data will be stored in the buffer of the named pipe, waiting for another process to read data from the named pipe. The writing process will not block before reading, unless the buffer Full. When another process reads data from the named pipe, the data is read from the buffer and passed to the process. When no data comes, it blocks and waits (hence synchronous communication).

Advantages: Communication between processes of any relationship can be realized;

Disadvantages: It exists in the system for a long time and is error-prone if used improperly; the buffer is limited.

3. Message queue  message queue

Principle: 1. The message queue is a first-in-first-out queue-type data structure. It is actually an internal linked list in the system kernel. Messages are inserted into the queue sequentially, where the sending process adds the message to the end of the queue and the receiving process reads the message from the head of the queue.
2. Multiple processes can send messages to a message queue at the same time, and can also receive messages from a message queue at the same time. The sending process sends the message to the end of the queue, and the receiving process reads the message from the head of the message queue. Once the message is read, it is deleted from the queue.

Advantages: Communication between any processes can be realized, and synchronization between message sending and receiving can be achieved through system call functions. There is no need to consider synchronization issues, which is convenient;

Disadvantages: Copying information requires additional CPU time and is not suitable for situations with large amounts of information or frequent operations.

4. Shared memory shared memory

Principle: Shared memory is to map a section 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 (inter-process communication) method, which is specially designed for the low efficiency of other inter-process communication methods.

Advantages: no need to copy, fast, large amount of information.

Disadvantages: Shared memory only provides a mechanism for data sharing, but does not provide a mechanism for synchronization and mutual exclusion.

5, semaphore semophore

Principle: A semaphore is usually an integer variable used to record the available amount of a shared resource. It can be accessed and modified by multiple threads or processes at the same time. (The semaphore is essentially a counter)

Advantages: can synchronize processes;

Disadvantages: It does not support message passing, it can only do synchronous mutual exclusion, and its functions are limited.

6. Signal signal

Principle: Signals are a relatively complex communication method used to notify the receiving process that an event has occurred. Mainly used as a means of synchronization between processes and between different threads of the same process. For Linux, the actual signal is a soft interrupt, and many important programs need to handle signals. For example, if the end user enters ctrl+c to interrupt the program, a program will be stopped through the signal mechanism.

  • The process sends signals: signals are sent to the target process through kill, raise, alarm and other functions.

  • The process receives signals: register signal processing functions through functions such as signal and sigaction.

  • When a signal comes, the processing function is called, and corresponding processing is done according to the signal in the function.

Advantages: Different signals can convey different information. Relatively flexible communication can be achieved by defining corresponding processing functions for each signal.

Disadvantages: Less information is transmitted.

7. socket socket

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

Advantages: 1) The transmission data is byte level, the transmission data can be customized, the data volume is small and the efficiency is high; 2) The data transmission time is short and the performance is high; 3) Suitable for real-time information interaction between the client and the server; 4) It can be encrypted and has strong data security.
Disadvantages: 1) The transmitted data needs to be parsed and converted into application-level data.

Inter-process communication methods and their respective advantages and disadvantages_Analysis and summary of the differences and advantages and disadvantages of the four process communication methods_Echo_Anna's Blog-CSDN Blog

https://www.cnblogs.com/jiangknownet/p/14516630.html (summary is quite good)


Second, the choice of inter-process communication method

PIPE and FIFO (named pipe) are used to send very short, high-frequency messages to each other between processes. These two methods are usually suitable for communication between two processes.

Shared memory is used to realize very large data shared between processes and with high frequency of read and write operations.

Others consider using socket. Mainly used in distributed development.


3. What are the synchronization methods between threads?

  • Atomic operations
  • spin lock
  • Read and write spin lock
  • Sequential lock (seqlock, only included in 2.6 kernel and later versions)
  • Signal amount
  • Read and write semaphores
  • mutex
  • Big Kernel Lock (BKL, Big Kernel Lock, only included in the 2.4 kernel, not discussed)
  • Big reader lock (brlock, only included in the 2.4 kernel, not discussed)
  • RCU (optimization for read-write locks, only included in 2.6 kernel and later versions)

【005 Basic knowledge】Linux synchronization mechanism? _Kashine's Blog-CSDN Blog


4. Detailed explanation of inter-process communication methods

1. Pipeline

  • Call the fork() function to create a new child process. The fork() function will return twice, once in the parent process to return the process ID of the child process, and once in the child process to return 0.
  • In the parent process, call the sleep(3) function to let the child process execute first, output a prompt message, close the reading end of the pipe, and then call write(fd[1], "hello from father", strlen("hello from father" )) function writes data to the write end of the pipe.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
 
/*使用匿名管道实现进程间通信*/
int main()
{
        int fd[2];//fd[0]为读端 fd[1]为写端
        pid_t pid;
        char buf[128];
        //int pipe(int pipefd[2]);
        if(pipe(fd) == -1)//创建管道
        {
                printf("管道创建失败\n");
                perror("why"); // 打印错误信息
        }
 
        pid = fork(); // 创建子进程
 
        if(pid < 0 )
        {
                printf("子进程开辟失败\n");
                perror("why"); // 打印错误信息
        }else if(pid > 0){
 
                sleep(3);//让子进程先执行
                printf("这是一个父进程\n");//父进程完成写操作
                close(fd[0]); // 关闭读端
                write(fd[1],"hello from father",strlen("hello from father")); // 往写端写入数据
        }else{
 
                printf("这是一个子进程\n");//子进程完成读操作
                close(fd[1]); // 关闭写端
                read(fd[0],buf,sizeof(buf)); // 从管道读取数据,并存储到buf数组中
                printf("buf = %s\n",buf); // 输出读取的数据
        }
 
        return 0;
}

2. Named pipe FIFO

First create a named pipe:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
//       int mkfifo(const char *pathname, mode_t mode);
 
int main()
{
        if(mkfifo("myfifo",0600) == -1 && errno != EEXIST)
        {
                printf("mkfifo failed\n");
                perror("why");
        }
 
        return 0;
}

Read the pipe in read.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
 
//       int mkfifo(const char *pathname, mode_t mode);
 
int main()
{
        int nread;
        char buf[30] = {'\0'};
 
        if(mkfifo("myfifo",0600) == -1 && errno != EEXIST)//创建命名管道
        {
                printf("mkfifo failed\n");
                perror("why");
        }
 
        int fd = open("./myfifo",O_RDONLY);//以只读的形式打开管道,程序阻塞在这,直到有另一个进程对其执行写操作
        if(fd < 0)
        {
                printf("read open failed\n");
        }else
        {
                printf("read open successn\n");
        }
 
        while(1)
        {
                nread = read(fd,buf,sizeof(buf));
                printf("read %d byte,context is:%s\n",nread,buf);
        }
 
        close(fd);
 
        return 0;
}

Write to the pipe in write.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
//       int mkfifo(const char *pathname, mode_t mode);
 
int main()
{
        int nread;
        char buf[30] = "message from myfifo";
 
        if(mkfifo("myfifo",0600) == -1 && errno != EEXIST)//创建命名管道
        {
                printf("mkfifo failed\n");
                perror("why");
        }
 
        int fd = open("./myfifo",O_WRONLY);//打开管道,程序阻塞在这,直到其他进程为读而打开它
        if(fd < 0)
        {
                printf("write open failed\n");
        }
        else
        {
                printf("write open success\n");
        }
 
        while(1)
        {
                sleep(1);
                write(fd,buf,strlen(buf));
        }
        close(fd);
 
        return 0;
}
  1. The mkfifo() function is used to create a named pipe, and the parameters are the pipe path and permissions. If the pipe already exists, return -1 and set errno to EEXIST.

  2. The read process opens the pipe for read-only (O_RDONLY), and the write process opens the pipe for write-only (O_WRONLY).

  3. When the pipe is opened, the reading process will block in the read call, and the writing process will block in the write call.

  4. Once both processes have opened the pipe, the reading process's read will return the written data, and the writing process's write will also return successfully.

3. Message queue:

The message queue is a linked list of messages stored in the kernel. A message queue is identified by an identifier (queue ID).

User processes can add messages to the message queue and read messages from the message queue.

// Create or open a message queue: return the queue ID on success, -1 on failure
int msgget(key_t key, int flag);
// Add a message: return 0 on success, -1 on failure
int msgsnd(int msqid, const void *ptr , size_t size, int flag);
// Read the message: Return the length of the message data successfully, or -1 if failed
int msgrcv(int msqid, void *ptr, size_t size, long type, int flag); //
Control the message queue : Return 0 on success, -1 on failure
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgSend.c file:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//       int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 
//       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
 
 
int main()
{
        struct msgbuf sendbuf={888,"message from send"};
        struct msgbuf readbuf;
 
        key_t key;
 
        if((key = ftok(".",'z')) < 0){
                printf("ftok error\n");
        }
        int msgId = msgget(key,IPC_CREAT|0777);
 
        if(msgId == -1){
                printf("get quen failed\n");
        }
 
        msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
        printf("send over\n");
 
        msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),999,0);
        printf("read from get is:%s\n",readbuf.mtext);
 
        msgctl(msgId,IPC_RMID,NULL);
 
        return 0;
}

msgGet.c file:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//       int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 
//       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
 
int main()
{
        struct msgbuf readbuf;
        memset(readbuf.mtext, '\0', sizeof(readbuf.mtext));
        struct msgbuf sendbuf={999,"thank for your reach"};
 
        key_t key;
 
        //获取key值
        if((key = ftok(".",'z')) < 0){
                printf("ftok error\n");
        }
 
        int msgId = msgget(key,IPC_CREAT|0777);
 
        if(msgId == -1){
                printf("get quen failed\n");
                perror("why");
        }
 
        msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),888,0);
        printf("read from send is:%s\n",readbuf.mtext);
 
        msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
 
        msgctl(msgId,IPC_RMID,NULL);
 
        return 0;
}

msgsnd() is used to send a message to the specified message queue, which contains the following parameters:

  • msgId: The message queue ID to be sent.

  • &sendbuf: Pointer to the send message buffer, the message structure is represented by mtext.

  • strlen(sendbuf.mtext): The length of the data to be sent.

  • 0: Send flag, usually 0.

msgrcv() is used to receive messages from the specified message queue, which contains the following parameters:

  • msgId: Queue ID to receive messages.

  • &readbuf: A pointer to the receive message buffer, and the message structure is represented by mtext.

  • sizeof(readbuf.mtext): Specifies the maximum length of the message received this time.

  • 999: Received message type, 999 means to receive any type of message.

  • 0: Receive flag, usually 0.

4. Shared memory

Shared memory means that two or more processes share a given memory area.

// Create or obtain a shared memory: return the shared memory ID successfully, return -1 on failure
int shmget(key_t key, size_t size, int flag);
// Connect the shared memory to the address space of the current process: return pointing to the shared memory successfully Pointer, returns -1 on failure
void *shmat(int shm_id, const void *addr, int flag);
// Disconnect from shared memory: returns 0 on success, -1 on failure
int shmdt(void *addr); 
// Information related to controlling shared memory: return 0 if successful, return -1 if failed
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

For details, see: https://www.cnblogs.com/52php/p/5861372.html

shmwrite.c file:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
//       int shmget(key_t key, size_t size, int shmflg);
// void *shmat(int shmid, const void *shmaddr, int shmflg);
 
//       int shmdt(const void *shmaddr);
 
int main()
{
        int shmId;
        key_t key;
        char *shmaddr;
 
        if((key = ftok(".",1)) < 0){
                printf("ftok error\n");
        }
 
        shmId = shmget(key, 1024*4, IPC_CREAT|0666);//内存大小必须得是MB的整数倍
 
        if(shmId == -1){
                printf("shmget error\n");
                exit(-1);
        }
 
        /*第二个参数一般写0,让linux内核自动分配空间,第三个参数也一般写0,表示可读可写*/
        shmaddr = shmat(shmId, 0, 0);
        printf("shmat OK\n");
        strcpy(shmaddr,"I am so cool");
 
        sleep(5);//等待5秒,让别的进程去读
 
        shmdt(shmaddr);
        shmctl(shmId, IPC_RMID, 0);//写0表示不关心
        printf("quit\n");
 
        return 0;
}

shmread.c file:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
//       int shmget(key_t key, size_t size, int shmflg);
// void *shmat(int shmid, const void *shmaddr, int shmflg);
 
//       int shmdt(const void *shmaddr);
 
int main()
{
        int shmId;
        key_t key;
        char *shmaddr;
 
        if((key = ftok(".",1)) < 0){
                printf("ftok error\n");
        }
 
        shmId = shmget(key, 1024*4, 0);//内存大小必须得是MB的整数倍
 
        if(shmId == -1){
                printf("shmget error\n");
                exit(-1);
        }
 
        /*第二个参数一般写0,让linux内核自动分配空间,第三个参数也一般写0,表示可读可写*/
        shmaddr = shmat(shmId, 0, 0);
        printf("shmat OK\n");
        printf("data : %s\n",shmaddr);
 
        shmdt(shmaddr);
 
        return 0;
}
if((key = ftok(".",1)) < 0){
    printf("ftok error\n");
}

It can be seen that the number 1 is used as a parameter in both programs ., which ensures that the key values ​​generated by them are the same. This ensures that the same memory is being operated on.


5. Reference content

Six Common Ways of Inter - Process Communication_The Most Common Ways of Inter-Process Communication

Inter-process communication methods and their respective advantages and disadvantages_Analysis and summary of the differences and advantages and disadvantages of the four process communication methods_Echo_Anna's Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/qq_41709234/article/details/132007328