[Linux] Seven, inter-process communication (2)

Table of contents

3. system V (IPC)

3.1 system V shared memory

3.1.1 The concept of shared memory

3.1.2 The principle of shared memory

3.1.3 Create shared memory (shmget)

3.1.4 ftok function

3.1.5 View shared memory resources

3.1.6 Create shared memory test code

3.1.7 Understanding shared memory again

3.1.8 Release shared memory (shmctl) 

3.1.9 Associated shared memory (shmat)  

3.1.10 Disassociate shared memory (shmdt)   

3.1.11 Use shared memory to realize serve&client communication

3.1.12 Advantages and disadvantages of shared memory 

3.1.12 Kernel Data Structures for Shared Memory

3.2 System V message queue (understand) 

3.3 System V semaphore (understand)


3. system V (IPC)

system V is used for local communication , the essence of communication is to let different processes see the same resource

System V IPC provides the following three communication methods:

  1. system V shared memory
  2. system V message queue
  3. system V semaphore

3.1 system V shared memory

3.1.1 The concept of shared memory

Let different processes see the same memory block, this memory block is called shared memory, and the shared memory area is the fastest form of IPC

3.1.2 The principle of shared memory

        The way shared memory allows different processes to see the same resource is to apply for a memory space in the physical memory, and then map this memory space with the page tables of each process, and then open up space in the virtual address space And fill the virtual address into the corresponding position of their respective page tables, so that the corresponding relationship between the virtual address and the physical address is established, so that these processes can see the same physical memory, this physical memory is called shared memory, shared memory application is good After that, the starting address of the shared memory will be returned to the user

The future does not want to communicate (i.e. close communication):

  1. De-association: cancel the mapping relationship between the process and the memory
  2. free shared memory

Understanding of shared memory:

  1. Shared memory is specially designed for IPC. It is completely different from the memory obtained by malloc and new. This shared memory can be seen by all processes, while the memory obtained by malloc and new is only open to its own process. Private, other processes cannot see this memory
  2. Shared memory is a form of communication that can be used by all processes that want to communicate
  3. There must be a large amount of shared memory in the OS at the same time

3.1.3 Create shared memory (shmget)

The establishment of shared memory roughly includes the following two processes:

  1. Apply for shared memory space in physical memory
  2. Attach the requested shared memory to the address space, that is, establish a mapping relationship

Create shared memory needs to use the shmget function (system call)

man 2 shmget Check it out

 explain:

函数:shmget
shm: shared memory

头文件:
 #include <sys/ipc.h>
 #include <sys/shm.h>

函数声明:
int shmget(key_t key, size_t size, int shmflg);

参数:
    第一个参数key: 这个共享内存段名字,表示待创建共享内存在系统当中的唯一标识
    第二个参数size: 共享内存大小
    第三个参数shmflg: 表示创建共享内存的方式,用法和创建文件时使用的mode模式标志是一样的

返回值:
成功,返回一个有效的共享内存标识符(用户层标识符)
失败,返回-1,错误码被设置

 Options available for the third parameter shmflg:

There are only two commonly used options: IPC_CREAT and IPC_CREAT

  1. IPC_CREAT: Create a new shared memory, if the shared memory already exists, get the existing shared memory and return it
  2. IPC_CREAT: Cannot be used alone, usually used together with IPC_CREAT, that is (IPC_CREAT | IPC_CREAT), if the shared memory already exists, an error will be reported, that is to say, if the shared memory is created successfully, the shared memory must be newly created

The first parameter key: indicates the uniqueness of shared memory, which is very important

How to get the key? To use the function ftok

3.1.4 ftok function

The ftok function is used to obtain keuy

Man 3 ftok Check it out:

explain:

函数:ftok

头文件:
 #include <sys/types.h>
 #include <sys/ipc.h>

函数声明:
key_t ftok(const char *pathname, int proj_id);

参数:
    第一个参数pathname: 路径名
    第一个参数proj_id: 一个整数标识符
注:这两个参数都可以随便给

返回值:
成功key被返回
失败返回-1,错误码被设置

3.1.5 View shared memory resources

In Linux, we can use the ipcs command to view related resource information about inter-process communication. When the ipcs command is used alone, the message queue, shared memory and semaphore-related information will be listed by default.

