[Linux] Shared memory for inter-process communication

In the last blog post, we learned about interprocess communication through pipes, we learned about anonymous pipes and named pipes, and used anonymous pipes and named pipes through coding simulation. We know that in order to complete communication between processes, the two processes must first see the same resource, so given this premise, in this blog post, we understand another way to communicate between processes - shared memory.

Table of contents

1. System V shared memory

1.1 Understanding of the principle of shared memory

Schematic diagram of shared memory

1.2 Shared memory encoding

1.2.1 Shared memory functions

1.2.2 Delete shared memory

1.2.3 Using shared memory to complete inter-process communication

1.2.4 Cli write character Ser get

appendix:

ipcShmCli.cc

ipcShmSer.cc

comm.hpp

Log.hpp

makefile


1. System V shared memory

system V is a set of standards, which are system-level interfaces. We know that the premise of inter-process communication is: let different processes see the same resource first!

1.1 Understanding of the principle of shared memory

First, let's understand the principle of shared memory:

We have known before that a process has its own tast_struct and the address space of the process, so for us, tast_struct has its own address space. We know that there are stack areas, heap areas, data areas, etc. in the process address space... We also know in the dynamic and static libraries that we can add the libraries in the disk to the memory and map them to our process through the page table A shared area in the address space of a process. The shared library is here. And inter-process communication must have at least two processes (as shown in the figure below), then these two processes have nothing to do with each other. Each process has its own page table, and can also be mapped to its own address space through the page table. Assuming that there is a special interface today, first a space can be created in physical memory, and then each process calls this interface to map this space to the address space of its own process through the page table. Then the process can see this space in physical memory through its own address space. Therefore, each process can see the same resource in physical memory. At this point, for us, this mechanism is shared memory.

Schematic diagram of shared memory

1.2 Shared memory encoding

1.2.1 Shared memory functions

  • shmget function

Function: shmget is used to create shared memory

parameter:

  1. key : the name of this shared memory segment
  2. size : The size of the shared memory is recommended to be set to an integer multiple of the page (4KB)
  3. shmflg: Consists of 9 permission flags, their usage is the same as the mode mode representation used when creating files

Return value: successfully returns a non-negative integer, which is the identification code of the shared memory segment; fails and returns -1

IPC_CREAT: This parameter is the same as the named pipe. If it exists, it will be obtained without creating it, and if it does not exist, it will be created.

IPC_EXCL: To be used together with IPC_CREAT (bitwise or |), if the specified shared memory does not exist, it will be created; if the specified shared memory exists, an error will be returned. It can be guaranteed that if the shmget function call is successful, it must be a brand new shared memory shared memory!

This shared memory is the structure that the kernel that exists in the kernel will give us to maintain the shared memory! Shared memory is also managed by the kernel, first described and then organized. The following is the kernel data structure of shared promotion:

struct shmid_ds {
 struct ipc_perm shm_perm; /* operation perms */
 int shm_segsz; /* size of segment (bytes) */
 __kernel_time_t shm_atime; /* last attach time */
 __kernel_time_t shm_dtime; /* last detach time */
 __kernel_time_t shm_ctime; /* last change time */
 __kernel_ipc_pid_t shm_cpid; /* pid of creator */
 __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
 unsigned short shm_nattch; /* no. of current attaches */
 unsigned short shm_unused; /* compatibility */
 void *shm_unused2; /* ditto - used by DIPC */
 void *shm_unused3; /* unused */
};

So how do I know that shared memory exists or does not exist?

We know above that shared memory should be managed and reorganized by the kernel -> struct shmid_ds{} -> struct ipc_perm -> key(shmget) The unique value of shared memory! And through this key we can represent this shared memory. Therefore, if a shared memory exists, there is a key value. As long as we get the key value, is it equivalent to getting the shared memory? So for better control, this key value is provided by our users. We can calibrate this shared memory through the key value. The way to let different processes see the same shared memory in the kernel is to let them have the same key value.

We generally use ftok to generate this key.

Let's use it to see the effect:

comm.hpp

#pragma once

#include <iostream>
#include <cstdio>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

