进程间通信之共享内存及其shm函数的使用【Linux】

简介: 进程间通信之共享内存及其shm函数的使用【Linux】


什么是共享内存

  共享内存区是最快的IPC(Inter-Process Communication,进程间通信)形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据.

  匿名管道和命名管道都需要通过系统调用接口来实现进程间通信,因为管道属于文件,属于内核中的一种特殊数据结构,由OSweihu,所以只能使用系统调用。而共享内存存在堆栈之间,可以直接读写。双方进程若要进行通信,直接进行内存级的读写即可。

从下图可以更加直观的看到在内核照中的运行方式:

  • 共享内存的提供者是操作系统。
  • 共享内存缺乏访问控制,用户(进程)双方都不会影响对方,甚至不知道对方的存在,容易造成接受的数据是不完整的。
  • 共享内存 = 共享内存块 + 对应的共享内存的内核数据结构

共享内存的内核数据结构

如何实现共享内存

共享内存函数

shmget函数

功能:用来创建共享内存

原型: int shmget(key_t key, size_t size, int shmflg);

参数:

key:这个共享内存段名字

size:共享内存大小

shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

  参数key要保证在系统中的唯一性,key值相同,就是同一块共享内存,保证key的唯一性可以使用ftok函数实现:

ftok函数

所需要的头文件:

#include <sys/types.h>

#include <sys/ipc.h>

函数原型:

key_t ftok(const char *pathname, int proj_id);

参数:

pathname:指定的文件,此文件必须存在且可存取

proj_id:计划代号(project ID),可以在[0, 255]中随机取一个数字。

函数返回值

  • 成功:返回key_t值(即IPC 键值)
  • 出错:-1,错误原因存于error中
    ftok函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键(也就是shmget函数中需要的key值)。

  注意:如果在使用ftok的这段时间里,pathname指向的文件或者目录被删除而且又重新创建,ftok是能够成功返回的,但是由于inode可能不同了,返回值也就不同于所希望的值,由于key值相同,使用同一块共享内存,这时候就可能会导致key值不同,双方无法共享一块内存,所以这个pathname一定要是一个不会被修改的文件路径。


  对于shmget的第二个参数size,代表这个共享内存的大小,最好是页(PAGE:4096byte)的整数倍,(OS和磁盘进行I/O操作的基本单位也是4096byte),如果设置为4097byte,虽然只是多了一个字节,但是系统底层可能会创建4096 * 2的空间,即使这样,也只能使用自己申请的大小(4097byte),不能使用4096*2的空间。

第三个参数shmflg:

  • 该参数用于确定共享内存属性。
  • 使用上为:标志位 | 内存权限
  • 标志位参数有两种:IPC_CREAT、IPC_EXCL
  • 使用TPC_CREAT | 0666(内存权限):创建共享内存若底层存在,就获取,并返回,若不存在,就新建,并返回。
  • 使用TPC_CREAT | IPC_EXCL | 0666(内存权限):创建共享内存若底层存在,则出错返回。若不存在,则新建,并返回。所以返回的一定是一个全新的shm.

shmat函数

功能:将共享内存段连接到进程地址空间

原型: void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

shmid: 共享内存标识

shmaddr:指定连接的地址

shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY

返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

使用这个函数建立物理内存和虚拟内存的映射。

shmaddr设置为nullptr表示让内核自己确定位置。

shmflg设置为0为默认方式。

使用shmat函数可以简单的这样:

char* shmaddr = (char*)shmat(shmid, nullptr, 0);

  • shmaddrNULL,核心自动选择一个地址
  • shmaddr不为NULLshmflgSHM_RND标记,则以shmaddr为连接地址。
  • shmaddr不为NULLshmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -(shmaddr % SHMLBA)
  • shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离

原型:int shmdt(const void *shmaddr);

参数: shmaddr: 由shmat所返回的指针

返回值:成功返回0;失败返回-1

注意:将共享内存段与当前进程脱离不等于删除共享内存段功能

shmctl函数

功能:用于控制共享内存

原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

shmid:由shmget返回的共享内存标识码

cmd:将要采取的动作(有三个可取值)

buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:成功返回0;失败返回-1

 cmd的三个命令:

命令 说明
IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID 删除共享内存段

shmctl常用IPC_RMID,用于删除共享内存空间。

代码实现

//server.cpp
// 由server再共享内存里面读取信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <cstdlib>
const int SIZE = 4094;
int main()
{
    key_t key = ftok(".", 113);
    if (key < 0)
    {
        perror("ftok");
        exit(1);
    }
    int shmid = shmget(key, SIZE, IPC_CREAT | 0666);
    if (shmid < 0)
    {
        perror("shmget");
        exit(2);
    }
    char *addr = (char *)shmat(shmid, NULL, 0);
    int i = 0;
    while (i < 20)
    {
        i++;
        printf("client# %s\n", addr);
        fflush(stdout);
        memset(addr, 0, sizeof(addr)); // 每次写入都清空,保证共享内存中没有上一次的数据残留。
        sleep(1);
    }
    shmdt(addr);
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}
// client.cpp
// 由client再共享内存里面发送信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <cstdlib>
const int SIZE = 4094;
int main()
{
    key_t key = ftok(".", 113);
    if (key < 0)
    {
        perror("ftok");
        exit(1);
    }
    int shmid = shmget(key, SIZE, IPC_CREAT | 0666);
    if (shmid < 0)
    {
        perror("shmget");
        exit(2);
    }
    char *addr = (char *)shmat(shmid, NULL, 0);
    int i = 0;
    while (i < 15)
    {
        fgets(addr, 100, stdin);
        fflush(stdin);
        sleep(1);
        i++;
    }
    shmdt(addr);
    return 0;
}
// makeflie
.PHONY:all
all:server client
server:server.cpp
  g++ -o $@ $^
client:client.cpp
  g++ -o $@ $^
.PHONY:clean
clean:
  rm -f server client

  上面的代码是实现一个由客户端进行在终端上进行写入信息到共享内存,服务端从共享内存读出到终端打印到屏幕上的一个过程。


    😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

  • List item
相关文章
|
4月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
539 14
|
4月前
|
缓存 Linux
linux 手动释放内存
在 Linux 系统中,内存管理通常自动处理,但业务繁忙时缓存占用过多可能导致内存不足,影响性能。此时可在业务闲时手动释放内存。
206 17
|
2月前
|
监控 Linux Python
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
130 27
|
2月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
3月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
167 48
|
2月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
123 5
|
3月前
|
缓存 NoSQL Linux
Linux系统内存使用优化技巧
交换空间(Swap)的优化 禁用 Swap sudo swapoff -a 作用:这个命令会禁用系统中所有的 Swap 空间。swapoff 命令用于关闭 Swap 空间,-a 参数表示关闭 /etc/fstab 文件中配置的所有 Swap 空间。 使用场景:在高性能应用场景下,比如数据库服务器或高性能计算服务器,禁用 Swap 可以减少磁盘 I/O,提高系统性能。
106 3
|
3月前
|
缓存 Linux
Linux查看内存命令
1. free free命令是最常用的查看内存使用情况的命令。它显示系统的总内存、已使用内存、空闲内存和交换内存的总量。 free -h • -h 选项:以易读的格式(如GB、MB)显示内存大小。 输出示例: total used free shared buff/cache available Mem: 15Gi 4.7Gi 4.1Gi 288Mi 6.6Gi 9.9Gi Swap: 2.0Gi 0B 2.0Gi • to
128 2
|
4月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
317 20
|
5月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
211 13