Linux - detailed shared memory shared memory

Table of contents

1. Introduction to shared memory

(1). What is shared memory

(2). Advantages of shared memory

(3). Disadvantages of shared memory

2. Use of shared memory

(1). Create—shmget

①key

②size

③shmflg

④ return value

(2). Connection—shmat

(3). Separation—shmdt

(4). Destroy—shmctl

(5). View -ipcs

(6).Delete—ipcrm

(7). Read and write 

3. Shared memory and access control

(1). Add access control

(2). Possible pitfalls


1. Introduction to shared memory

(1). What is shared memory

Shared memory is essentially an area in memory used for inter-process communication. This memory space is allocated and managed by the operating system . Similar to the file system, when the operating system manages shared memory, it not only has memory data blocks, but also creates a corresponding structure to record the attributes of the shared memory for easy management.

Therefore, there is not only one copy of shared memory, you can apply for more than one according to your needs.

When communicating between processes, the address of the shared memory will be obtained, the write-end process writes data, and the read-end process completes the data reading by directly accessing the memory.

(2). Advantages of shared memory

Compared with pipelines, shared memory can not only be used for communication between non-parent-child processes, but also access data faster than pipelines . This is due to the fact that the communication directly accesses the memory, while the pipeline needs to access the file through the operating system before obtaining the memory data.

(3). Disadvantages of shared memory

When used for inter-process communication, shared memory itself does not support blocking wait operations. This is because when the reader reads the data, the data will not be cleared in memory. Therefore, the read end and the write end can access the memory space at the same time, that is, full duplex. Because the essence of shared memory is that the process directly accesses the memory, it cannot actively stop reading. If the reading end does not limit it, it will continue to read data. Similarly, the write end will continue to write data. In other words, shared memory itself has no access controls

2. Use of shared memory


(1). Create—shmget

If you want to use shared memory, you must first create shared memory.

①key

shmget will create a shared memory according to the key value, so when creating multiple shared memories, each key value must be unique.

To obtain the key value, you can use the library function ftok to obtain a unique key_t type value.

The parameter pathname is a path, which must be a real and accessible path.

The parameter proj_id is an int type number, and a non-zero value must be passed in.

The key_t value is returned successfully, and -1 is returned on failure.

The ftok function will generate a unique key_t return value through an algorithm based on the path and proj_id.

In multi-process communication, both communication parties need to use the same key value, so the ftok parameters used by both parties should be consistent. 

②size

This parameter is used to determine the shared memory size.

Generally speaking, it is an integer multiple of 4096, because the size of a memory block is 4KB or 4096B. Therefore , even if the size of the space we need is not an integer multiple of the block size, the operating system actually allocates multiples of the block. But when in use, those excess allocated space exceeding size cannot be accessed .  

③shmflg

 This parameter is used to determine shared memory properties.

In use: flag bit | memory permission

There are two flag parameters: IPC_CREAT, IPC_EXCL

There are two commonly used methods:

Way meaning
shmget(..., IPC_CREAT | permission) If the creation fails, no error will be reported and the existing shmid will be returned.
shmget(..., IPC_CREAT | IPC_EXCL | permissions) Creation failure returns -1

It is worth noting that PC_EXCL cannot be used alone.

Usually, in multi-process communication, the creator uses IPC_CREAT | IPC_EXCL, and the receiver uses 0.

④ return value

The return value is int type, called shmid. Each shared memory will have a shmid, which is used to pass parameters when connecting and detaching. 

(2). Connection—shmat

After the shared memory is created, it cannot be used directly. It needs to find the memory address before using it, that is, to connect. 

 shmid is the return value of shmget.

shmaddr is used to determine where to hang the shared memory in the virtual address of the process. Generally, filling in nullptr means letting the kernel determine the location by itself.

shmflg is used to determine the mount mode, generally fill in 0 .

If the connection is successful, it returns the starting address of the shared memory in the process, and if it fails, it returns -1. 

(3). Separation—shmdt

When you are done using it, you need to detach the mounted shared memory.

 shmaddr is the same as shmat, which is the address position of the shared memory in the process, and generally fills in nullptr.

Returns 0 if the separation is successful, and -1 if it fails. 

(4). Destroy—shmctl

The interface itself is used to control shared memory and can be used for destruction. 