using namespace std;

#define PATH_NAME "/home/Lxy/code"
#define PROJ_ID 0x14
#define MEM_SIZE 4096



key_t CreatKey()
{
    key_t key = ftok(PATH_NAME,PROJ_ID);
    if(key < 0 )
    {
        cerr<< "ftok:" << strerror(errno) <<endl;
        exit(1);
    }

    return key;
}


IpcShmCli.cc

#include "comm.hpp"
#include "Log.hpp"

int main()
{
    key_t key = CreatKey();
    cout<<"key :" <<key << endl;
    return 0;
}


IpcShmSer.cc

#include "comm.hpp"
#include "Log.hpp"

//充当创建共享内存的角色
int main()
{
    key_t key = CreatKey();
    Log() << "key " << key <<endl;

    //创建一个全新的共享内存
    int shmid = shmget(key,MEM_SIZE,IPC_CREAT|IPC_EXCL);
    if(shmid < 0)
    {
        Log()<<"shmget:" << strerror(errno) << endl;
        return 2;
    }

    Log() << "creat shm success ,shmid: " << shmid << endl;

    return 0;
}


Log.hpp

#pragma once

#include <iostream>
#include <ctime>

std::ostream &Log()
{
    std::cout << "For Debug | " << "timestamp: " << (uint64_t)time(nullptr) << " ";
    return std::cout;
}


makefile

.PHONY:all
all: ipcShmCli ipcShmSer

ipcShmCli:IpcShmCli.cc
	g++ -Wall -o $@ $^ -std=c++11

ipcShmSer:IpcShmSer.cc
	g++ -Wall -o $@ $^ -std=c++11
	
.PHONY:clean
clean:
	rm -f ipcShmCli ipcShmSer

When we run ipcShmSer for the second time, the shared memory already exists at this time, so IPC_EXCL will fail and return. We check that the exit code is 2, so the life cycle of shared memory under system V depends on the kernel! Moreover, if the shared memory does not show deletion, it can only be deleted after the OS is restarted. So next, we need to learn a delete interface for shared memory

1.2.2 Delete shared memory

1. Use commands to delete

To delete shared memory, first we need to check whether the shared memory exists, we can enter on the command line

ipcs -m

When we view all the shared memory of our current system, we can delete the shared memory of the specified shmid by entering the following command on the command line

ipcrm -m shmid

At this time, after we delete the shared memory, we can create a new shared memory again by running ipcShmSer

2. Use the system interface to delete

  • shmctl

Function: Used to control shared memory

parameter:

  1. shmid : shared memory identifier returned by shmget
  2. cmd: the action to be taken (there are three possible values) where IPC_RMID is to delete the shared memory
  3. buf : points to a data structure that holds the mode state and access rights of the shared memory

Return value: success returns 0; failure returns -1

Not much to say, we directly use it in the ipcShmSer.cc we just wrote

#include "comm.hpp"
#include "Log.hpp"

//充当创建共享内存的角色
int main()
{
    key_t key = CreatKey();
    Log() << "key " << key <<endl;


    Log() << "create share memory begin! \n";
    sleep(5);
    //创建一个全新的共享内存
    int shmid = shmget(key,MEM_SIZE,IPC_CREAT|IPC_EXCL);
    if(shmid < 0)
    {
        Log()<<"shmget:" << strerror(errno) << endl;
        return 2;
    }

    Log() << "creat shm success ,shmid: " << shmid << endl;


    //使用
    sleep(5);


    //删除
    shmctl(shmid,IPC_RMID,nullptr);
    Log()<<"delete shm : " << shmid << "sucess \n";
    sleep(5);
    return 0;
}

In order to be able to feel the whole process of shared memory from creation to deletion subjectively, we write a monitoring script to check it every 1 second

while :; do ipcs -m; sleep 1; done

1.2.3 Using shared memory to complete inter-process communication

In the above preparatory work, we have completed the principle of shared memory, the creation and deletion of shared memory, and the most important thing is to use shared memory to complete inter-process communication between different processes. At this time After the shared memory is created in memory, we need to attach the shared memory in physical memory to the address space of the process. Here you need to use another function

  • shmat

