进程通信——共享内存

简介: 进程通信——共享内存

背景


在linux系统开发当中,存在多种方案可以实现进程之间的通信。但最高效的方法莫过于共享内存。但是共享内存传输的数据尽量不要太大。


原理


linux操作系统共享内存是借助tmpfs这个文件系统来实现的,tmpfs文件系的目录为/dev/shm,/dev/shm是驻留在内存 RAM 当中的,因此读写速度与读写内存速度一样,/dev/shm的容量默认尺寸为系统内存大小的一半大小,使用df -h命令可以看到。但实际上它并不会真正的占用这块内存,如果/dev/shm/下没有任何文件,它占用的内存实际上就是0字节,仅在使用shm_open文件时,/dev/shm才会真正占用内存。


共享内存接口

/*创建共享内存文件*/
int shm_open(const char *name, int oflag, mode_t mode);
/*实现文件映射到内存*/
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
/*关闭文件映射*/
int munmap(void *addr, size_t length);
/*删除共享内存文件*/
int shm_unlink(const char *name);
/*重置文件大小*/
int ftruncate(int fd, off_t length)


创建接口

int shm_open(const char *name, int oflag, mode_t mode);

功能说明:shm_open()用于创建或者打开共享内存文件。shm_open()函数的使用和open 使用很像。 区别之处是shm_open()操作的文件一定是位于tmpfs文件系统里的,常见的Linux发布版的tmpfs文件系统的存放目录就是/dev/shm。


返回值: 成功返回fd>0, 失败返回-1


参数说明:


name:要打开或创建的共享内存文件名,由于shm_open()打开或操作的文件都是位于/dev/shm目录的,因此name最好不能带路径。例如:/var/myshare 这样的名称是错误的,而 myshare 是正确的,因为 myshare 不带任何路径。如果你一定要在name添加路径,那么,请在/dev/shm目录里创建一个目录,例如,如果你想创建一个 bill/myshare 的共享内存文件,那么请先在/dev/shm目录里创建 bill这个子目录,由于不同厂家发布的linux系统的tmpfs的位置也许不是/dev/shm,因此带路径的名称也许在别的环境下打开不成功。


oflag:打开的文件操作属性:O_CREAT、O_RDWR、O_EXCL的按位或运算组合。


mode:文件共享模式,例如 0777


映射接口

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

功能说明: 将打开的文件映射到内存,一般是将shm_open打开的文件映射到内存,当然也可以将硬盘上的用open函数打开的文件映射到内存。这个函数只是将文件映射到内存中,使得我们用操作内存指针的方式来操作文件数据。


参数说明:


addr:要将文件映射到的内存地址,一般应该传递NULL来由Linux内核指定。


length:要映射的文件数据长度。


prot:映射的内存区域的操作权限(保护属性),包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE


flags:标志位参数,包括:MAP_SHARED、MAP_PRIVATE与MAP_ANONYMOUS。

MAP_SHARED: 建立共享,用于进程间通信,如果没有这个标志,则别的进程即使能打开文件,也看不到数据。

MAP_PRIVATE: 只有进程自己用的内存区域

MAP_ANONYMOUS:匿名映射区


fd: 用来建立映射区的文件描述符,用 shm_open打开或者open打开的文件。


offset:映射文件相对于文件头的偏移位置,应该按4096字节对齐。默认输入0即可


返回值:成功返回映射的内存地址指针,可以用这个地址指针对映射的文件内容进行读写操作,读写文件数据如同操作内存一样;如果 失败则返回NULL。


取消内存映射

int munmap(void *addr, size_t length);

功能说明: 取消内存映射,addr是由mmap成功返回的地址,length是要取消的内存长度,munmap 只是将映射的内存从进程的地址空间撤销,如果不调用这个函数,则在进程终止前,该片区域将得不到释放。


删除文件

int shm_unlink(const char *name);

功能说明:删除/dev/shm目录的文件,shm_unlink 删除的文件是由shm_open函数创建于/dev/shm目录的。可以用系统函数unlink来达到同样的效果,用/dev/shm + name 组成完整的路径即可,但一般不要这么做,因为系统的tmpfs的位置也许不是/dev/shm。用shm_open 创建的文件,如果不调用此函数删除,会一直存在于/dev/shm目录里,直到操作系统重启或者调用linux命令rm来删除为止。


