Detailed explanation of Linux shared memory and functions (including examples)

The concept of shared memory

Shared memory means that multiple processes can map a common memory of a section of memory into their own process space to realize data sharing and transmission. It is a resource at the kernel level and is the fastest way of communication among all processes. Kind of.

In the shell environment, you can use ipcs to view the status of the current system IPC, such as the current computer:

$ ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2260992    deeplearni 600        524288     2          dest
0x00000000 2490369    deeplearni 600        67108864   2          dest
0x00000000 163842     root       777        7680       2
0x00000000 196611     root       777        8294400    2
0x00000000 229380     root       777        4096       2
0x00000000 262149     root       777        8192       2
0x00000000 294918     root       777        12288      2
...省略
0x00000000 2064444    root       777        233472     2
0x00000000 2097213    root       777        237568     2
0x00000000 2129982    root       777        241664     2
0x00000000 2162751    root       777        245760     2
0x00000000 2654272    deeplearni 600        524288     2          dest
0x00000000 2687041    deeplearni 600        524288     2          dest
0x00000000 2719810    deeplearni 600        524288     2          dest
0x00000000 2752579    deeplearni 600        524288     2          dest
0x00000000 2981956    deeplearni 600        524288     2          dest
0x00000000 2949189    deeplearni 600        524288     2          dest
0x00000000 3014726    deeplearni 600        67108864   2          dest

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

This command uses additional parameters to view an IPC separately: -m (shared memory), -q (message queue), -s (semaphore).

Since multiple processes have access rights to the same memory area, the synchronization problem between each process needs to be solved and can be controlled with semaphores.

[Article benefits] Xiaobian recommends his own C/C++Linux group: 812855908! Organized some learning books and video materials that I think are better to share in it, and you can add them if you need them! ~
Insert picture description here
Insert picture description here

For each shared memory segment, the kernel maintains a shmid_ds type structure:

// 摘自所用ubuntu18.04电脑中的/usr/include/i386-linux-gnu/bits/shm.h
struct shmid_ds
  {
    
    
    struct ipc_perm shm_perm;           /* operation permission struct */
    size_t shm_segsz;                   /* size of segment in bytes */
    __time_t shm_atime;                 /* time of last shmat() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved1;
#endif
    __time_t shm_dtime;                 /* time of last shmdt() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved2;
#endif
    __time_t shm_ctime;                 /* time of last change by shmctl() */
#ifndef __x86_64__
    unsigned long int __glibc_reserved3;
#endif
    __pid_t shm_cpid;                   /* pid of creator */
    __pid_t shm_lpid;                   /* pid of last shmop */
    shmatt_t shm_nattch;                /* number of current attaches */
    __syscall_ulong_t __glibc_reserved4;
    __syscall_ulong_t __glibc_reserved5;
  };

Related operations of shared memory

Create/open shared memory The
shmget() function is needed to create shared memory. The prototype is as follows:

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);

The ID of the shared memory is returned when the creation is successful, and -1 is returned when the error occurs.

The parameter key is the key value of the shared memory, the parameter size is the size of the shared memory created, and the parameter flag is the operation type of the calling function. The function of shmget() determined by the parameter key and the parameter flag:

  • When the key is IPC_PRIVATE, a new shared memory is created, and the flag value is invalid.
  • When the key is not IPC_PRIVATE, and the IPC_CREAT bit is set in the flag, but the IPC_EXCL bit is not set, if the key is an existing shared memory key value in the kernel, open it, otherwise create a new shared memory.
  • When the key is not IPC_PRIVATE, and the IPC_CREAT and IPC_EXCL bits are set in the flag, only the operation of creating shared memory is performed. If the key is an existing shared memory key value in the kernel, an EEXIST error is returned.

Attachment (mapping) of
shared memory After creating a shared memory, if a process wants to use it, it needs to attach this memory area to its own process space (or address mapping), which requires the shmat() function:

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int *shmat(int shmid, const void *addr, int flag);

Successful operation returns the address pointer to the shared memory segment, and error -1 is returned.

