linux c编程:Posix共享内存区

Posix共享内存区:
共享内存是最快的可用IPC形式。它允许多个不相关(无亲缘关系)的进程去访问同一部分逻辑内存。
如果需要在两个进程之间传输数据,共享内存将是一种效率极高的解决方案。一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传输就不再涉及内核。这样就可以减少系统调用时间,提高程序效率。
共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的地址空间中。其他进程可以把同一段共享内存段“连接到”它们自己的地址空间里去。所有进程都可以访问共享内存中的地址。如果一个进程向这段共享内存写了数据,所做的改动会立刻被有访问同一段共享内存的其他进程看到。
要注意的是共享内存本身没有提供任何同步功能。也就是说,在第一个进程结束对共享内存的写操作之前,并没有什么自动功能能够预防第二个进程开始对它进行读操作。共享内存的访问同步问题必须由程序员负责。可选的同步方式有互斥锁、条件变量、读写锁、纪录锁、信号灯。
实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止。
相应的API如下:
shm_open 函数
功能:

用来创建或打开一个共享内存对象
原型:
int shm_open(const char* name,int oflag,mode_t mode);
参数:
name:共享内存对象的名字
oflag:与open 函数类似,可以是O_RDONLY,O_RDWR,还可以按位或上O_CREAT,O_EXCL,O_TRUNC等.
mode:此参数总是需要设置,如果oflag没有指定 O_CREAT, 可以指定为0;
返回值: 成功返回非负整数文件描述符;失败返回-1

ftruncate
功能:
修改共享内存对象大小
原型:
int ftruncate(int fd,off_t length);
参数:
fd:文件描述符
length:长度
返回值:
成功返回0,失败返回-1

fstat函数
功能:获取共享内存对象信息
原型:
int fstat(int fd,struct stat *buf);
参数:
fd: 文件描述符
buf: 返回共享内存状态。stat结构有12个或以上的成员,然而当fd指代一个共享内存区对象时。只有4个成员含有信息。
Struct stat
{
    mode_t  st_mode;
    uid_t      st_uid;
    gid_t      st_gid;
    off_t       st_size;
}
返回值:
成功返回0,失败返回-1

shm_unlink函数
功能:删除一个共享内存对象
原型:
int  shm_unlink(const char* name);
参数:
name:共享内存对象的名字
返回值:成功返回0,失败返回-1;
下面来看一个简单的测试程序:
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

typedef struct Content
{
    char name[32];
    unsigned char age;
}Con;

void Test1()
{
    int shmid;
    shmid=shm_open("myshm",O_CREAT | O_RDWR,0775);
    if(shmid == -1)
        printf("shmid error\n");
    if(ftruncate(shmid,sizeof(Con)) == -1)
        printf("ftruncate error\n");
    struct stat buf;
    if(fstat(shmid,&buf) == -1)
        printf("stat error\n");
    printf("size=%d,mode=%o\n",buf.st_size,buf.st_mode & 0777);
    close(shmid);
}
测试结果:可以看到共享内存区的大小 已经和Content结构体一样大,都是33字节。

共享内存区创建的文件默认保存在/dev/shm路径中

[email protected]:/dev/shm# ls -al

总用量 4

drwxrwxrwt 2 root root 80 5月 11 14:36 .

drwxr-xr-x 21 root root 4240 5月 11 14:13 ..

-rw-r----- 1 www-data www-data 4096 5月 11 14:13 mono.1538

-rwxrwxr-x 1 zhf zhf 36 5月 11 14:35 myshm

使用od -c查看myshm中的内容,可以看到全被初始化为0

[email protected]:/dev/shm# od -c myshm

0000000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

*

0000040 \0 \0 \0 \0

0000044

下面来测试往共享内存区里面写数据,在这里要用到mmap函数

mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特定地址区域的对象映射。

采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

基于文件的映射,在mmap和munmap执行过程的任何时刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情况下没有得到更新,首次对映射区的第一个页索引时会更新该字段的值。用PROT_WRITE

MAP_SHARED标志建立起来的文件映射,其st_ctime

st_mtime在对映射区写入之后,但在msync()通过MS_SYNC

MS_ASYNC两个标志调用之前会被更新。

void
*mmap(void *start, size_t length, int prot, int flags,

int
fd, off_t offset);

int
munmap(void *start, size_t length);

返回说明:

成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void
*)-1],munmap返回-1。errno被设为以下的某个值

EACCES:访问出错

EAGAIN:文件已被锁定,或者太多的内存已被锁定

EBADF:fd不是有效的文件描述词

EINVAL:一个或者多个参数无效

