首页 > 系统相关 >linux 进程间通信 --- 共享内存(POSIX 版本)

linux 进程间通信 --- 共享内存(POSIX 版本)

时间:2023-12-17 18:12:25浏览次数:33  
标签:--- include int mmap 间通信 fd POSIX 共享内存 shm

POSIX 进程间通信

POSIX 进程间通信 (Interprocess Communication, IPC) 是 System V 进程间通信的变体。它是在 Solaris 7 发行版中引入的。与 System V 对象类似,POSIX IPC 对象的属主、属主的组以及其他用户具有读取和写入权限,但是没有执行权限。POSIX IPC 对象的属主无法将对象分配给其他属主。POSIX IPC 包括以下功能:

  • 消息允许进程将已格式化的数据流发送到任意进程。

  • 信号量允许进程同步执行。

  • 共享内存允许进程共享其部分虚拟地址空间。

与 System V IPC 接口不同,POSIX IPC 接口均为多线程安全接口。

POSIX 共享内存涉及的内存映射函数

mmap 函数

mmap函数把一个文件或一个posix共享内存对象映射到调用进程的地址空间。

//成功返回映射内存的起始地址,失败返回MAP_FAILED
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

mmap参数解析:

  • addr指定映射内存的起始地址,通常设为NULL,让内核自己决定起始地址
  • len是被映射到调用进程地址空间中的字节数,它从被映射文件fd开头起第offset个字节处开始算,offset通常设为0,下图展示了这个映射关系
  • prot指定对映射内存区的保护,通常设为PROT_READ | PROT_WRITE
  • flags必须在MAP_SHAREDMAP_PRIVATE这两个标志中选择指定一个,进程间共享内存需要使用MAP_SHARED
  • 可移植的代码应把addr设为NULL,并且flags不指定MAP_FIXED

 

prot说明flags说明
PROT_READ 数据可读 MAP_SHARED 变动是共享的
PROT_WRITE 数据可写 MAP_PRIVATE 变动是私有的
PROT_EXEC 数据可执行 MAP_FIXED 准确地解释addr参数
PROT_NONE 数据不可访问  

 mmap成功返回后,可以关闭fd,这对已建立的映射关系没有影响。
注意,不是所有文件都能进行内存映射,例如终端和套接字就不可以。

munmap 函数

 mmap建立的映射关系通过munmap删除,其中addr是mmap返回的地址,len是映射区的大小,同mmap的参数len。

msync 函数

默认情况下,内核采用虚拟内存算法保持内存映射文件与内存映射区的同步,前提是指定了MAP_SHARED标志,但这种同步可能不是立即生效的,而是在随后某个时间进行。
但有时候我们修改完数据并进行下一步操作之前,需要确认数据已经同步完成,这时可调用msync函数。

//成功返回0,失败返回-1
int msync(void *addr, size_t len, int flags);

 

其中addr和len含义同munmap,flags使用下表中的常值,其中MS_ASYNCMS_SYNC这两个常值中必须选择指定一个。

flags说明
MS_ASYNC 执行异步写,msync立即返回
MS_SYNC 执行同步写,msync等同步完成才返回
MS_INVALIDATE 使高速缓存的数据失效

POSIX 共享内存其他相关函数

shm_open和shm_unlink函数

shm_open用于创建一个新的posix共享内存对象或打开一个已存在的Posix共享内存对象。
shm_unlink用于从系统中删除一个posix共享内存对象。

//成功返回非负描述符,失败返回-1
int shm_open(const char *name, int oflag, mode_t mode);

//成功返回0,失败返回-1
int shun_unlink(const char *name);

shm_open参数说明:

  • oflag参数不能设置O_WRONLY标志
  • 和mq_open、sem_open不同,shm_open的mode参数总是必须指定,当指定了O_CREAT标志时,mode为用户权限位,否则将mode设为0

shm_open的返回值是一个描述符,它随后用作mmap的第五个参数fd。

ftruncate和fstat函数