Function: Connect shared memory to process address space

parameter:

  1. shmid : shared memory identifier
  2. shmaddr: Specifies the address to connect to
  3. shmflg: Its two possible values ​​are SHM_RND and SHM_RDONLY

Return value: Return a pointer to the first section of the shared memory if successful; return -1 if failed

Then we use directly in the code:

//使用
    //1.将共享内存挂接到进程地址空间内
    char * str = (char*)shmat(shmid,nullptr,0);
    Log()<< " attach shm : " << shmid << "sucess !\n";

So if we don't want to use it after hooking up, we want to associate, we can use the following function

  • shmdt function

Function: Separate the shared memory segment from the current process

parameter:

  1. shmaddr: pointer returned by shmat

Return value: success returns 0; failure returns -1

Note: Detaching the shared memory segment from the current process does not mean deleting the shared memory segment

We directly reflect in the code

//使用
    //1.将共享内存挂接到进程地址空间内
    char * str = (char*)shmat(shmid,nullptr,0);
    Log()<< " attach shm : " << shmid << "sucess !\n";
    
    //2.不想用就去关联
    shmdt(str);
    Log()<< " detach shm : " << shmid << "sucess !\n";
    sleep(5);

Now let's write ipcShmCli.cc again

#include "comm.hpp"
#include "Log.hpp"

int main()
{
    //创建相同的Key值
    key_t key = CreatKey();
    Log() <<"key :" <<key << endl;

    //获取 shmid 
    int shmid = shmget(key,MEM_SIZE,IPC_CREAT);
    if(shmid < 0)
    {
        Log()<<"shmget:" << strerror(errno) << endl;
        return 2;
    }

    //获取成功之后挂接
    char* str = (char*)shmat(shmid,nullptr,0);

    //用它  
    sleep(5);


    //去关联
    shmdt(str);

    //不用删除共享内存 共享内存的创建和销毁全权由Ser管理
    return 0;
}

When we attach Cli and Ser to a shared memory at the same time, it is guaranteed that the two processes have seen the same resource at the same time. Next, we will write the operation of Cli. It should be noted here that we have mapped the shared memory to our process address space (between the stacks), so for each process, the shared memory attached to its own context belongs to its own space. Similar to heap space or stack space, so it can be used directly by users without calling any system interface!

1.2.4 Cli write character Ser get

After we have established the shared memory and attached to two processes at the same time, we can communicate between processes. We can first complete a relatively simple communication. We let Cli write 26 English characters into the shared memory, one for each second, and let Ser read the contents of the shared memory every second. In this way, when we start the program at the same time, what we should see is that Ser prints English characters (from A to Z) on the screen every second, and one more character and a new line every second. Let's run it and see the result:

   //用它  
    //没有使用任何的系统调用接口
    int cnt = 0;
    while(cnt <= 26)
    {
        str[cnt] = 'A' + cnt;
        ++cnt;
        str[cnt] = '\0';
        sleep(1);
    }

   //2.使用
    while(true)
    {
        printf("%s\n",str);
        sleep(1);
    }

operation result:

Therefore, because of its own characteristics, shared memory does not have any access control! The shared memory is directly seen by both parties of the process and can communicate directly. It belongs to both users, but it is not safe! Therefore, when the shared memory is attached to two processes, when process 1 writes content to the shared memory, process 2 can see it immediately. So shared memory is the fastest of all inter-process communication!

After completing the above functions, we know that when writing characters in Cli, Ser can get them immediately. Therefore, we only need to slightly modify the code of Cli to transmit our customized information. Let's take a look at the implementation of the code.

#include "comm.hpp"
#include "Log.hpp"

