一,内存映射
对于磁盘文件和进程:
将一个文件或其它对象映射到进程地址空间,实现文件在磁盘的存储地址和进程地址空间中一段虚拟地址的映射关系。有了这样的映射,进程利用指针直接读写虚拟地址就可以完成对文件的读写操作。这样可以避免进行read/write函数操作。
文件的内存映射示意图:
对于用户进程和内核进程:
将用户进程的一段内存区域映射到内核进程,映射成功后,用户进程对这段内存区域的修改直接反映到内核空间,同样,内核进程对这段内存区域的修改也直接反映到用户空间。
没有内存映射的I/O操作示意图: 磁盘->内核空间->用户空间
有内存映射的I/O操作示意图:少了一个copy操作
内存映射的优点:
减少了拷贝次数,节省I/O操作的开支
用户空间和内核空间可以直接高效交互
进程可以直接操作磁盘文件,用内存读写代替 I/O读写
应用场景:
1.进程间通信
使用内存映射实现进程间通信的两个场景:
场景1.有亲缘关系的进程间通信(父子进程)
step1: 父进程创建内存映射区
step2: 父进程利用fork()创建子进程
step3: 子进程继承了父进程的内存映射区后,父子进程通过内存映射区进行通信
场景2.没有亲缘关系的进程间通信
step1: 准备一个非空的磁盘文件
step2: 进程a通过磁盘文件创建内存映射区
step3: 进程b通过磁盘文件创建内存映射区
step4: 进程a和进程b共同修改内存映射区实现进程通信
*基于内存映射区的进程间通信,是非阻塞的。
*子进程能通过fork继承存储映射区(因为子进程复制父进程地址空间,而存储映射区是该地址空间中的一部分),但是由于同样的原因,新程序则不能通过exec继承存储映射区。
2.文件读写操作
step1: 读磁盘文件,获得文件描述符
step2: 基于文件描述符建立进程的内存映射区
step3: 利用进程进行内存映射区的读写操作
step4: 释放内存映射区,关闭文件描述符
内存映射的重要函数--mmap/munmap/msync
代码语言:javascript 复制void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
start:用户进程中要映射的某段内存区域的起始地址,通常为NULL(由内核来指定)
length:要映射的内存区域的大小
prot:期望的内存保护标志
flags:指定映射对象的类型
fd:要映射的文件描述符
offset:要映射的用户空间的内存区域在内核空间中已经分配好了的内存区域中的偏移
--prot参数取值:
PROT_READ:映射区可读
PROT_WRITE:映射区可写
PROT_EXEC:映射区可执行
PROT_NONE:映射区不可访问
--flags参数取值:
MAP_SHARED:变动是共享的,内存区域的读写影响到原文件
MAP_PRIVATE:变动是私有的,内存区域的读写不会影响到原文件
返回:若成功,返回指向内存映射区域的指针,若出错,返回MAP_FAILED(-1)。
*使用mmap时需要注意,不是所有文件都可以进行内存映射,一个访问终端或者套接字的描述符只能用read/write这类的函数去访问,用mmap做内存映射会报错。超过文件大小的访问会产生SIGBUS信号。
代码语言:javascript 复制int munmap(void *start, size_t length);
start:指向内存映射区的指针
length:内存映射区域的大小
返回:若成功,返回0,若出错,返回-1。
代码语言:javascript
复制
int msync(void *start, size_t length, int flags);
start:指向内存映射区的指针
length:内存映射区域的大小
flags:模式的设置
--flags参数取值:
MS_ASYNC:异步写
MS_SYNC:同步写
MS_INVALIDATE:使高速缓存的数据失效
*MS_ASYNC和MS_SYNC的区别,一旦写操作已经由内核排入队列,MS_ASYNC立即返回,MS_SYNC则要等到写操作完成后才返回。
代码样例:
Demo1: 文件操作--利用内存映射读文件的第一行
代码语言:javascript 复制#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[]) {
int fd, index;
char *map;
struct stat file_stats;
if ((fd = open("test.txt", O_RDONLY)) == -1) {
perror("open");
exit(1);
}
if (stat("test.txt", &file_stats) == -1) {
perror("stat");
exit(1);
}
// mmap to read
map = mmap(0, file_stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
// Print the first line
printf("The first line is:\n");
index = 0;
while(1) {
if (map[index] == '\n') {
printf("\n");
break;
} else {
printf("%c", map[index]);
}
index += 1;
}
if (munmap(map, file_stats.st_size) == -1) {
close(fd);
perror("Error un-mmapping the file");
exit(1);
}
close(fd);
return 0;
}
Demo2: 利用内存映射实现进程间通信
代码语言:javascript 复制#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
void* create_shared_memory(size_t size) {
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_ANONYMOUS | MAP_SHARED;
int fd = 0;
int offset = 0;
return mmap(NULL, size, prot, flags, fd, offset);
}
int main() {
char* parent_message = "hello";
char* child_message = "goodbye";
void* shmem = create_shared_memory(128);
memcpy(shmem, parent_message, sizeof(parent_message));
int pid = fork();
if (pid == 0) {
printf("Child read: %s\n", shmem);
memcpy(shmem, child_message, sizeof(child_message));
printf("Child wrote: %s\n", shmem);
} else {
printf("Parent read: %s\n", shmem);
sleep(3);
printf("After 3s, parent read: %s\n", shmem);
}
return 0;
}
运行结果:
代码语言:javascript 复制Parent read: hello
Child read: hello
Child wrote: goodbye
After 3s, parent read: goodbye
二,共享内存:
内存映射和共享内存的区别:
1.内存映射与文件关联,共享内存不需要与文件关联,把共享内存理解为内存上的一个匿名片段。
2.内存映射可以通过fork继承给子进程,共享内存不可以。
3.文件打开的函数不同,内存映射文件由open函数打开,共享内存区对象由shm_open函数打开。但是它们被打开后返回的文件描述符都是由mmap函数映射到进程的地址空间。
共享内存允许多个进程共享一个给定的存储区。
对于Client-Server架构,如果服务器进程和客户端进程共享同一块存储区,服务器进程正在将数据写入共享存储区时,在写入操作完成之前,客户端进程不应去取出这些数据。一般用信号量来同步共享内存的访问。
共享内存区在系统存储中的位置:
为什么要用共享内存:
对于涉及到内核操作的,内核和进程之间,经历了四次复制操作,开销很大。
使用共享内存后,客户到服务器之间只需要经历两次复制操作
共享内存常用函数:
Posix标准版本:
1.创建或获取共享内存
代码语言:javascript 复制int shm_open(const char *name, int oflag, mode_t mode);
--name:共享内存对象的名字
--oflag:与open函数类似,可以是O_RDONLY、O_WRONLY、O_RDWR,还可以按位或O_CREAT、O_EXCL、O_TRUNC等
--mode:如果oflag没有指定O_CREAT,可以指定为0
返回值:若成功,返回文件描述符。若失败,返回-1
2.销毁共享内存
代码语言:javascript 复制int shm_unlink(const char *name);
3.修改共享内存的大小(还可以修改文件的大小)
代码语言:javascript 复制int ftruncate(int fd, off_t length)
处理mmap的时候,普通文件或共享内存区对象的大小都可以通过调用ftruncate修改。文件大小如果大于length, 额外的数据就会被丢掉。
System_V标准版本:
1. 创建或获取共享内存
代码语言:javascript 复制#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg)
--key:进程间事先约定的key,或者调用key_t ftok( char * fname, int id )获取
--size:共享内存大小,当创建一个新的共享内存区时,size必须大于0,如果是访问一个已经存在的内存共享区,size可以是0
--shmflg:标志位,可以取IPC_CREATE|IPC_EXCL,它的用法和创建文件时使用的mode参数是一样的。
返回值:若成功,返回shmid。若失败,返回-1
2. 将进程附加到已创建的共享内存
代码语言:javascript 复制#include <sys/types.h>
#include <sys/shm.h>
void * shmat(int shmid, const void *shmaddr, int shmflg)
--shmid:共享内存区的标识id,shmget的返回值
--shmaddr:共享内存附加到本进程后在本进程地址空间的内存地址,若为NULL,由内核分配地址。
--shmflg:一般为0,不设置任何限制权限。如果设置为只读,shmflg=SHM_RDONLY
返回值:若成功,返回指向共享内存区的指针。若失败,返回-1
3. 从已附加的共享内存段中分离进程
代码语言:javascript 复制#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr)
--shmaddr:指向共享内存区的指针
返回值:若成功,返回0。若失败,返回-1
4.控制共享内存
代码语言:javascript 复制#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
--shmid:共享内存标识符
--cmd:共享内存控制指令
IPC_STAT:得到共享内存的状态
IPC_SET:改变共享内存的状态
IPC_RMID:删除该共享内存
--shmid_ds: 共享内存管理结构体
返回值:若成功,返回0。若失败,返回-1
两个版本的微小差异:Posix共享内存区对象的大小可在任意时刻由ftruncate函数修改,System V共享内存区对象的大小是在调用shmget创建时固定下来的。
代码样例:
Demo1: POSIX版
Producer:
代码语言:javascript 复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main()
{
/* the size (in bytes) of shared memory object */
const int SIZE = 4096;
/* name of the shared memory object */
const char* name = "OS";
/* strings written to shared memory */
const char* message_0 = "Hello";
const char* message_1 = "World!";
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory object */
void* ptr;
/* create the shared memory object */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory object */
ftruncate(shm_fd, SIZE);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
/* write to the shared memory object */
sprintf(ptr, "%s", message_0);
ptr += strlen(message_0);
sprintf(ptr, "%s", message_1);
ptr += strlen(message_1);
return 0;
}
consumer:
代码语言:javascript 复制#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main()
{
/* the size (in bytes) of shared memory object */
const int SIZE = 4096;
/* name of the shared memory object */
const char* name = "OS";
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory object */
void* ptr;
/* open the shared memory object */
shm_fd = shm_open(name, O_RDONLY, 0666);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
/* read from the shared memory object */
printf("%s", (char*)ptr);
/* remove the shared memory object */
shm_unlink(name);
return 0;
}
编译方式:
代码语言:javascript 复制gcc producer.c -pthread -lrt -o producer
gcc consumer.c -pthread -lrt -o consumer
Demo2: System_V版
代码语言:javascript 复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main(int argc, char *argv[])
{
key_t key;
int shmid;
char *data;
int mode;
key = 1234;
/*if ((key = ftok("shmdemo.c", 'R')) == -1) {
perror("ftok");
exit(1);
}*/
/* connect to the segment: */
if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1)
{
perror("shmget");
exit(1);
}
/* attach to the segment to get a pointer to it: */
data = shmat(shmid, (void *)0, 0);
if (data == (char *)(-1))
{
perror("shmat");
exit(1);
}
char *str_test = "Send by producer";
printf("Writing to segment: \"%s\"\n", str_test);
strncpy(data, str_test, SHM_SIZE);
/* Reading from the segment*/
printf("Reading form the segment: \"%s\"\n", data);
/* detach from the segment: */
if (shmdt(data) == -1)
{
perror("shmdt");
exit(1);
}
return 0;
}
运行结果:
代码语言:javascript 复制Writing to segment: "Send by producer"
Reading form the segment: "Send by producer"
参考教程:
《UNIX环境高级编程第3版》
https://code-examples.net/zh-TW/q/564fd2
https://www.geeksforgeeks.org/posix-shared-memory-api/
==============================================
文章目录
- 共享内存
-
- 一、共享内存的概念
- 二、相关函数
-
- 1、shmget函数(获取或创建共享内存)
- 2、shmat函数(把共享内存连接到当前进程的地址空间)(当shm_addr为NULL时,返回系统自动分配的共享物理地址映射到本进程的虚拟地址?否则返回shm_addr指定共享内存物理地址映射到本进程的虚拟地址?)(第二三个参数存疑)
- 3、shmdt函数(将共享内存从当前进程中分离,相当于shmat函数的反操作)(分离:解除虚拟内存地址与共享物理地址的映射关系?)
- 4、shmctl函数(删除共享内存)
- 总结:shmdt(addr)使进程中的shmaddr指针无效化,不可以使用,但是保留空间。shmctl(shmid,IPC_RMID,0) 删除共享内存,彻底不可用,释放空间。
- 三、示例程序
- 四、其它的操作命令
共享内存
一、共享内存的概念
共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。
共享内存并未提供锁机制,也就是说,在某一个进程对共享内存的进行读写的时候,不会阻止其它的进程对它的读写。如果要对共享内存的读/写加锁,可以使用信号灯。
二、相关函数
Linux中提供了一组函数用于操作共享内存,程序中需要包含以下头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
- 1
- 2
1、shmget函数(获取或创建共享内存)
它的声明为:
int shmget(key_t key, size_t size, int shmflg);
- 1
参数key
是共享内存的键值,是一个整数,typedef unsigned int key_t
,是共享内存在系统中的编号,不同共享内存的编号不能相同,这一点由程序员保证。key用十六进制表示比较好。
参数size
是待创建的共享内存的大小,以字节为单位。
参数shmflg
是共享内存的访问权限,与文件的权限一样,0666|IPC_CREAT
表示全部用户对它可读写,如果共享内存不存在,就创建一个共享内存。
【shmget】—— 是用来开辟/指向一块共享内存的函数
参数:
shmkey 是这块共享内存的标识符,如果是IPC_PRIVATE,则表示使用系统分配的键值创建; //疑问:系统分配的共享内存没有固定的键值,别的进程怎么知道,怎么来共享它呢?
shmsize 申请内存大小
flag 标志、权限;
当只有IPC_CREAT选项打开时,不管是否已存在该块共享内存,则都返回该共享内存的ID,若不存在则创建共享内存;
当只有IPC_EXCL选项打开时,不管有没有该块共享内存,shmget()都返回-1;(即:单独使用该标志无意义)
当IPC_CREAT | IPC_EXCL时, 如果没有该块共享内存,则创建,并返回共享内存ID,若已有该块共享内存,则返回-1;
返回值:
成功返回shmid 内存标识的ID,失败返回-1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
2、shmat函数(把共享内存连接到当前进程的地址空间)(当shm_addr为NULL时,返回系统自动分配的共享物理地址映射到本进程的虚拟地址?否则返回shm_addr指定共享内存物理地址映射到本进程的虚拟地址?)(第二三个参数存疑)
它的声明如下:
void* shmat(int shm_id, const void* shm_addr, int shmflg);
- 1
参数shm_id
是由shmget函数返回的共享内存标识。
参数shm_addr
指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。(存疑。。。下面别人又说这是输出参数)
参数shm_flg
是一组标志位,通常为0。
调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.
【shmat】—— 链接上指定ID标识的共享内存
参数:
shmid 内存标识ID,由shmget()函数返回;
shmaddr (输出参数)共享内存的首地址,同该函数返回值一致;
shmflag SHM_RDONLY 只读, 0 可读写;(SHM_COPY\SHM_MAP\SHM_RND不在此说明)
原文链接:https://www.cnblogs.com/yanzi-meng/p/10214755.html
- 1
- 2
- 3
- 4
- 5
- 6
1.如果shmaddr 是NULL,系统将自动选择一个合适的地址!
2.如果shmaddr 不是NULL 并且没有指定SHM_RND则此段连接到addr所指定的地址上。
3.如果shmaddr非0 并且指定了SHM_RND 则此段连接到shmaddr -(shmaddr mod SHMLAB)所表示的地址上。
这里解释一下SHM_RND命令,它的意思是取整,而SHMLAB的意思是低边界地址的倍数,它总是2的乘方,该算式是将地址向下取最近一个SHMLAB的倍数。
除非只计划在一种硬件上运行应用程序(在现在是不太可能的),否则不用指定共享段所连接到的地址。
所以一般指定shmaddr为0,以便由内核选择地址。
原文链接:https://blog.csdn.net/wangshiqueque/article/details/9628633
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3、shmdt函数(将共享内存从当前进程中分离,相当于shmat函数的反操作)(分离:解除虚拟内存地址与共享物理地址的映射关系?)
它的声明如下:
int shmdt(const void *shmaddr);
- 1
参数shmaddr
是shmat函数返回的地址。
调用成功时返回0,失败时返回-1.
【shmdt】—— 断开链接的共享内存指针
参数:
shmaddr 调用函数shmat()链接上共享内存的指针;
当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,
但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的 shm_nattch域的值减1,
当这个值为0时,内核才从物理上删除这个共享段
- 1
- 2
- 3
- 4
- 5
- 6
- 7
4、shmctl函数(删除共享内存)
它的声明如下:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
- 1
参数shm_id
是shmget函数返回的共享内存标识符。
参数command
填IPC_RMID。
参数buf
填0。
解释一下,shmctl是控制共享内存的函数,其功能不只是删除共享内容,但其它的功能没什么用,所以不介绍了。
注意,用root创建的共享内存,不管创建的权限是什么,普通用户无法删除。
【shmctl】—— 控制共享内存
参数:
shmid 共享内存标识ID;
cmd IPC_STAT 得到共享内存的状态
IPC_SET 改变共享内存的状态
IPC_RMID 删除共享内存
buf 是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定;
struct shmid_ds结构体:
struct shmid_ds {
struct ipc_perm shm_perm;
int shm_segsz;
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
unsigned short shm_cpid;
unsigned short shm_lpid;
short shm_nattch;
unsigned short shm_npages;
unsigned long *shm_pages;
struct vm_area_struct *attaches;
};
IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,
实际的删除发生在最后一个进程离开这个共享段时。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
总结:shmdt(addr)使进程中的shmaddr指针无效化,不可以使用,但是保留空间。shmctl(shmid,IPC_RMID,0) 删除共享内存,彻底不可用,释放空间。
三、示例程序
示例(book258.cpp)共享内存使用char*类型
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main()
{
int ret;
int shmid; // 共享内存标识符
// 创建共享内存,键值为0x5005,共1024字节。
ret = shmget((key_t)0x5005, 1024, 0640 | IPC_CREAT); //IPC_CREAT表示如果共享内存不存在则创建它
//ret = shmget((key_t)IPC_PRIVATE, 1024, 0640|IPC_CREAT); //IPC_CREAT表示如果共享内存不存在则创建它
if (ret == -1)
{
printf("shmget(0x5005) failed\n");
return -1;
}
shmid = ret;
char* ptext = 0; // 用于指向共享内存的指针
// 将共享内存连接到当前进程的地址空间,由ptext指针指向它
ptext = (char *)shmat(shmid, NULL, 0);
if (ptext == (void*)-1)
{
printf("shmat failed\n");
return -1;
}
// 操作本程序的ptext指针,就是操作共享内存
printf("写入前:%s\n",ptext);
sprintf(ptext,"本程序的进程号是:%d",getpid());
printf("写入后:%s\n",ptext);
// 把共享内存从当前进程中分离
shmdt(ptext);
//printf("分离后:%s\n",ptext); //段错误了!说明分离后,指针就相当于野指针了,不能再使用
/*
// 删除共享内存
ret = shmctl(shmid, IPC_RMID, NULL);
if (ret == -1)
{
printf("shmctl(0x5005) failed\n");
return -1;
}
*/
//printf("不分离并删除共享内存后:%s\n",ptext); //不分离并删除共享内存后:本程序的进程号是:40905 //IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
编译运行结果:
[root@ubuntu /arnold_test/test]54# ./a.out
写入前:本程序的进程号是:41349
写入后:本程序的进程号是:41420
[root@ubuntu /arnold_test/test]55#
[root@ubuntu /arnold_test/test]55# ./a.out
写入前:本程序的进程号是:41420
写入后:本程序的进程号是:41421
[root@ubuntu /arnold_test/test]56#
[root@ubuntu /arnold_test/test]56# ./a.out
写入前:本程序的进程号是:41421
写入后:本程序的进程号是:41422
[root@ubuntu /arnold_test/test]57#
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
注意,程序第一次运行的时候,共享内存未创建,所以第一次运行程序的时候,共享内存中的内容为空。之后程序每次运行都能获取到上一次程序运行写入的内容。
共享内存使用 struct* 类型
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
typedef struct _st_girl
{
char name[21];
int age;
}st_girl;
int main()
{
int ret;
int shmid; // 共享内存标识符
// 创建共享内存,键值为0x5005,共1024字节。
ret = shmget((key_t)0x5005, sizeof(st_girl)*2, 0640 | IPC_CREAT); //IPC_CREAT表示如果共享内存不存在则创建它
//ret = shmget((key_t)IPC_PRIVATE, 1024, 0640|IPC_CREAT); //IPC_CREAT表示如果共享内存不存在则创建它
if (ret == -1)
{
printf("shmget(0x5005) failed\n");
return -1;
}
shmid = ret;
//char* ptext = 0; // 用于指向共享内存的指针
st_girl* ptext = NULL;
// 将共享内存连接到当前进程的地址空间,由ptext指针指向它
//ptext = (char *)shmat(shmid, NULL, 0);
ptext = (st_girl*)shmat(shmid, NULL, 0);
if (ptext == (void*)-1)
{
printf("shmat failed\n");
return -1;
}
// 操作本程序的ptext指针,就是操作共享内存
printf("写入前:[%s, %d]\n",ptext->name, ptext->age);
printf("写入前:[%s, %d]\n",(ptext+1)->name, (ptext+1)->age);
//memcpy(ptext->name, "春哥",size_of(ptext->name));
sprintf(ptext->name, "春哥[%d]", getpid());
ptext->age = 23;
sprintf((ptext+1)->name, "韵哥[%d]", getpid());
(ptext+1)->age = 21;
printf("写入后:[%s, %d]\n",ptext->name, ptext->age);
printf("写入后:[%s, %d]\n",(ptext+1)->name, (ptext+1)->age);
// 把共享内存从当前进程中分离
shmdt(ptext);
//printf("分离后:%s\n",ptext); //段错误了!说明分离后,指针就相当于野指针了,不能再使用
/*
// 删除共享内存
ret = shmctl(shmid, IPC_RMID, NULL);
if (ret == -1)
{
printf("shmctl(0x5005) failed\n");
return -1;
}
*/
//printf("不分离并删除共享内存后:%s\n",ptext); //不分离并删除共享内存后:本程序的进程号是:40905 //IPC_RMID 命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生在最后一个进程离开这个共享段时。
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
编译运行结果:
四、其它的操作命令
用ipcs -m
可以查看系统的共享内存,内容有键值(key),共享内存编号(shmid),创建者(owner),权限(perms),大小(bytes)。
(下面红框框住的是我刚刚在程序中用指定键值和自动分配两种方式创建的)
用ipcrm -m
共享内存编号,可以手工删除共享内存,如下:
[root@ubuntu /arnold_test/test]58# ipcrm -m 30113804
[root@ubuntu /arnold_test/test]59#
[root@ubuntu /arnold_test/test]59# ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 连接数 状态
0x00000000 294912 yg 600 524288 2 目标
0x00000000 1245185 yg 600 67108864 2 目标
0x00000000 491522 yg 600 524288 2 目标
0x00000000 589827 yg 600 16777216 2
0x00000000 753668 yg 600 524288 2 目标
0x00000000 917509 yg 600 524288 2 目标
0x00000000 1015814 yg 600 524288 2 目标
0x00000000 1343495 yg 600 524288 2 目标
0x00000000 1146888 yg 600 524288 2 目标
0x00000000 1572873 yg 600 524288 2 目标
0x00000000 1671178 yg 600 524288 2 目标
0x00000000 1703947 yg 600 30240 2 目标
0x00000000 28377101 yg 600 115500 2 目标
0x00000000 20938766 yg 600 524288 2 目标
0x00000000 22544399 root 640 1024 0
[root@ubuntu /arnold_test/test]60# ipcrm -m 22544399
[root@ubuntu /arnold_test/test]61#
[root@ubuntu /arnold_test/test]61# ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 连接数 状态
0x00000000 294912 yg 600 524288 2 目标
0x00000000 1245185 yg 600 67108864 2 目标
0x00000000 491522 yg 600 524288 2 目标
0x00000000 589827 yg 600 16777216 2
0x00000000 753668 yg 600 524288 2 目标
0x00000000 917509 yg 600 524288 2 目标
0x00000000 1015814 yg 600 524288 2 目标
0x00000000 1343495 yg 600 524288 2 目标
0x00000000 1146888 yg 600 524288 2 目标
0x00000000 1572873 yg 600 524288 2 目标
0x00000000 1671178 yg 600 524288 2 目标
0x00000000 1703947 yg 600 30240 2 目标
0x00000000 28377101 yg 600 115500 2 目标
0x00000000 20938766 yg 600 524288 2 目标
[root@ubuntu /arnold_test/test]62#
[root@ubuntu /arnold_test/test]62#
=================================================
参考:
来自:https://cloud.tencent.com/developer/article/1998058
来自:https://blog.csdn.net/Dontla/article/details/126412876
标签:共享内存,映射,int,内存,linux,进程,include From: https://www.cnblogs.com/rebrobot/p/18264877