美文网首页
APR分析-共享内存篇

APR分析-共享内存篇

作者: 偷风筝的人_ | 来源:发表于2017-12-01 22:04 被阅读0次

APR分析-共享内存篇

共享内存是一种重要的IPC方式。在项目中多次用到共享内存,只是用而并未深入研究。这次趁研究APR代码的机会复习了共享内存的相关资料。

APR共享内存封装的源代码的位置在$(APR_HOME)/shmem目录下,本篇blog着重分析unix子目录下的shm.c文件内容,其相应头文件为$(APR_HOME)/include/apr_shm.h。

一、共享内存简单小结

共享内存是最快的IPC方式,因为一旦这样的共享内存段映射到各个进程的地址空间,这些进程间通过共享内存的数据传递就不需要内核的帮忙了。Stevens的解释是“各进程不是通过执行任何进入内核的系统调用来传递数据,显然内核的责任仅仅是建立各进程地址空间与共享内存的映射,当然像处理页面故障这一类的底层活还是要做的”。相比之下,管道和消息队列交换数据时都需要内核来中转数据,速度就相对较慢。

Unix“历史悠久”,所以在历史上不同版本的Unix提供了不同的支持共享内存的方式,我想这也是Stevens在《Unix网络编程第2卷》中花费三章来讲解共享内存的原因吧。你也不妨先看看shm.c中的代码,代码用条件宏分割不同Share

Memory的实现。

二、APR共享内存封装

APR提供多种创建共享内存的方式,其中最主要的就是apr_shm_create接口,其伪码如下:

apr_shm_create

{

if (要创建匿名shm) {

#if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON

#if APR_USE_SHMEM_MMAP_ZERO

xxxx ---------- (1)

#elif APR_USE_SHMEM_MMAP_ANON

xxxx ---------- (2)

#endif

#endif /* APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON */

#if APR_USE_SHMEM_SHMGET_ANON

xxxx ---------- (3)

#endif

} else { /*创建有名shm */

#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM

#if APR_USE_SHMEM_MMAP_TMP

xxxx ---------- (4)

#endif

#if APR_USE_SHMEM_MMAP_SHM

xxxx ---------- (5)

#endif

#endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */

#if APR_USE_SHMEM_SHMGET

xxxx ---------- (6)

#endif

}

}

apr_shm_create函数代码很长,之所以这样是因为其支持多种创建Share Memory的方式,在上面的伪代码中共用条件宏分隔了6种方式,这6种方式将在下面分析。可以看出shmem主要分为"匿名的"和"有名的",其中"有名的"都是通过filename来标识(或通过ftok转换filename而得到的shmid来标识)。

其中不同版本Unix创建匿名shmem的做法如下:

(1) SVR4通过映射"/dev/zero"设备文件来获得匿名共享内存,其代码一般为:

fd = open("/dev/zero", ..);

ptr = mmap(..., MAP_SHARED, fd, ...);

(2) 4.4 BSD提供更加简单的方式来支持匿名共享内存(注意标志参数MAP_XX)

ptr = mmap(..., MAP_SHARED | MAP_ANON, -1, ...);

(3) System V匿名共享内存区的做法如下:

shmid = shmget(IPC_PRIVATE, ...);

ptr = shmat(shmid, ...);

匿名共享内存一般都用于有亲缘关系的进程间的数据通讯。由父进程创建共享内存,子进程自动继承下来。由于是匿名,没有亲缘关系的进程是不能动态连接到该共享内存区的。

不同版本Unix创建有名shmem的做法如下:

(4)由于是有名的shmem,所以与匿名不同的地方在于用filename替代"/dev/zero"做映射。

fd = open(filename, ...);

apr_file_trunc(...);

ptr = mmap(..., MAP_SHARED, fd, ...);

(5) Posix共享内存的做法

fd = shm_open(filename, ...);

apr_file_trunc(...);

ptr = mmap(..., MAP_SHARED, fd, ...);