ENFILE:已达到系统对打开文件的限制

ENODEV:指定文件所在的文件系统不支持内存映射

ENOMEM:内存不足,或者进程已超出最大内存映射数量

EPERM:权能不足,操作不允许

ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志

SIGSEGV:试着向只读区写入

SIGBUS:试着访问不属于进程的内存区

参数:

start:映射区的开始地址。

length:映射区的长度。

prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起

PROT_EXEC
//页内容可以被执行

PROT_READ
//页内容可以被读取

PROT_WRITE
//页可以被写入

PROT_NONE
//页不可访问

flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体

MAP_FIXED
//使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。

MAP_SHARED
//与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。

MAP_PRIVATE
//建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。

MAP_DENYWRITE
//这个标志被忽略。

MAP_EXECUTABLE
//同上

MAP_NORESERVE
//不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。

MAP_LOCKED
//锁定映射区的页面,从而防止页面被交换出内存。

MAP_GROWSDOWN
//用于堆栈,告诉内核VM系统,映射区可以向下扩展。

MAP_ANONYMOUS
//匿名映射,映射区不与任何文件关联。

MAP_ANON
//MAP_ANONYMOUS的别称,不再被使用。

MAP_FILE
//兼容标志,被忽略。

MAP_32BIT
//将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。

MAP_POPULATE
//为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。

MAP_NONBLOCK
//仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。

offset:被映射对象内容的起点。

2.
系统调用munmap()

int
munmap( void * addr, size_t len )

该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。

写入测试函数:

void
Shem_write()

{

int
shmid;

unsigned
char *ptr;

struct
stat stat;

shmid=shm_open("myshm",O_CREAT
| O_RDWR,0775);

if(shmid
== -1)

printf("shmid
error\n");

if(fstat(shmid,&stat)
== -1)

printf("stat
error\n");

ptr=mmap(NULL,stat.st_size,PROT_READ
| PROT_WRITE,MAP_SHARED,shmid,0);

close(shmid);

for(int
i=0;i<stat.st_size;i++)

{

*ptr++=i;

}

printf("%c\n",*ptr);

}

查看写入的结果:

[email protected]:/dev/shm#
od -c myshm

0000000
\0 001 002 003 004 005 006 \a \b \t \n \v \f \r 016 017

0000021
020 021 022 023 024 025 026 027 030 031 032 033

共享内存区读取程序:

void
Shem_read()

{

int
i,shmid;

struct
stat stat;

unsigned
char *ptr;

shmid=shm_open("myshm",O_RDONLY,0775);

fstat(shmid,&stat);

ptr=mmap(NULL,stat.st_size,PROT_READ,MAP_SHARED,shmid,0);

close(shmid);

for(i=0;i<stat.st_size;i++)

{

printf("value=%d\n",*(ptr+i));

}

}

执行结果:

如果我们想结果转换为结构体的形式,函数可以更新如下,将ptr赋值给Con结构体

void Shem_read()

{

int i,shmid;

struct stat stat;

void *ptr;

unsigned char *tmp;

Con *c;

shmid=shm_open("myshm",O_RDONLY,0775);

fstat(shmid,&stat);

ptr=mmap(NULL,stat.st_size,PROT_READ,MAP_SHARED,shmid,0);

close(shmid);

tmp=(unsigned char *)ptr;

for(i=0;i<stat.st_size;i++)

{

printf("value=%d\n",*(tmp+i));

}

c=(Con *)ptr;

printf("age=%d",c->age);

}

可以看到age的数值等于最后一个字节的值,也就是32.

原文地址:https://www.cnblogs.com/zhanghongfeng/p/10848851.html

时间: 2024-11-06 09:17:47

linux c编程:Posix共享内存区的相关文章

Linux环境编程之共享内存区(二):Posix共享内存区

现在将共享内存区的概念扩展到将无亲缘关系进程间共享的内存区包括在内.Posix提供了两种在无亲缘关系进程间共享内存区的方法: 1.内存映射文件:由open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件.(上一节就是这种技术) 2.共享内存区对象:由shm_open打开一个Posix名字(也许是在文件系统中的一个路径名),所返回的描述符由mmap函数映射到当前进程的地址空间.(本节内容) Posix共享内存区涉及以下两个步骤要求: 1.指定一个名字参数调用shm_open

Linux环境编程之共享内存区(一):共享内存区简介

Spark生态圈,也就是BDAS(伯克利数据分析栈),是伯克利APMLab实验室精心打造的,力图在算法(Algorithms).机器(Machines).人(People)之间通过大规模集成,来展现大数据应用的一个平台,其核心引擎就是Spark,其计算基础是弹性分布式数据集,也就是RDD.通过Spark生态圈,AMPLab运用大数据.云计算.通信等各种资源,以及各种灵活的技术方案,对海量不透明的数据进行甄别并转化为有用的信息,以供人们更好的理解世界.Spark生态圈已经涉及到机器学习.数据挖掘.

Linux环境编程之共享内存区(一):共享内存区简单介绍

共享内存区是可用IPC形式中最快的.一旦内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核.然而往该共享内存区存放信息或从中取走信息的进程间通常须要某种形式的同步.不再涉及内核是指:进程不再通过运行不论什么进入内核的系统调用来彼此传递数据.内核必须建立同意各个进程共享该内存区的内存映射关系.然后一直管理内存区. 默认情况下通过fork派生的子进程并不与其父进程共享内存区. mmap函数把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间.使用该函数的目的有: 1.使用

UNIX网络编程:共享内存区

IPC形式除了管道.FIFO.信号量以外,还有共享内存区和消息队列.这里主要堆共享内存进行介绍. 共享内存区是可用IPC形式中最快的.一旦这样的内存区映射到共享它的进程地址空间,这些进程间数据的传递就不再涉及内核.共享内存与其他进程通信方式相比较,不需要复制数据,直接读写内存,是一种效率非常高的进程通信方案.但它本身不提供同步访问机制,需要我们自己控制.在LINUX中,只要把共享内存段连接到进程的地址空间中,这个进程就可以访问共享内存中的地址了.为了实现往该共享内存区存放和取出数据的进程间的同步

linux进程间通信之Posix共享内存用法详解及代码举例

Posix共享内存有两种非亲缘进程间的共享内存方法:1).  使用内存映射文件,由open函数打开,再由mmap函数把返回的文件描述符映射到当前进程空间中的一个文件. 2). 使用共享内存区对象,由shm_open打开一个 Posix IPC名字.再由mmap把返回的描述符映射到当前进程的地址空间. Posix共享内存相关函数头文件及原型:#include <sys/mman.h>int shm_open(const char *name, int oflag, mode_t mode);功能