If you only want to view the relevant information of one of them, you can choose to carry the following options:

  • q: List message queue related information
  • -m: List shared memory related information
  • -s: List semaphore related information

 For example, to view shared memory: ipcs -m

The meaning of each column of information output by the ipcs command is as follows: 

3.1.6 Create shared memory test code

code show as below:

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

using namespace std;

#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << errno << ":" << strerror(errno) << endl;
    }

    //打印key值
    printf("key: %x\n", k);

    //创建共享内存
    int shm = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL);
    if(shm < 0)//创建失败
    {
        cerr << errno << ":" << strerror(errno) << endl;
        exit(-1);
    }

    //打印shm
     printf("shm: %d\n", shm);

    return 0;
}

operation result

ipcs -m check 

run again

  

It can be seen from here that the shared memory has not been released after the program ends, indicating that the declaration cycle of the shared memory follows the OS and does not end with the end of the process.

Now that the shared memory is created, it also needs to be released (de-associated), as follows

3.1.7 Understanding shared memory again

Shared memory is not ordinary memory (that is, it is not malloc or new), shared memory has its own attributes, and OS also has a lot of shared memory, all of which need to be managed by OS

How to manage shared memory? Describe first, organize later

Shared memory = physical memory block + related attributes of shared memory

When creating shared memory, how to ensure the uniqueness of shared memory? ? by key

When communicating, two processes also need to see the same shared memory. How to ensure that another process also sees the same shared memory? ? Only by letting another process see the same key can guarantee that two processes can see the same shared memory

The key is to be set into the property of the shared memory, which is used to represent the unique shared memory

shmget successfully creates a shared memory and returns a valid shared memory identifier. Let's call it shmid here. What is the difference between shimid and key? ?

shimid and key are a bit like fd and inode

shmid -> fd
key   -> inode

Files are identified by inode in the kernel, and the upper layer does not use inode, but uses fd, so fd is used by the upper layer, and the same is true for shmid and key;

The key represents the shared memory in the kernel, and the upper layer does not use the key, but uses the shmid, so the comparison can be well understood

So, we try to delete the shared memory

The ipcrm -m command is used to delete shared memory

From the test results, the shared memory cannot be deleted by using the key, and shmid must be used, which also reflects that the upper layer uses shmid instead of key

ipcrm -m shmid

3.1.8 Release shared memory (shmctl) 

There are two ways to release shared memory, one is to use the command (ipcrm) to release the shared memory, the other is to call the function to release the shared memory after the process communication is completed, the shnctl system call is used to control the shared memory (including control or setting Attributes of shared memory, release of shared memory)

man 2 shmctl check

explain:

函数: shmctl
shmctl: shared memory control

头文件:
 #include <sys/ipc.h>
 #include <sys/shm.h>

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

参数:
    第一个参数shmid,表示所控制共享内存的用户级标识符
    第二个参数cmd,表示具体的控制动作
    第三个参数buf,用于获取或设置所控制共享内存的数据结构

返回值:
成功,返回0
失败返回-1,错误码被设置

 The options for the second parameter cmd are:

  • IPC_STAT: Get the current associated value of the shared memory, at this time the parameter buf is used as an output parameter
  • IPC_SET: On the premise that the process has sufficient permissions, set the current associated value of the shared memory to the value in the data structure pointed to by buf
  • IPC_RMID: delete shared memory

The commonly used option is the third: IPC_RMID, delete shared memory

The third parameter buf is generally not used, just pass nullptr, it is used to control the data structure of the shared memory

The data structure of shared memory is discussed below

Test delete shared memory:

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

using namespace std;

#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << errno << ":" << strerror(errno) << endl;
    }

    //打印key值
    printf("key: %x\n", k);

    //创建共享内存
    int shmid = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL);
    if(shmid < 0)//创建失败
    {
        cerr << errno << ":" << strerror(errno) << endl;
        exit(-1);
    }

    //打印shm
     printf("shm: %d\n", shmid);

     //间隔10s删除共享内存
    sleep(10);
    //释放共享内存
     int ret = shmctl(shmid, IPC_RMID, nullptr);
     if(ret == -1)//删除失败
     {
         cerr << errno << ":" << strerror(errno) << endl;
     }

    return 0;
}