重置文件大小

int ftruncate(int fd, off_t length);

功能说明:重置文件大小。任何open打开的文件都可以用这个函数,不限于shm_open打开的文件。


编译


头文件
       #include <sys/mman.h>
       #include <sys/stat.h>        /* For mode constants */
       #include <fcntl.h>           /* For O_* constants */
编译
    Link with -lrt.


例子


下面代码是简单例子 还能够优化

/**
 * @file shm_send.c
 * @author Fischer (2092086704@qq.com)
 * @brief 创建两个进程,通过send进程调用recv进程,并把相应的信息传入到recv进程。
 *  进程之间通信采用share memory.
 * @version 0.1
 * @date 2021-12-03
 * 
 * @copyright Copyright (c) 2021
 * 
 */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "shm_com.h"
#define MMAP_DATA_SIZE 1024
int main (int argc, char **argv) {
    /*1.获取共享内存的名字*/
    unsigned char shm_name [256];
    struct timeval tv;
    //struct timezone tz;
    gettimeofday(&tv, NULL);
    sprintf(shm_name, "%d", tv.tv_sec/tv.tv_usec + tv.tv_sec%tv.tv_usec);
    int oflag = O_RDWR | O_CREAT | O_EXCL;
    mode_t mode = 0755;
    int shmfd = shm_open(shm_name, oflag, mode);
    if (shmfd < 0) {
        printf("send shm_open error!\n");
        return -1;
    }
    /*调整shm 重置文件大小*/
    int ret = ftruncate(shmfd, MMAP_DATA_SIZE);
    if (ret < 0) {
        printf("The reset the file size error!\n");
        return -1;
    }
    /**构建文件映射 */
    int prot = PROT_READ|PROT_WRITE;
    int flags = MAP_SHARED;
    shm_info_t * shm_share_info = (shm_info_t *)mmap(NULL, MMAP_DATA_SIZE, prot, flags, shmfd, 0 );
    if ( shm_share_info == NULL) {
        printf("send the file map to memory faild!\n");
        return -1;
    }
    char * shmbuf = "This is hello!";
    strcpy(shm_share_info->identify, "sendtorecv");
    strcpy(shm_share_info->shm_data, shmbuf);
    shm_share_info->shm_length = strlen(shmbuf);
    printf("send shm_name :%s\n",shm_name);
    printf("send shm_identfy :%s shm_data :%s length:%u \n", shm_share_info->identify, shm_share_info->shm_data, shm_share_info->shm_length);
    /* put info to shm**/
    char *args[3] = {NULL};
    args[0] = "./shm_recv";
    args[1] = shm_name;
    args[2] = NULL;
    if (execv("./shm_recv", args)) {
        printf(" execv failed\n");
        return -1;
    }
    close(shmfd);
    /** 取消文件映射*/
    int retm = munmap(shm_share_info, MMAP_DATA_SIZE);
    if (retm < 0) {
        printf("send Unmap memory faild!\n");
        return -1;
    }
    /**删除sham_open 创造出的文件*/
    int retu = shm_unlink(shm_name);
    if (retu < 0) {
        printf("send delete share memeory file!\n");
        return -1;
    }
    return 0;
}

/**
 * @file shm_recv.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2021-12-03
 * 
 * @copyright Copyright (c) 2021
 * 
 */
#include "shm_com.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#define MMAP_DATA_SIZE 1024
int main(int argc ,char **argv) {
    printf("The shm_recv running!\n");
    if (argc != 2) {
        printf("argc should be 2, but it's %d",argc);
    }
    printf("argv[1]: %s\n",argv[1]);
    char shm_name [32] = {0};
    strcpy(shm_name, argv[1]);
    //printf("shm_name : %s -- %s\n",shm_name, argv[1] );
    /***/
    int oflags = O_RDWR;
    mode_t mode = 0755;
    int shmfd = shm_open(argv[1], oflags, mode);
    if (shmfd < 0) {
        printf("recv shm_open is faild!\n");
        return -1;
    }
    int prot = PROT_READ|PROT_WRITE;
    int flags = MAP_SHARED;
    shm_info_t * shmbuf = (shm_info_t *)mmap(NULL, MMAP_DATA_SIZE, prot, flags, shmfd, 0 );
    if (shmbuf == NULL) {
        printf("recv shmbuf is faild !\n");
        return -1;
    }
    printf("shm_identfy: %s  shm_length: %u shm_data:%s \n",shmbuf->identify, shmbuf->shm_length, shmbuf->shm_data);
    int ret = munmap(shmbuf, MMAP_DATA_SIZE);
    if (ret < 0) {
        printf("recv unmap memeory !\n");
        return -1;
    }
    close (shmfd);
    int retu = shm_unlink(shm_name);
    if (retu < 0)
    {
        printf("recv unlink shm_open file!\n");
        return  -1;
    }
    return 0;
}