值得注意的一点就是通过shm_open映射的共享内存可以供无亲缘关系的进程共享。apr_file_trunc用于重新设定共享内存对象长度。

(6) System V有名共享内存区的做法如下:

shmkey = ftok(filename, 1);

shmid = shmget(shmkey, ...); //相当于open orshm_open

ptr = shmat(shmid, ...); //相当于mmap

有名共享内存一般都与一个文件相关,该文件映射到共享内存段,而不同的进程(包括无亲缘关系的进程)则都映射到该文件以达到目的。在APR中通过apr_shm_attach可以动态将调用进程连接到已存在的共享内存区上,前提是你必须知道该共享内存区的标识,在APR中一律用filename做标识。

三、总结

内核架起了多个进程间共享数据的纽带--共享内存。通过上面的叙述你会发现共享内存的创建其实并不困难,真正困难的是共享内存的管理[注1],在正规的软件公司像内存/共享内存管理这样的重要底层功能都是封装成库形式的,当然内存管理的内容不是这篇blog重点涉及的内容。

四、参考资料:

1、《Unix网络编程第2卷》

2、《Unix环境高级编程》

[注1] SIGSEGV和SIGBUS

涉及共享内存的管理就不能不提到访问共享内存对象。谈到访问共享内存对象就要留神“SIGSEGV和SIGBUS”这两个信号。

系统分配内存页来承载内存映射区,由于内存页大小是固定的,所以存在多余的页空间空闲,比如待映射文件大小为5000 bytes,内存映射区大小也为5000

bytes。而一个内存页大小4096,系统势必要分配两页来承载,这时空闲的有效空间为从5000-8191,如果进程访问这段地址空间也不会发生错误。但是要超出8191,就会收到SIGSEGV信号,导致程序停止。关于SIGBUS信号的来历,这里也举例说明:若待映射文件大小为5000

bytes,我们在mmap时指定内存映射区size = 15000 > 5000,这时内核真正的共享区承载体大小只有8192(能包容映射文件大小即可),此时在[0,8191]内访问均没问题,但在[8192,14999]之间会得到SIGBUS信号;超出15000访问时会触发SIGSEGV信号。

相关文章

  • APR分析-共享内存篇

    APR分析-共享内存篇 共享内存是一种重要的IPC方式。在项目中多次用到共享内存,只是用而并未深入研究。这次趁研究...

  • APR分析-内存篇

    APR分析-内存篇 内存管理一直是让C程序员头痛的问题,作为一个通用接口集,APR当然也提供其自己的内存管理接口-...

  • APR分析-高级IO篇

    APR分析-高级IO篇 近两天稍轻闲了些,便抓紧时间学习、学习再学习。在“APR分析-文件IO篇”,我们只分析了最...

  • APR分析-进程篇

    APR分析-进程篇 Apache Server的进程调度一直为人所称道,Apache 2.0推出的APR对进程进行...

  • APR分析-线程篇

    APR分析-线程篇 并行一直是程序设计领域的难点,而线程是并行的一种重要的手段,而且线程的一些特性也能在进程并行时...

  • APR分析-环篇

    APR分析-环篇 APR中少见对数据结构的封装,好像唯一例外的就是其对循环链表,即环(RING)的封装。 在大学的...

  • APR分析-设计篇

    APR分析-设计篇 作为一个可移植的运行时环境,APR设计当然是很精妙的,但精妙的同时对使用者有一些限制。 APR...

  • APR分析信号篇

    APR分析-信号篇 U know信号是Unix的重要系统机制。信号机制使用起来很简单,但是理解起来有并不是那么Ea...

  • APR分析-整体篇

    APR分析-整体篇 由于部门所使用的底层库与Apache Server有着“一定的渊源”,所以总有一种想看看Apa...

  • Android窗口管理分析(4):Android View绘制内

    前文Android匿名共享内存(Ashmem)原理分析了匿名共享内存,它最主要的作用就是View视图绘制,Andr...

网友评论

      本文标题:APR分析-共享内存篇

      本文链接:https://www.haomeiwen.com/subject/obkgbxtx.html