处理mmap的时候,普通文件或Posix共享内存对象的大小都可以通过调用ftruncate设置。

#include <unistd.h>

//成功返回0,失败返回-1
int ftruncate(int fd, off_t length):
  • 对于普通文件,若文件长度大于length,额外的数据会被丢弃;若文件长度小于length,则扩展文件大小到length
  • 对于Posix共享内存对象,ftruncate把该对象的大小设置成length字节

我们调用ftruncate来指定新创建的Posix共享内存对象大小,或者修改已存在的Posix共享内存对象大小。

  • 创建新的Posix共享内存对象时指定大小是必须的,否则访问mmap返回的地址会报bus error错误
  • 当打开一个已存在的Posix共享内存对象时,可以调用fstat来获取该对象的信息
#include <sys/stat.h>
#include <sys/types.h>

//成功返回0,失败返回-1
int fstat(int fd, struct stat *buf);

stat结构有12个或以上的成员,然而当fd指代一个Posix共享内存对象时,只有四个成员含有信息:

struct stat
{
    mode_t st_mode;  //用户访问权限
    uid_t  st_uid;   //user id of owner
    gid_t  st_gid;   //group id of owner
    off_t  st_size;  //文件大小
};

POSIX 共享内存示例代码

示例代码包括一个.h文件和两个.c文件:

  • common.h定义server和client共同使用的信息
  • server.c创建并初始化Posix共享内存对象,以及同步需要的信号量
  • client.c打开Posix共享内存对象,然后给共享内存中的计数器加1

例子1:进程同步使用posix有名信号量

common.h

#ifndef _COMMON_H_
#define _COMMON_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/mman.h>

#define SHM_FILE    "/shm_file"
#define SEM_PATH    "/sem_mmap"

struct Shared
{
    int count;
};

#endif

server.c

#include "common.h"