Linux 进程通信(共享内存区)

共享内存是由内核出于在多个进程间交换信息的目的而留出的一块内存区(段). 如果段的权限设置恰当,每个要访问该段内存的进程都可以把它映像到自己的私有地址空间中. 如果一个进程更新了段中的数据,其他进程也立即会看到更新. 由一个进程创建的段,也可以由另一个进程读写. 每个进程都把它自己对共享内存的映像放入自己的地址空间. 创建共享内存区 int shmget(key_t key,size_t size,int shm-flg); 参数key既可以是IPC_PRIVATE(IPC_PRIVATE表示让

进程间通信(三)&mdash;&mdash;共享内存区

1.概述 共享内存区是IPC中最快的,当内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核. 但是这需要某种形式的同步,最常用的是信号量. 不再涉及内核:进程不再通过执行任何进入内核的系统调用来彼此传递数据.内核必须建立允许各个进程共享该内存区的内存映射关系,然后一值管理该内存区.   管道,FIFO和消息队列的问题是,两个进程要交换信息时,这些信息必须经由内核传递. 共享内存区可以绕过这个问题,但是一般必须同步数据.   使用内存映射文件的特性,所有的I/O都不再有内核直接参与

Unix IPC之共享内存区(1)

1 共享内存区 共享内存区是可用IPC形式中最快的,只有映射和解除映射需要进入内核的系统调用,映射后对共享内存区的访问和修改不再需要系统调用(内核只要负责好页表映射和处理页面故障即可),但通常需要同步手段. 一个客户-服务器间传递文件数据的例子中,FIFO或消息队列等IPC方式通常需要4次内核-进程间的数据复制(但是Posix消息队列可使用内存映射I/O实现,就不一定4次了),每次都需要切换地址空间,开销很大:共享内存区只需要2次跨内核的数据复制. 2  mmap mmap的三个目的: 1.  

细说linux IPC(四):posix 共享内存

上一节讲了由open函数打开一个内存映射文件,再由mmap函数把得到的描述符映射到当前进程地址空间中来.这一节说说另外一种类似的共享内存方法,即 有shm_open函数打开一个Posix.1 IPC名字(也许是文件系统中的一个路径名),所返回的描述符由函数mmap映射到当前进程地址空间.        posix共享内存和尚上一节类似,首先需要制定一个名字参数调用shm_open ,以创建一个新的共享内存对象或打开一个已存在的共享内存对象:然后再调用mmap把共享内存区映射到调用进程的地址空间中