[Linux] Inter-process communication - shared memory

foreword

        We know that in Linux, processes exist independently of each other, and there is no direct way for processes to communicate with each other. But if we can see the same memory between different processes , that is, can we read and write this area, can we achieve inter-process communication?

        It turns out that it is. Before we can use pipelines to achieve communication between processes. (anonymous pipes and named pipes) But it seems that the transmission process is all done through the hands of the operating system ( system calls ). So can the processes have a piece of shared memory under their own jurisdiction? This article is a study note about the System V shared memory method to achieve inter-process communication~

 "But, I met a beautiful angel"

PrevLinux study notes portal!

[Linux] Production and use of static and dynamic libraries - Programmer Sought

Table of contents

1. Understand a shared memory

2. Shared memory related interface

1. ftok creates a unique key

2.shmget gets shared memory

3. shmctl operates shared memory

4.shmat mount shared memory

5.shmdt is associated with shared memory

3. Simple demo 


1. Understand a shared memory

        As the name implies, shared memory is memory that can be shared between processes.

        This memory is a section of memory opened up by the operating system. Through the page table mapping-to the shared area in the process address space, the memory can be shared between different processes. When releasing, remove the mapping first, and then release it.

         In fact, when shared memory is mounted between different processes, ipc (Inter-Process Communication-inter-process communication) is the fastest at this time, because data transmission does not involve the kernel, that is, it does not pass each other's information through system calls. information.

        Shared memory is also applied by the operating system, so the operating system also needs to manage it (described in the organization first), so the composition of shared memory is actually: shared memory = shared memory block + kernel data structure of shared memory . The kernel data structure stores relevant information, such as the number of mounted processes and so on.

        So how do we let the operating system create shared memory and let different processes attach to the same shared memory?

2. Shared memory related interface

1. ftok creates a unique key

        Similar to identifiers. In order to distinguish the names of different shared memory segments, we need a unique shared memory segment name.

(man 3 ftok)

Function prototype:

        key_t ftok(const char *pathname, int proj_id);

Dependency header file:

        #include <sys/types.h>
        #include <sys/ipc.h>

parameter:

        pathname : Given an existing path - must have permissions. (easy to create files)

        proj_id : a number not exceeding 8bit in size, that is, data within one byte (0 ~ 255)

return value:

        key_t : typedef int is similar to the identifier key, which is used to obtain the parameters of shared memory below. If the return value is less than zero, the creation failed.

Notice:

        It may not be 100% successful, you can try a few more times.

2.shmget gets shared memory

        With a unique identifier, we can use this identifier to obtain a piece of shared memory that can be mounted by a process through a system call. Acquisition can be by finding an existing one or recreating it. Generally, in actual use, the server creates and the client obtains.

(man 2 shmget)

Function prototype:

        int shmget(key_t key, size_t size, int shmflg);

Dependency header file:

        #include <sys/ipc.h>
        #include <sys/shm.h>