shmid is no longer introduced, cmd is passed to IPC_RMID, and buf is passed to nullptr. 

Returns 0 on success and -1 on failure. 

(5). View -ipcs

This command is a system command.

When using it, you can view all the current shared memory.

ipcs -m 

(6).Delete—ipcrm

Delete by specifying the shared memory shmid.

ipcrm -m [shmid] 

 

(7). Read and write 

After calling shmat, an address will be returned, the reader can directly read the address data, and the write terminal can directly write to the address.

//读端, 将共享内存数据读取到文件,此处为显示器文件
char* p = (char*)shmat(...);
write(1, p, sizeof p);
//写端,将文件中数据写入共享内存,此处为键盘文件
char* p = (char*)shmat(...);
read(0, p, 4096);

 

3. Shared memory and access control

(1). Add access control

Through the first part of the blog, we know that shared memory does not support access control, so can we add access control to shared memory-it is absolutely possible.

The method is to borrow the access control of the named pipe, that is, blocking. 

First of all, we have the following code, the code is that the reader keeps reading the data from the write end until the write end enters quit.

//写端
int main()
{
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|0660);//获取shmid
  char* p = (char*)shmat(shmid, nullptr, 0);//连接
  while(1){
    ssize_t s = read(0, p, 4096);//写入shm
    p[s - 1] = 0;
    assert(s > 0);
    (void)s;
  }
  shmdt(p);//分离
  return 0;
}
//读端
int main()
{
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0660);//创建
  char* p = (char*)shmat(shmid, nullptr, 0);//连接
  while(1){
    assert(p != nullptr);
    if(strcmp(p, "quit") == 0)break;
    printf("%s\n", p);//读取shm中数据
    sleep(1);
  }
  shmdt(p);//分离
  shmctl(shmid, IPC_RMID, nullptr);//销毁
  return 0;
}

 However, because the shared memory cannot be accessed and controlled, the reader will always read the data. Even if we add the sleep function, the problem cannot be fundamentally solved.

The solution is to add the read end and write end of the pipeline respectively at the read end and the write end. Because we know that the pipe reader will block until it reads data from the writer, we place the pipe reader before the shared memory read and the pipe write after the shared memory write.

In this way, when the shm write end writes data, it will trigger the pipeline write end to write data, and when the pipeline write end writes data, the pipeline read end will stop blocking, and then execute the shm read end.

The legend is as follows:


code show as below: 

//写端
int main()
{
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|0660);//获取shmid
  char* p = (char*)shmat(shmid, nullptr, 0);//连接
  int fd = open(..., O_WRONLY);//打开命名管道
  while(1){
    ssize_t s = read(0, p, 4096);//写入shm
    p[s - 1] = 0;
    assert(s > 0);
    (void)s;
    char i[4] = { 0 };
    write(fd, i, sizeof i);//写入管道
  }
  shmdt(p);//分离
  close(fd);
  return 0;
}
//读端
int main()
{
  int i = mkfifo(PATH_FIFO, 0660);//创建管道
  assert(i >= 0);
  key_t key = ftok(".", 131);
  int shmid = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0660);//创建
  char* p = (char*)shmat(shmid, nullptr, 0);//连接shm
  int fd = open(..., O_RDONLY);//连接管道
  while(1){
    char buf[4];
    read(fd, buf, sizeof buf);//管道等待读取,阻塞
    assert(p != nullptr);
    if(strcmp(p, "quit") == 0)break;
    printf("%s\n", p);//读取shm中数据
    sleep(1);
  }
  shmdt(p);//分离
  shmctl(shmid, IPC_RMID, nullptr);//销毁
  close(fd);
  return 0;
}

(2). Possible pitfalls

When adding access control, there will be a possible trap, that is, can the named pipe be opened (open) before creating shm?

No, because opening a pipe requires the read and write ends to be open at the same time to continue, otherwise it will block.

If the write end is blocked, it is fine. When the read end creates shm, the write end creation fails and returns shmid, but if the read end is blocked, then after the write end creates shm, the read end fails because of the addition of IPC_EXCL. Return -1, and then shmat also fails to return nullptr, and then the address obtained by the reader is empty.

Pay attention to encapsulation for simple modules, and layering for complex modules——Unnamed


Please correct me if there is any mistake

Guess you like

Origin blog.csdn.net/weixin_61857742/article/details/128252280