monitoring script

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

Running results, the shared memory has been deleted

The work done above is to create and delete shared memory, but the shared memory has not been associated with the process, and the process cannot be used, so let's talk about the association of shared memory

3.1.9 Associated shared memory (shmat)  

The function of the shmat function: connect the shared memory segment to the process address space, that is, the associated shared memory

Man 2 shmat Check it out:

explain:

函数: shmat
at:attach

头文件:
#include <sys/types.h>
#include <sys/shm.h>

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

参数:
    第一个参数shmid,表示待关联共享内存的用户级标识符
    第二个参数shmaddr,指定共享内存映射到进程地址空间的某一地址,通常设置为NULL,表示让内核自己决定一个合适的地址位置
    第三个参数shmflg,表示关联共享内存时设置的某些属性

返回值:
成功,返回共享内存映射到进程地址空间中的起始地址(成功返回一个指针,指向共享内存第一个节)
败,返回(void*)-1,错误码被设置

  The third parameter shmflg is related to read and write permissions, usually set to 0, indicating that the default is read and write permissions

carry out testing:

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

using namespace std;

#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << "ftok: " << errno << ":" << strerror(errno) << endl;
         exit(-1);
    }

    //打印key值
    printf("key: %x\n", k);

    //创建共享内存
    int shmid = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL);
    if(shmid < 0)//创建失败
    {
        cerr << "shmget: "<< errno << ":" << strerror(errno) << endl;
        exit(-1);
    }

    //打印shm
     printf("shm: %d\n", shmid);

     //关联共享内存
     void* men = shmat(shmid, nullptr, 0);
     if(men == (void*)-1)//关联失败
     {
         cerr << "shmat: "<< errno << ":" << strerror(errno) << endl;
         exit(-1);
     }

     //间隔10s删除共享内存
    sleep(10);
    //释放共享内存
     int ret = shmctl(shmid, IPC_RMID, nullptr);
     if(ret == -1)//删除失败
     {
         cerr << "shmctl: "<< errno << ":" << strerror(errno) << endl;
          exit(-1);
     }

    return 0;
}

The result of the operation is that the permission is denied when associating the shared memory 

why is that? ?

This is because when we use the shmget function to create a shared memory, we do not set permissions on the created shared memory, so the default permission of the created shared memory is 0, that is, there is no permission, so the server process has no permission to associate with the shared memory

Perme is the permission of the shared memory, the permission of the shared memory we created is 0,

nattch is the number of processes associated with shared memory, n represents the number, and attch represents the association

So we need to add permissions when creating shared memory:

Compile and run again, and the shared memory association is successful

3.1.10 Disassociate shared memory (shmdt)   

To cancel the association between shared memory and process address space, we need to use the shmdt function

man 2 shmdt View:

explain: 

函数:shmdt
dt: detach

头文件与shmat一致

函数原型:
 int shmdt(const void *shmaddr);

参数:传入shmat所返回的指针,即共享内存的起始地址

返回值
成功,返回0
失败,返回-1,错误码被设置

 carry out testing:

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

using namespace std;

#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096

int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << "ftok: " << errno << ":" << strerror(errno) << endl;
         exit(-1);
    }

    //打印key值
    printf("key: %x\n", k);

    //创建共享内存
    int shmid = shmget(k, MAX_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if(shmid < 0)//创建失败
    {
        cerr << "shmget: "<< errno << ":" << strerror(errno) << endl;
        exit(-1);
    }

    //打印shm
     printf("shm: %d\n", shmid);

     //关联共享内存
     void* men = shmat(shmid, nullptr, 0);
     if(men == (void*)-1)//关联失败
     {
         cerr << "shmat: "<< errno << ":" << strerror(errno) << endl;
         exit(-1);
     }

     //使用和其他操作
     sleep(5);

     //去关联共享内存
    int n = shmdt(men);
    if(n == -1)
    {
         cerr << "shmdt: "<< errno << ":" << strerror(errno) << endl;
         exit(-1);
    }

     //间隔5s删除共享内存
    sleep(5);
    //释放共享内存
     int ret = shmctl(shmid, IPC_RMID, nullptr);
     if(ret == -1)//删除失败
     {
         cerr << "shmctl: "<< errno << ":" << strerror(errno) << endl;
          exit(-1);
     }

    return 0;
}

