首页 > 系统相关 >linux 内存映射 与 内存共享

linux 内存映射 与 内存共享

时间:2024-06-24 13:42:35浏览次数:3  
标签:共享内存 映射 int 内存 linux 进程 include

 

一,内存映射

对于磁盘文件和进程:

将一个文件或其它对象映射到进程地址空间,实现文件在磁盘的存储地址和进程地址空间中一段虚拟地址的映射关系。有了这样的映射,进程利用指针直接读写虚拟地址就可以完成对文件的读写操作。这样可以避免进行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:共享内存

参考文章2:信号量

参考文章3:shmdt() 与 shmctl() 的区别?

文章目录

 

共享内存

一、共享内存的概念

共享内存(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

相关文章

  • SUSE linux的启动过程介绍
    引导Linux系统涉及不同的组件和任务。在固件和硬件初始化过程(取决于机器的架构)之后,内核通过引导加载程序GRUB2启动。此后,引导过程完全由操作系统控制并由systemd处理。systemd提供了一组“target”,用于为日常使用、维护或紧急情况启动配置。1术语init有两种不同的进程会......
  • linux笔记10--编辑器之神VIM
    文章目录1.简单介绍①为什么叫vim②linux常见的编辑器③注意事项④其它2.操作模式的划分①两种--国际上普通模式(命令操作模式)插入模式②三种--国内普通模式如何进入与退出界面插入模式如何进入与退出界面命令模式如何进入与退出界面常见的命令模式③......
  • linux三剑客工具使用及硬盘知识介绍
    文本处理工具,文件查找工具,文本处理三剑客,文本格式化命令(printf)的相关命令及选项,示例。文本处理工具cat:连接文件并打印到标准输出。catfile1file2tac:反向连接文件并打印到标准输出。tacfilenl:给文件的每一行添加行号。nlfilemore:分页显示文件内容。morefilele......
  • linux基础知识
    总结计算机发展相关,并且总结服务器硬件相关知识计算机发展相关计算机的发展可以分为以下几个阶段:第一代计算机(1940s-1950s):使用电子管作为主要元件。体积庞大,功耗高。主要用于科学计算和军事用途。代表:ENIAC、UNIVAC。第二代计算机(1950s-1960s):使用晶体管代替电子管。体......
  • ubuntu gitlab+frp 映射 版本管理平台搭建
    环境准备站点服务器:ubuntuserver22LST,RAM4G+(不然很可能502)个人PC:Vscode,git,xshell远程工具站点服务器云服务器,实体机子均可。再次动用家里的NAS-unraid开了一台ubuntu虚拟机,虚拟机用任意linux发行最新版本均可。搭建开始上一篇文章分享了hexo博客站点的搭......
  • linux的source命令
    用法sourcefile也可以用.+空格file来代替.file作用在当前bash环境下读取并执行FileName中的命令.source(或点)令通常用于重新执行刚修改的初始化文档,如.bash_profile和.profile等配置文件.简单的说就是:source命令会把file里的命令在当前shell里一个一个执行区别......
  • Linux收到You have mail in xxx
    当系统显示"Youhavemailin/var/spool/mail/root"提示时,一般有一些重要的邮件被发送到了root用户的邮箱,存放在/var/spool/mail/root这个文件中。这些邮件通常包含了系统关键事件的通知或报告,例如系统状态变化、错误报告、定期任务的执行结果等。原因:系统报告和通知:系......
  • Linux passwd命令:守护账户安全的密钥
    Linuxpasswd命令:守护账户安全的密钥在Linux的广阔世界中,passwd命令是每位系统管理员和用户的得力助手。它不仅是账户安全的守护者,也是数据处理和分析中不可或缺的一环。本文将带您深入了解passwd命令的方方面面,从它的基本功能到高级应用,再到使用时的注意事项和最佳实践。......
  • Linux各个中间件日志位置
    Linux各个中间件日志位置Linux系统中各种常见中间件日志文件的默认位置取决于安装时的配置以及中间件的具体类型。以下是一些常见中间件日志文件的标准位置ApacheHTTPServer:访问日志:/var/log/httpd/access_log或/var/log/apache2/access.log ......
  • Linux Vim最全面的教程
    LinuxVim是一个功能强大的文本编辑器,在Linux系统中被广泛使用。它具有很多高级特性和快捷键,可以提高编辑效率。本教程将详细介绍LinuxVim的各种功能和用法,适合初学者和有一定经验的用户。第一部分:入门指南介绍Vim和它与其他编辑器的区别安装Vim启动Vim和基本操作基本编辑命......