/**
 * @file shm_com.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2021-12-03
 * 
 * @copyright Copyright (c) 2021
 * 
 */
/*This the file is the shm_send and shm_recv shared*/
#ifndef __SHM_COM_H__
#define __SHM_COM_H__
#define SHM_MAX_LENGTH     (1024 -1)
#define SHM_MAX_INDENTIFY  (32 - 1)
typedef struct shm_info_s  shm_info_t;   /**< 重命名结构体*/
struct shm_info_s {
    unsigned int shm_length;
    unsigned char shm_data[SHM_MAX_LENGTH];
    unsigned char identify[SHM_MAX_INDENTIFY];
};
#endif

 1 #!/bin/bash
  2
  3 gcc -o shm_send shm_send.c -lrt
  4
  5 gcc -o shm_recv shm_recv.c -lrt

目录
相关文章
|
2月前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
248 2
|
3月前
|
存储 Linux 调度
深入理解操作系统:从进程管理到内存分配
【8月更文挑战第44天】本文将带你深入操作系统的核心,探索其背后的原理和机制。我们将从进程管理开始,理解如何创建、调度和管理进程。然后,我们将探讨内存分配,了解操作系统如何管理计算机的内存资源。最后,我们将通过一些代码示例,展示这些概念是如何在实际操作系统中实现的。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
|
12天前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
11天前
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
21天前
|
算法 调度 开发者
深入理解操作系统:从进程管理到内存分配
本文旨在为读者提供一个深入浅出的操作系统知识之旅,从进程管理的基础概念出发,探索内存分配的策略与技巧。我们将通过实际代码示例,揭示操作系统背后的逻辑与奥秘,帮助读者构建起对操作系统工作原理的直观理解。文章不仅涵盖理论知识,还提供实践操作的指导,使读者能够将抽象的概念转化为具体的技能。无论你是初学者还是有一定基础的开发者,都能在这篇文章中找到有价值的信息和启发。
|
25天前
|
算法 调度 C++
深入理解操作系统:从进程管理到内存分配
【10月更文挑战第42天】本文将带你进入操作系统的神秘世界,探索其核心概念和关键技术。我们将从进程管理开始,了解操作系统如何协调和管理多个程序的运行;然后,我们将深入研究内存分配,看看操作系统如何有效地分配和管理计算机的内存资源。通过这篇文章,你将获得对操作系统工作原理的深入理解,并学会如何编写高效的代码来利用这些原理。
|
26天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
1月前
|
存储 Unix Linux
进程间通信方式-----管道通信
【10月更文挑战第29天】管道通信是一种重要的进程间通信机制,它为进程间的数据传输和同步提供了一种简单有效的方法。通过合理地使用管道通信,可以实现不同进程之间的协作,提高系统的整体性能和效率。
|
1月前
|
消息中间件 存储 供应链
进程间通信方式-----消息队列通信
【10月更文挑战第29天】消息队列通信是一种强大而灵活的进程间通信机制,它通过异步通信、解耦和缓冲等特性,为分布式系统和多进程应用提供了高效的通信方式。在实际应用中,需要根据具体的需求和场景,合理地选择和使用消息队列,以充分发挥其优势,同时注意其可能带来的复杂性和性能开销等问题。
|
2月前
|
缓存 算法 调度
深入浅出操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅。我们将从进程管理的基本概念出发,逐步深入到内存管理的复杂世界,最终探索如何通过实践技巧来优化系统性能。文章将结合理论与实践,通过代码示例,帮助读者更好地理解操作系统的核心机制及其在日常技术工作中的重要性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往操作系统深层次理解的大门。

热门文章

最新文章

相关实验场景

更多