int main()
{
    //创建相同的Key值
    key_t key = CreatKey();
    Log() <<"key :" <<key << endl;

    //获取 shmid 
    int shmid = shmget(key,MEM_SIZE,IPC_CREAT);
    if(shmid < 0)
    {
        Log()<<"shmget:" << strerror(errno) << endl;
        return 2;
    }

    //获取成功之后挂接
    char* str = (char*)shmat(shmid,nullptr,0);

    //自定义输入
    while(true)
    {
        printf("Please Enter# ");
        fflush(stdout);
        ssize_t s = read(0,str,MEM_SIZE);
        if(s > 0)
        {
            str[s] = '\0';
        }
    }



    //去关联
    shmdt(str);

    //不用删除共享内存 共享内存的创建和销毁全权由Ser管理
    return 0;
}

This communication method is shared memory!

(End of this article)

appendix:

ipcShmCli.cc

#include "comm.hpp"
#include "Log.hpp"

int main()
{
    //创建相同的Key值
    key_t key = CreatKey();
    Log() <<"key :" <<key << endl;

    //获取 shmid 
    int shmid = shmget(key,MEM_SIZE,IPC_CREAT);
    if(shmid < 0)
    {
        Log()<<"shmget:" << strerror(errno) << endl;
        return 2;
    }

    //获取成功之后挂接
    char* str = (char*)shmat(shmid,nullptr,0);

    while(true)
    {
        printf("Please Enter# ");
        fflush(stdout);
        ssize_t s = read(0,str,MEM_SIZE);
        if(s > 0)
        {
            str[s] = '\0';
        }
    }

    //用它  
    //没有使用任何的系统调用接口
    // int cnt = 0;
    // while(cnt <= 26)
    // {
    //     str[cnt] = 'A' + cnt;
    //     ++cnt;
    //     str[cnt] = '\0';
    //     sleep(1);
    // }


    //去关联
    shmdt(str);

    //不用删除共享内存 共享内存的创建和销毁全权由Ser管理
    return 0;
}

ipcShmSer.cc

#include "comm.hpp"
#include "Log.hpp"

//充当创建共享内存的角色
int main()
{
    key_t key = CreatKey();
    Log() << "key " << key <<endl;


    Log() << "create share memory begin! \n";
    //sleep(5);
    //创建一个全新的共享内存
    int shmid = shmget(key,MEM_SIZE,IPC_CREAT|IPC_EXCL | 0666);
    if(shmid < 0)
    {
        Log()<<"shmget:" << strerror(errno) << endl;
        return 2;
    }

    Log() << "creat shm success ,shmid: " << shmid << endl;

    sleep(2);
    //使用
    //1.将共享内存挂接到进程地址空间内
    char * str = (char*)shmat(shmid,nullptr,0);
    Log()<< " attach shm : " << shmid << "sucess !\n";
    //sleep(2);

    //2.使用
    while(true)
    {
        //有数据了再读
        printf("%s\n",str);
        //sleep(1);
    }
    
    //2.不想用就去关联
    shmdt(str);
    Log()<< " detach shm : " << shmid << "sucess !\n";

    
    sleep(2);


    //删除
    shmctl(shmid,IPC_RMID,nullptr);
    Log()<<"delete shm : " << shmid << "sucess \n";
    //sleep(5);
    return 0;
}

comm.hpp

#pragma once

#include <iostream>
#include <cstdio>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

using namespace std;

#define PATH_NAME "/home/Lxy/code"
#define PROJ_ID 0x14
#define MEM_SIZE 4096



key_t CreatKey()
{
    key_t key = ftok(PATH_NAME,PROJ_ID);
    if(key < 0 )
    {
        cerr<< "ftok:" << strerror(errno) <<endl;
        exit(1);
    }

    return key;
}

Log.hpp

#pragma once

#include <iostream>
#include <ctime>

std::ostream &Log()
{
    std::cout << "For Debug | " << "timestamp: " << (uint64_t)time(nullptr) << " ";
    return std::cout;
}

makefile

.PHONY:all
all: ipcShmCli ipcShmSer

ipcShmCli:IpcShmCli.cc
	g++ -Wall -o $@ $^ -std=c++11

ipcShmSer:IpcShmSer.cc
	g++ -Wall -o $@ $^ -std=c++11
	
.PHONY:clean
clean:
	rm -f ipcShmCli ipcShmSer

Guess you like

Origin blog.csdn.net/qq_58325487/article/details/128031836