Running result, the shared memory is deassociated successfully

3.1.11 Use shared memory to realize serve&client communication

Now that we know the creation, association, disassociation and release of shared memory, we can now try to let two processes communicate through shared memory

Common header file (comm.hpp):

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

using namespace std;

#define PATHNAME "."
#define PROJ_ID 0x666
#define MAX_SIZE 4096

//获取key
key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJ_ID);
    if(k < 0)//失败
    {
        // cin cout cerr  ->  stdin stdout stderr  ->  对应文件描述符:0 1 2
        cerr << "ftok: " << errno << ":" << strerror(errno) << endl;
         exit(-1);
    }
}

int getShmHelper(key_t k, int flags)
{
    int shmid = shmget(k, MAX_SIZE, flags);
    if(shmid < 0)//创建或获取失败
    {
        cerr << "shmget: "<< errno << ":" << strerror(errno) << endl;
        exit(-1);
    }
}

//创建共享内存
int createShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0600);
}

//获取共享内存
int getShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT);
}

 //关联共享内存
void *attachShm(int shmid)
{
    void* men = shmat(shmid, nullptr, 0);
     if(men == (void*)-1)//关联失败
     {
         cerr << "shmat: "<< errno << ":" << strerror(errno) << endl;
         exit(-1);
     }
     return men;
}

//去关联共享内存
void detachShm(void *start)
{
     if(shmdt(start) == -1)
     {
         cerr << "shmdt: "<< errno << ":" << strerror(errno) << endl;
     }
}

 //释放共享内存
 void delShm(int shmid)
 {
     int ret = shmctl(shmid, IPC_RMID, nullptr);
     if(ret == -1)//删除失败
     {
         cerr << "shmctl: "<< errno << ":" << strerror(errno) << endl;
     }
 }

The server is responsible for creating the shared memory. After creation, associate the shared memory with the server to obtain the message sent by the client.

The server code is as follows (shm_server.cpp):

#include "comm.hpp"

//服务端
int main()
{
    //获取key
    key_t k = getKey(); 
    printf("key: 0x%x\n", k);
    //创建共享内存
    int shmid = createShm(k);
    printf("shmid: %d\n", shmid);

    //关联共享内存
    char *start = (char*)attachShm(shmid);
    printf("attach success, address start: %p\n", start);

    //使用,进行通信
    while(true)
    {
         printf("client say : %s\n", start);
         sleep(1);
    }

    //去关联
    detachShm(start);

    sleep(5);
    // //释放共享内存
     delShm(shmid);

    return 0;
}

The client only needs to directly associate with the shared memory created by the server, and the client sends a message to the server

The client code is as follows (shm_client.cpp):

#include "comm.hpp"

//用户端
int main()
{
     //获取key,key是与服务端一致的
    key_t k = getKey(); 
    printf("key: 0x%x\n", k);
    //获取服务端创建的共享内存
    int shmid = getShm(k);
    printf("shmid: %d\n", shmid);

    //关联共享内存
    char *start = (char*)attachShm(shmid);
    printf("attach success, address start: %p\n", start);

    //使用,与服务端通信
    const char* message = "hello server, 我是另一个进程,正在和你通信";
     pid_t id = getpid();
    int cnt = 1;
     while(true)
    {
        snprintf(start, MAX_SIZE, "%s[pid:%d][消息编号:%d]", message, id, cnt++);//snprintf自带 '\n'
        sleep(1);
    }

    //去关联
    detachShm(start);

    //done

    return 0;
}

Note: delete the shm ipc resource, note that it is not necessary to delete it manually, here is only to demonstrate the relevant instructions, deleting the IPC resource is what the process should do 

The server runs first, then the client, and the communication is successful

 Check ipcs, the number of shared memory links is 2

3.1.12 Advantages and disadvantages of shared memory 

Advantages of shared memory:

Shared memory is the fastest of all inter-process communication (no buffer required, which can greatly reduce the number of copies of communication data) 

Disadvantages of shared memory:

Shared memory is not synchronized and mutually exclusive, without any protection for data . For example: if the server reads faster and the client sends data slower, the same message will be read multiple times by the server