parameter:

        key : A unique identifier generated by the ftok algorithm function.

        size : The size of the custom shared memory, in bytes. (preferably an integer multiple of the page size (4KB))

        shmflg : Operand. (A total of nine permission flags are used, and the usage is the same as the mode flag used when creating a file)

                If IPC_CREAT         creates a share alone, the bottom layer exists, it will be obtained and returned, and it will be returned if it does not exist. (Create key if key does not exist.)

                IPC_EXCL         alone has no value, and IPC_CREAT is used together to create and return if the underlying layer does not exist. There was an error in the underlying layer and returned. (Fail if key exists.)

                Permission code (permission to file [Linux] permission management_Qihaila's blog-CSDN blog )

                0 : Take the shared memory identifier, if it does not exist, the function will report an error.

                ......

(contain at least IPC_CREAT | permission code )

return value :

        If it succeeds, it will return a legal identifier, which is used to represent the number of this segment of shared memory (similar to fd in file operations, here is shm+id), and if it fails, it will return -1 and set an error code.

Notice:

        The reason why the size of the allocated memory space is an integer multiple of pages is to prevent waste . Although in the monitoring window (ipcs -m you can view the shared memory resource ipcrm -m shmid you can delete the corresponding shared memory-command operation) is as you set, but in fact the operating system rounds up the provided memory, that is, for example, if you want 1 byte of shared memory, in fact, the operating system allocates a whole page of memory, and then the rest of the memory cannot be used, which causes a waste of resources.

3. shmctl operates shared memory

        Usually, we can use this system call to release the shared memory we created.

(man 2 shmctl)

Function prototype:

        int shmctl(int shmid, int cmd, struct shmid_ds *buf);

Dependency header file:

        #include <sys/ipc.h>
        #include <sys/shm.h>

parameter:

        shmid : It is the id returned by the shmget interface to obtain the shared memory, indicating the operation of this segment of shared memory.

        cmd : Operand.

                IPC_RMID         releases this shared memory (whether there is a process and its hook)

                ......

        buf : It is a pointer to shmid_ds strucc‐, in general we don’t need to set it to nullptr.

return value :

        A successful operation returns 0, and a failure returns -1.

Notice:

        Usually, this interface is used to delete the created shared memory at the end of the server end , so there are only two parameters actually used, and only one operand.

4.shmat mount shared memory

        After obtaining the id of the shared memory, it is necessary to mount a connection with the address space of its own process, obtain the corresponding address, and prepare for inter-process communication.

(man 2 shmat)

Function prototype:

        void *shmat(int shmid, const void *shmaddr, int shmflg);

Dependency header file:

        #include <sys/types.h>
        #include <sys/shm.h>

parameter:

        shmid : It is the id returned by the shmget interface to obtain the shared memory, indicating the operation of this segment of shared memory.

        shmaddr : It is generally related to the form of the mount, and generally does not operate, just set it to nullptr.

        shmflg : Operand.

                0: Take the shared memory identifier, if it does not exist, the function will report an error.

                .......

return value :

        Returns the address of this shared memory on a successful mount. If the hook fails, return (void*)-1 and set the error code.

Notice:

        The purpose of this interface is to mount with the current process. You don’t need to know about the form of the mount at the beginner stage. You only need to connect to it, and know that the address of the shared memory returned by the mount is successful, which is the address we operate in peacetime. similar.

5.shmdt is associated with shared memory

        On the client side (the server side does not need to be associated - it will be released in the end) and finally we need to disconnect the connection between the process and the shared memory to use this system interface.

(man 2 shmdt)

Function prototype:

        int shmdt(const void *shmaddr);

Dependency header file:
        #include <sys/types.h>
        #include <sys/shm.h>

parameter:

        shmaddr : Shared memory address. (It can be the shared memory address value returned by the shmat function above)

return value:

        Return 0 if the deassociation succeeds, -1 if it fails, and set the error code.

Notice:

        Shared memory and de-association functions between processes.

3. Simple demo 

        The following simple code demonstrates the creation of shared memory by the simple server, the server and the client mount the shared memory, the server reads the shared memory, and the customer service writes to the shared memory to achieve inter-process communication. Finally, the server and the client associate the shared memory, and the server releases the shared memory, ending. (The shutdown signal can be identified with quit)

        The above requirements can be fulfilled by using the interface introduced above, come and try~

//common.h
#pragma once

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <cstdio>
#include <string>
#include <cstring>

#define PATH_NAME "/home/QiHai/code/2022/TestProject"
#define PROJ_ID 0x69  // 0 ~ 255 非零
#define SHMSIZE 4096  // 页的整数倍(4KB = 4 * 1024byte) - 否则就会造成空间上的浪费
//client.cpp
#include "common.h"
// 服务端
int main()
{
    // 首先生成秘钥
    key_t key = ftok(PATH_NAME, PROJ_ID);
    // std::cout << key << std::endl;

    // 创建共享内存
    umask(0);  // 掩码
    int shmid = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        perror("shmget:");
        exit(1);
    }
    std::cout << "创建共享内存成功!" << std::endl;

    // 挂接到当前进程的共享区
    char* shmarr = (char*)shmat(shmid, nullptr, 0);  // 共享内存id 挂接形式... 操作
    if (*((int*)shmarr) < 0)
    {
        perror("shmat:");
        exit(2);
    }
    std::cout << getpid() << "进程挂接共享内存成功!" << std::endl;

    // 进程通信 服务端进行循环读取,由于是直接访问内存,所以没有阻塞,需要配合管道食用更佳
    while (true)
    {
        char* tmp = shmarr;
        if (strcmp(shmarr, "quit") == 0) break;
        printf("%s\n", tmp);
        sleep(1);
    }

    // 去关联 - 实际上服务端可以不用做这一步,因为服务端最后会将此共享内存给删除
    int s = shmdt(shmarr);
    if (s < 0)
    {
        perror("shmdt:");
        exit(2);
    }
    std::cout << getpid() << "进程去关联成功!" << std::endl;
    // 删除共享内存
    int m = shmctl(shmid, IPC_RMID, nullptr);  // 共享内存id 操作(当前操作是无论有多少个进程挂接上都会删除掉) null
    if (m < 0)
    {
        perror("shmctl:");
        exit(3);
    }
    std::cout << "删除共享内存成功!" << std::endl;

    return 0;
}
//server.cpp
#include "common.h"
// 客户端
int main()
{
    // 首先生成秘钥
    key_t key = ftok(PATH_NAME, PROJ_ID);

    int shmid = shmget(key, SHMSIZE, 0);  // 客户端给0即可
    if (shmid < 0)
    {
        perror("shmget:");
        exit(1);
    }

    // 挂接到当前进程的共享区
    char* shmarr = (char*)shmat(shmid, nullptr, 0);  // 共享内存id 挂接形式... 操作
    if (*((int*)shmarr) < 0)
    {
        perror("shmat:");
        exit(2);
    }
    std::cout << getpid() << "进程挂接共享内存成功!" << std::endl;

    // 进程通信 客户端写即可
    std::string buffer;
    while (true)
    {
        std::getline(std::cin, buffer);  // 等待键盘输入
        snprintf(shmarr, buffer.size() + 1, "%s", buffer.c_str());
        if (buffer == "quit") break;
    }

    // 去关联
    int s = shmdt(shmarr);
    if (s < 0)
    {
        perror("shmdt:");
        exit(2);
    }
    std::cout << getpid() << "进程去关联成功!" << std::endl;
    return 0;
}

operation result:

         The server will continuously read the input data from the client. Of course, it can be controlled that if the client does not input, it will not read in (using the characteristics of the pipeline), then the pipeline operation can be added in the middle.

Guess you like

Origin blog.csdn.net/weixin_61508423/article/details/128265907