int main()
{
    struct Shared *ptr;
    sem_t *mutex;
    int fd;

    shm_unlink(SHM_FILE);
    fd = shm_open(SHM_FILE, O_RDWR | O_CREAT, 0666);
    ftruncate(fd, sizeof(struct Shared));
    ptr = mmap(NULL, sizeof(struct Shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    sem_unlink(SEM_PATH);
    mutex = sem_open(SEM_PATH, O_CREAT, 0666, 1);
    sem_close(mutex);

    pause();

    return 0;
}

client.c

#include "common.h"

int main(int argc, char **argv)
{
    struct Shared *ptr;
    struct stat buf;
    sem_t *mutex;
    int fd;
    int nloop;
    int i;

    fd = shm_open(SHM_FILE, O_RDWR, 0);
    fstat(fd, &buf);
    ptr = mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    mutex = sem_open(SEM_PATH, 0);
    nloop = atoi(argv[1]);

    for (i = 0; i < nloop; i++)
    {
        sem_wait(mutex);
        printf("pid %d: %d\n", getpid(), ptr->count++);
        sem_post(mutex);
    }

    return 0;
}

编译并启动server,阻塞在pasue()中。

 后台同时运行三个client进程。

 截取进程切换时的部分输出片段,可以看到切换后计数依然是连续的。

例子2:进程同步使用posix无名信号量

common.h

#ifndef _COMMON_H_
#define _COMMON_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/mman.h>

#define SHM_FILE    "/shm_file"

struct Shared
{
    sem_t mutex;
    int count;
};

#endif

server.c

#include "common.h"

int main()
{
    struct Shared *ptr;
    int fd;

    shm_unlink(SHM_FILE);
    fd = shm_open(SHM_FILE, O_RDWR | O_CREAT, 0666);
    ftruncate(fd, sizeof(struct Shared));
    ptr = mmap(NULL, sizeof(struct Shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    sem_init(&ptr->mutex, 1, 1);

    pause();

    return 0;
}

client.c

#include "common.h"

int main(int argc, char **argv)
{
    struct Shared *ptr;
    struct stat buf;
    int fd;
    int nloop;
    int i;

    fd = shm_open(SHM_FILE, O_RDWR, 0);
    fstat(fd, &buf);
    ptr = mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    //共享内存中的mutex已由server初始化,client直接使用就可以了,不能重复初始化
    nloop = atoi(argv[1]);

    for (i = 0; i < nloop; i++)
    {
        sem_wait(&ptr->mutex);
        printf("pid %d: %d\n", getpid(), ptr->count++);
        sem_post(&ptr->mutex);
    }

    return 0;
}

输出同例子1。

 

标签:---,include,int,mmap,间通信,fd,POSIX,共享内存,shm
From: https://www.cnblogs.com/god-of-death/p/17909480.html

相关文章

  • 2023-2024-1 学号20231315第十二周学习总结
    学期:2023-2024-1学号:20231315《计算机基础与程序设计》第十二周学习总结作业信息这个作业属于哪个课程2023-2024-1《计算机基础与程序设计》这个作业要求在哪里2023-2024-1《计算机基础与程序设计》这个作业的目标学习《C语言程序设计》第11章作业正文http......
  • Mybatis-Plus技术教程
    Mybatis-Plus课程目标了解Mybatis-Plus整合Mybatis-Plus通用CRUDMybatis-Plus的配置条件构造器Mybatis-Plus的Service封装代码生成器1Mybatis-Plus介绍1.1Mybatis-Plus介绍MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强......
  • 无涯教程-Java - String substring(int beginIndex)函数
    从beginIndex索引处开始截取字符串。Stringsubstring-语法publicStringsubstring(intbeginIndex)这是参数的详细信息-beginIndex  -  包含开始索引。Stringsubstring-返回值指定的子字符串。Stringsubstring-示例importjava.io.*;publicclassTest......
  • 《Java编程思想第四版》学习笔记48--关于Runnable
    现在run()位于类内,但它在init()结束以后仍处在“睡眠”状态。若按下启动按钮,线程便会用多少有些暧昧的表达方式创建(若线程尚不存在):newThread(Counter3.this);若某样东西有一个Runnable接口,实际只是意味着它有一个run()方法,但不存在与之相关的任何特殊东西——它不具有任何天......
  • WorkPlus即时通讯app-私有化部署的最佳解决方案
    随着数字化时代的到来,企业在业务发展和沟通协作方面面临着前所未有的挑战。传统的通讯工具无法满足安全、高效、全面掌控业务和生态的需求。而在这个背景下,WorkPlus作为安全专属的移动数字化平台崭露头角,成为企业实现全面业务掌控的最佳选择。WorkPlus不仅仅是一款移动应用,它更像一......
  • 238-hover 、active、focus伪类
    :hover伪类用于选择鼠标悬停在元素上的状态:active伪类表示元素处于激活状态,通常是在用户点击并按住鼠标按钮时触发。这一状态通常在点击瞬间发生,持续到鼠标按钮释放。:focus伪类表示元素获得焦点,通常是通过键盘(Tab键)或者其他方式进行焦点切换时触发。对于可点击元素,点击时也......
  • 视野修炼-技术周刊第66期
    ......
  • 2023-12-17 每天一练
    LeetCode每日一题746.使用最小花费爬楼梯问题给你一个整数数组cost,其中cost[i]是从楼梯第i个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为0或下标为1的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费......
  • 数据库版本历史的总结-非信创部分
    数据库版本历史的总结-非信创部分OracleOracle数据库是最悠久的关系型数据库.诞生于美国军方的管理项目他的第一个版本是Oracle2上世纪八九十年代的Oracle8和Oracle9是非常成功的版本.进入21世纪后Oracle发布了三个大版本Oracle10goracle11goracle12c(Oracle9......
  • 无涯教程-Java - String substring(int beginIndex, int endIndex)函数
    截取beginIndex索引开始到endIndex结束之间的字符串内容。Stringsubstring-语法这是此方法的语法-publicStringsubstring(intbeginIndex,intendIndex)这是参数的详细信息-beginIndex - 包含开始索引。endIndex   - 不包含结束索引。Stringsubstri......