Linux communication - Shared memory solution for building process communication IPC | Implementing server&client communication using shared memory

Shared memory is the fastest form of IPC. Once such memory is mapped into the address space of the process that shares it, these inter-process data transfers no longer involve the kernel, i.e. processes no longer pass each other's data by executing system calls into the kernel.

Table of contents

1. The principle of shared memory

2. Use shared memory

3. Shared memory functions

1.shmget (used to create shared memory)

2.shmat (associate shared memory with process address space)

3.shmctl (used to control shared memory)

4.shmdt (detach the shared memory segment from the current process)

4. Shared memory server&client communication test

①Create shared memory

 ②Associate shared memory and processes

//2-3 Communicate

③Cancel the association between the process and the shared memory

④Close shared memory


1. The principle of shared memory

  • 1. Create a space in physical memory
  • 2. Let different processes map this space into their own process virtual address space through the page table.
  • 3. Different processes operate shared memory by operating virtual addresses in their own process's virtual space.

Processes A and B both map their virtual addresses to physical addresses through their respective page tables. Process A operates the virtual address to write data and saves it in physical memory, and process B reads the physical memory.

2. Use shared memory

Shared memory is in the physical address space and is in the shared area.

  1. Create shared memory
  2. Associated processes - Establish a mapping relationship between the virtual address of the process and the address of the shared memory through the page table
  3. communication
  4. Unassociate the process--by deleting the key-value of the page table in the process
  5. Shared memory release

3. Shared memory functions

The above figure shows that process A and process B can communicate through shared memory, and similarly C and D can also communicate through another shared memory. Therefore, in the system, there must be multiple shared memories at the same time. The OS needs to manage these memory blocks , so a structure is used to store the various attributes of the shared memory .

So shared memory == shared memory kernel data structure + real opened physical space

1.shmget (used to create shared memory)

原型:int shmget(key_t key,size_t size,int shmflg)

参数:key 这个共享内存段名字 size 共享内存大小 shmflg 由九个权限标志构成,用法的mode一样

返回值:成功返回一个非负整数,即共享内存段的标识码,失败返回-1

其中key_t key 是由ftok算法生成的随机值,具有唯一性,ftok函数如下:

key_t  ftok(const char * pathname,int proj_id)

形参:pathname 传入一个地址  proj_id 输入一个数字 这两个值可以任意设置,但是在通信双方必须是相同的,具体地,A进程调用ftok生成key,将key放入struct shm中,B也生成相同的key,B用这个key去struct shm去匹配,匹配成功,就找到了共享内存。

shmflg宏 含义
IPC_CREAT 单独使用时,创建一个共享内存,如果不存在就直接创建,如果存在就获取已有的共享内存起始地址返回
IPC_EXCL 需要依赖IPC_CREAT使用,如果不存在则创建共享内存,如果存在立马退出报错返回

2.shmat(将共享内存和进程地址空间关联)

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

参数: shmid 共享内存标识 shmaddr 指定连接的地址 shmflg它的两个可能取值是SHM_RND 和SHM_RDONLY

返回值:成功返回一个指针,指向共享内存的起始地址,失败返回-1(类似于malloc)

说明:shmaddr为null,os自动选择一个地址,

           shmaddr不为null,且shmflg无SHM_RND标记,则以shmaddr为连接地址

         shmaddr不为null,且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整 shmlba的整数倍

            shmflg = SHM_RDONLY 表示连接操作用来只读共享内存

3.shmctl(用于控制共享内存)

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

Parameters: shmid shared memory identification code returned by shmget

            cmd: The action to be taken (there are three possible values ​​​​as shown in the table below)

           buf: Points to a data structure that holds the mode status and access permissions of shared memory.

Return value: 0 on success, -1 on failure

Order illustrate
IPC_STAT Set the data in the shmid_ds structure to the current associated value of the shared memory
IPC_SET On the premise that the process has sufficient permissions, set the current associated value of the shared memory to the value given in the shmid_ds data structure
IPC_RMID Delete shared memory segment


4.shmdt (detach the shared memory segment from the current process)

Prototype: int shmdt(const void * shmaddr);

Parameters: shmaddr: pointer returned by shmat

Returns 0 on success, -1 on failure

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

4. Shared memory server&client communication test

Steps to use shared memory communication: create shared memory, associate process and shared memory, communicate, cancel association, delete shared memory

 Create 4 files: server.cc, client.cc, common.hpp, makefile. The server implements writing and the client implements reading.