Note: Because the system allocates shared memory with 4KB as the basic unit (because the basic unit of memory division is Pag), it is generally recommended to apply for a shared memory size that is an integer multiple bytes (4KB). For example, the shared memory you apply for The size of the memory is 4097 bytes, the OS will apply for 8KB for you, but you can actually wait for only 4097 bytes

3.1.12 Kernel Data Structures for Shared Memory

The data structure of shared memory is as follows:

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 */
};

In the user layer, the OS also exposes most of the data structures to the user 

The unique identification key of the shared memory is also in this data structure

3.2 System V message queue (understand) 

The message queue is actually a queue created in the system. Each member of the queue is a data block. These data blocks are composed of two parts: type and information. Two communicating processes see the same message in some way. A message queue, both ends of the message queue can read and write data (simple understanding is enough)

In the kernel data structure of the message queue, the key is also used to identify the uniqueness of the queue

The interface used by the message queue is basically the same as the interface function of the shared memory, and the function name is different.

//创建消息队列
msgget
int msgget(key_t key, int msgflg);

//消息队列的控制
msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

//向消息队列发送数据
msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

//从消息队列获取数据
msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

3.3 System V semaphore (understand)

Semaphores are mainly used for synchronization and mutual exclusion , and also belong to the category of communication

The essence of the semaphore is:

The essence of a semaphore is a counter, which is usually used to indicate the number of resources in public resources

  • Public resources: resources that can be accessed by multiple processes at the same time (such as the above shared memory, message queues are public resources) 
  • Access to public resources that are not protected: data inconsistency will occur (such as the shared memory above)
  • Critical resources: public resources that will be protected in the future are called critical resources
  • Critical section: The process has corresponding code to access the corresponding critical resource, and the corresponding code is called critical section
  • Non-critical section: Code that does not access critical resources in a process is called a non-critical section
  • NOTE: Most resources are standalone
  • Mutual exclusion and synchronization: this is used to protect public resources (mutual exclusion: if a process accesses the public resource, other processes are not allowed to access the resource, let’s talk about synchronous multi-threading)
  • Atomicity: Either don’t do it, or do it if you want to do it, there are only two states, called atomicity

Note: The above concepts are just a brief discussion, some will be explained later

Why is there a semaphore? ? ?

Explain with an example, such as a movie theater, this movie theater is regarded as a public resource, and each seat in the movie theater is regarded as a sub-resource divided into shared resources;

I only have to buy a ticket, and this seat belongs to me within a certain period of time. Even if I buy a ticket and don’t go, this seat is mine. Conversely, if you don’t buy a ticket, this seat must not be yours. The purchased ticket must correspond to a seat.

In the same way, when I want to use a certain resource, I can make a reservation for this resource, which is equivalent to buying a ticket. I have already reserved this seat when I buy a ticket. must belong to me

Assuming that there are only 100 seats in the cinema, you can’t sell the 101st ticket, right? There will be a conflict when the corresponding process accesses the resource

If a process wants to access a certain resource, it must first apply for a semaphore. Applying for a semaphore is equivalent to booking a small part of the shared resource, allowing the process to access the resource.

Split a common resource into individual sub-resources, and different processes can access different sub-resource processes to achieve concurrency

The premise of applying for a semaphore is that all processes must see the same semaphore, so the semaphore itself is a public resource 

Since the semaphore is a public resource, it is a public resource that must ensure its own security. The semaphore can be regarded as an integer. This integer performs ++ and -- to ensure its own security, so ++ and -- operations are atomic

That is, the semaphore itself is also a critical resource. While it can protect other shared resources, it also needs to protect its own safety. The addition, subtraction, and subtraction inside the semaphore are atomic 

When the semaphore is 1, it means that the shared resource is used as a whole and provides mutual exclusion function (other processes cannot use it). The semaphore that provides mutual exclusion function is also called binary semaphore

Let’s talk about these briefly, and discuss them in detail later

Note: IPC resources must be deleted, otherwise they will not be cleared automatically, unless restarted, the life cycle of system V IPC resources varies with the kernel

----------------I am the dividing line---------------

The article is over here, the next one will be updated soon

Guess you like

Origin blog.csdn.net/m0_64280701/article/details/129847243