The parameter shmid is the ID of the shared memory. The parameter addr and the parameter flag together indicate the address value to be introduced. There are usually only two uses:

  • addr is 0, indicating that the kernel is allowed to determine the first referenceable position
  • addr is not 0, and SHM_RND is specified in flag, then this paragraph is introduced to the position pointed to by addr.

After the shmat() function is executed successfully, the shm_nattch counter value of the shmid_ds structure of the shmid shared memory segment will be increased by 1.

Separation of shared memory
When a process has used up shared memory, it needs to be detached from its process space, using the shmdt() function:

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(void *addr);

It returns 0 if it runs successfully and -1 if it fails.

The parameter addr is the return value of calling shmat(), which is the address pointer of the shared memory segment. After the shmdt() function is executed successfully, the shm_nattch counter value is decreased by 1.

Shared memory control
Use shmctl() to perform a variety of control operations on the shared memory segment, the function prototype:

#include <sys/types,h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_s *buf);

It returns 0 if it runs successfully and -1 if it fails.

The parameter shmid is the ID of the shared memory, and the parameter cmd indicates the operation to be performed. It is used in conjunction with the parameter *buf :

Take the shmid_ds structure of the shared memory pointed to by shmid, and assign a value to the structure pointed to by the parameter buf

Programming example

The basic steps:

  • Generate key, ftok()
  • Use key to create/get a shared memory, shmget()
  • Map shared memory, get virtual address, shmat()
  • Use shared memory through address pointer
  • Remove the mapping, shmdt()
  • Destroy shared memory, shmctl()

Example 1
Process 1 creates shared memory and writes data, shm1.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    
    
    // generate key
    key_t key = ftok("./", 200);
    printf("key=%#x\n", key);

    // create a share memory
    int shmid = shmget(key, 8, IPC_CREAT|0666|IPC_EXCL);
    if(shmid == -1)
    {
    
    
        perror("shmget failed\n");
        exit(1);
    }
    printf("shmid=%#x\n", shmid);

    // map share memory to get the virtual address
    void *p = shmat(shmid, 0, 0);
    if((void *)-1 == p)
    {
    
    
        perror("shmat failed");
        exit(2);
    }

    // write data to share memory
    int *pi = p;
    *pi = 0xaaaaaaaa;
    *(pi+1) = 0x55555555;

    // remove the map
    if(shmdt(p) == -1)
    {
    
    
        perror("shmdt failed");
        exit(3);
    }

    // delete the share memory
    printf("use Enter to destroy the share memory\n");
    getchar();
    if(shmctl(shmid, IPC_RMID, NULL) == -1)
    {
    
    
        perror("shmctl");
        exit(4);
    }

    return 0;
}

Process 2 reads the contents of shared memory, shm2.c;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
    
    
    // generate key
    key_t key = ftok("./", 200);
    printf("key=%#x\n", key);

    // get the share memory
    int shmid = shmget(key, 0, 0);
    if(shmid == -1)
    {
    
    
        perror("shmget failed\n");
        exit(1);
    }
    printf("shmid=%#x\n", shmid);

    // map share memory to get the virtual address
    void *p = shmat(shmid, 0, 0);
    if((void *)-1 == p)
    {
    
    
        perror("shmat failed");
        exit(2);
    }

    // read: get data from the share memory
    int x = *((int *)p);
    int y = *((int *)p+1);
    printf("x=%#x y=%#x\n", x, y);

    // remove the map
    if(shmdt(p) == -1)
    {
    
    
        perror("shmdt failed");
        exit(3);
    }

    return 0;
}

Compile and run the test, first run the shm1 program in a shell:

$ ./shm1
key=0xc81102ed
shmid=0x2e8047
use Enter to destroy the share memory

Run the shm2 program first in another shell:

$ ./shm2
key=0xc81102ed
shmid=0x2e8047
x=0xaaaaaaaa y=0x55555555

You can see that the communication is successful. Press "Enter" in the first shell to destroy the shared memory.

Example 2
Example 1 uses the key generated by the ftok() function to create shared memory. This example uses the IPC_PRIVATE parameter to create shared memory.

Create a shared memory and output its ID number, create_shm.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSZ 4096