The makefile needs to generate two target files, server and client, and can be written like this:

.PHONY:all
all:server client
client:client.c comm.c
 gcc -o $@ $^
server:server.c comm.c
 gcc -o $@ $^
.PHONY:clean
clean:
 rm -f client server

Common.hpp declares and defines some functions common to the client and server, as follows:

This file mainly implements the creation of shared memory, association and cancellation of processes, and release of shared memory.

①Create shared memory

  1. First, create shared memory using shmget
  2. There is a key, which is generated using ftok and can be called on both ends\
  3. When communicating, one process creates shared memory and another process acquires it.
  4.   When shared memory is created, it is measured in bytes.
//创建共享内存,要知道k和这个共享内存的大小
int createShm(key_t k, int size)
{
    //创建的时候,最好创建全新的
    int shmid = shmget(k,gsize,IPC_CREAT|IPC_EXCL);
    if(shmid == -1)
    {
       //创建失败
        exit(2);
    }
    
    return shmid;
}

//创建成功,一个进程获取
int getShm(key_t,int size)
{
    int shmid = shmget(k,gsize,IPC_CREAT);
    return shmid;
}

From the above two functions, one is created and the other is obtained. The only difference is shmflg, so it can be encapsulated into one function.

//static 只在本文件内有效
static int createShmHelper(int k, int size,int flag)
{
    int shmid = shmget(k,gsize,flag);
    if(shmid == -1)
    {
        exit(2);
    }

    return shmid;
}



int createShm(key_t k,int size)
{
      //创建的时候注意加权限
      umask(0);
      return createShmHelper(key,gsize,IPC_CREAT|IPC_EXCL|0666);
}

int getShm(key_t k,int size)
{
    return createShmHelper(key,gsize,IPC_CREAT);
}
    

 ②Associate shared memory and processes

        After the shared memory is created in physical memory, the shared memory must have permissions when it is created to operate. Associate the shared memory with the process. Put the starting address of the shared memory into the process pcb through page table mapping. You can set it yourself where to attach it to the pcb. Set it to null to let the system choose independently, that is, the association is completed.

char * attachShm(int shmid)
{
    char * start = (char *)shmat(shmid,nullptr,0);
    return start;
}

//2-3 Communicate

Specific communication can be set up by yourself

③Cancel the association between the process and the shared memory

detach(char * start)
{
    int n = shmdt(start);
    (void)n;
}

④Delete shared memory

When running both ends, it is found that after the process exits, shared memory cannot be created when running again, indicating that the shared memory is not closed directly with the process.

There are two ways to turn off shared memory:

  1. ipcrm -m command
  2. function shmctl
void delShm(int shmid)
{
   int n =  shmctl(shmid,IPC_RMID,nullptr);
   assert(n != -1);
   (void)n;
}

Server.cc mainly implements creating shared memory, associating shared memory, communicating (reading data from shared memory), canceling association, and deleting shared memory.

Client.cc mainly implements obtaining shared memory, associating shared memory, communicating (writing data to shared memory), and canceling association.

Next, elegantly modify the above code and encapsulate it.

common.hpp:

//前面的方法不变


#define SERVER 1
#define CLIENT 0

class Init
{

public:
    //构造
    Init(int t):type(t)
    {
        key_t k = getKey();
        if(type == SERVER) 
            shmid = createShm(k, gsize);
        else
             shmid = getShm(k, gsize);

        start = attachShm(shmid);
    }

    char *getStart()
    {     
        return start;
    }

    //析构
    ~Init()
    {
        detachShm(start);
        if(type == SERVER) delShm(shmid);
    }
private:
    char *start;
    int type;     //server or client
    int shmid;
};


server.cc

int main()
{
    Init init(SERVER);
    char * start = init.getStart();
    //开始通信
    ....
    //读取
    int n = 0;
    while(n <= 26)
    {
        cout<<" "<<start<<endl;  //设置start里都是字符串
        sleep(1);
    }

    //因为init是一个临时对象,所以函数跑完会自动调用析构
    return 0;
}

client.cc

int main()
{
    Init init(CLIENT);
    char * start = init.getStart();
    
    //开始通信

    ...
    //往start里写
    char c = 'A';
    while(c <= 'Z')
    {
        start[c-'A'] = c;
        c++;
        start[c] = '\0';
        sleep(1);
    }

    return 0;
}

Guess you like

Origin blog.csdn.net/jolly0514/article/details/132562297