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! ~
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.