int main(void)
{
    
    
    int shm_id;

    shm_id = shmget(IPC_PRIVATE, BUFSZ, 0666);
    if(shm_id < 0)
    {
    
    
        printf("shmget failed!\n");
        exit(1);
    }
    printf("create a shared memory segment successfully: %d\n", shm_id);

    system("ipcs -m");

    exit(0);
}

Open the shared memory with the specified ID, write the content, write_shm.c:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    
    
   char name[4];
   int age;
}people;

int main(int argc, char **argv)
{
    
    
   int shm_id, i;
   char temp;
   people *p_map;

   if(argc != 2)
   {
    
    
       printf("USAGE:atshm <identifier>\n");
       exit(1);
   }

   // get share memory ID from input
   shm_id = atoi(argv[1]);// str to int

   // map the share memory to get the virtul address
   p_map = (people *)shmat(shm_id, NULL, 0);

   // write
   temp = 'a';
   for(i=0; i<10; i++)
   {
    
    
       temp+=1;
       memcpy((*(p_map+i)).name, &temp, 1);
       (*(p_map+i)).age=20+i;
   }

   // remove map
   if(shmdt(p_map)==-1)
       perror("detach error!\n");

   return 0;
}

Open the shared memory with the specified ID, read the content, read_shm.c:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    
    
   char name[4];
   int age;
}people;

int main(int argc, char** argv)
{
    
    
   int shm_id, i;
   people *p_map;

   if(argc != 2)
   {
    
    
       printf("USAGC: atshm <identifier>\n");
       exit(1);
   }

   // get the share memory ID from input
   shm_id = atoi(argv[1]);// str to int

   // map the share memory to get the virtual address
   p_map = (people*)shmat(shm_id, NULL, 0);

   // read data
   for(i=0; i<10; i++)
   {
    
    
       printf("name:%s   ", (*(p_map+i)).name);
       printf("age %d\n", (*(p_map+i)).age);
   }

   // remove the map
   if(shmdt(p_map)==-1)
       perror("detach error!\n");

   return 0;
}

To compile the above three programs, first run the create_shm program to create a shared memory:

$ ./create_shm
create a shared memory segment successfully: 3080264

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2260992    deeplearni 600        524288     2          dest
0x00000000 2490369    deeplearni 600        67108864   2          dest
0x00000000 163842     root       777        7680       2
0x00000000 196611     root       777        8294400    2
0x00000000 229380     root       777        4096       2
...省略
0x00000000 2031675    root       777        229376     2
0x00000000 2064444    root       777        233472     2
0x00000000 2097213    root       777        237568     2
0x00000000 2129982    root       777        241664     2
0x00000000 2162751    root       777        245760     2
0x00000000 2654272    deeplearni 600        524288     2          dest
0x00000000 2687041    deeplearni 600        524288     2          dest
0x00000000 2719810    deeplearni 600        524288     2          dest
0x00000000 2752579    deeplearni 600        524288     2          dest
0x00000000 2981956    deeplearni 600        524288     2          dest
0x00000000 2949189    deeplearni 600        524288     2          dest
0x00000000 3014726    deeplearni 600        67108864   2          dest
0xc81102ed 3047495    deeplearni 666        8          0
0x00000000 3080264    deeplearni 666        4096       0

You can see that the program created a shared memory with shmid of 3080264, and then you can run the write_shm program, you need to take the ID number as a parameter:

$ ./write_shm 3080264

Although there is no output, the program has written the content to the shared memory and the shared memory has not been deleted. You can run the read_shm program to read the contents of the shared memory:

$ ./read_shm 3080264
name:b   age 20
name:c   age 21
name:d   age 22
name:e   age 23
name:f   age 24
name:g   age 25
name:h   age 26
name:i   age 27
name:j   age 28
name:k   age 29

As you can see, the program reads 10 characters stored in the shared memory.

In addition, useless shared memory can also be deleted through commands, using the form of ipcrm -m shared memory id, such as:

$ ipcrm -m 3080264

At this point, use the ipcs -m command to check that there is no shared memory with shmid of 3080264.

Guess you like

Origin blog.csdn.net/qq_40989769/article/details/110531512