对话 UNIX 通过共享内存进行进程间通信Word文档格式.docx
《对话 UNIX 通过共享内存进行进程间通信Word文档格式.docx》由会员分享,可在线阅读,更多相关《对话 UNIX 通过共享内存进行进程间通信Word文档格式.docx(9页珍藏版)》请在冰豆网上搜索。
Internet协议第6版
∙POSIX:
UNIX的可移植操作系统接口
尽管涉及一些复杂的机制,但是大多数应用程序不会注意到资源实际上是共享的,它们似乎是独享资源。
但是,可以编写相互交互的应用程序。
例如,一个应用程序收集或生成数据,而另一个应用程序同时监视进度并分析信息。
另一个例子是即时交换消息的聊天系统,其中有两个对等的应用程序相互收发数据。
SecureShell(ssh)也是这样,它可以在两个完全不同的主机之间进行协作。
在这些情况下,代码都要连接另一段独立的代码以交换信息,这常常需要使用某种协议协商和控制交换过程。
UNIX为实现这样的进程间通信提供了多种技术。
一些技术提供同一主机上的进程间通信,其他技术可以实现主机到主机的信息交换。
另外,各种技术的速度不同,所以必须选择最合适自己需求的技术。
还必须进行协调(实施时间控制和排他控制)。
例如,如果一个应用程序产生数据,另一个应用程序消费数据,那么当读完共享池时消费者必须停下来等待生产者。
另一方面,如果消费者无法足够快地读取池,生产者必须慢下来或暂停。
表1总结在典型的UNIX系统上可用的进程间通信形式。
表1.UNIX中的进程间通信
名称
说明
范围
用途
文件
在典型的UNIX文件中读写数据。
任意数量的进程都可以互操作。
本地
共享大数据集
管道
使用专用的文件描述符在两个进程之间传输数据。
通信只在父进程和子进程之间进行。
简单的数据共享,比如生产者和消费者
命名管道
通过专用的文件描述符在进程之间交换数据。
通信可以在同一主机上的任意两个对等进程之间进行。
生产者和消费者或命令-控制,比如MySQL和它的命令行查询工具
信号
通过中断通知应用程序某一情况。
无法在信号中传输数据,所以信号主要用于进程管理
共享内存
通过在同一内存段中读写数据共享信息。
任何类型的协作,尤其适合需要安全性的情况
套接字
完成特殊的设置过程之后,使用一般的输入/输出操作传输数据。
本地或远程
FTP、ssh和ApacheWebServer等网络服务
正如前面提到的,每种技术满足不同的需求。
假设多个进程之间的协作的复杂性大体相当,每种方法的优点和缺点如下:
∙通过一般的UNIX文件共享数据很简单,因为它使用大家熟悉的文件操作。
但是,通过文件系统共享数据很慢,因为磁盘输入和输出操作的效率远远比不上内存。
另外,只通过文件读写数据很难协调。
最后,在文件中保存敏感数据是不安全的,因为根用户和拥有特权的其他用户可以访问这些信息。
对于只读或只写的数据,适合使用文件。
∙管道和命名管道也很简单。
它们在连接的两端使用两个标准的文件描述符—一个只执行读操作,另一个只执行写操作。
但是,管道只能在父进程和子进程之间使用,不能在任意两个进程之间使用。
命名管道克服了这个缺点,是在同一系统上交换数据的好方法。
但是,管道和命名管道都不提供随机访问,因为它们都作为先入先出(FIFO)设备。
∙信号无法在进程之间传输数据。
一般情况下,信号应该只用于在进程之间通知异常情况。
∙共享内存适合比较大的数据集,因为它使用内存,支持快速的随机访问。
共享内存的实现有点儿复杂,尽管如此,对于多个进程之间的主机内协作,共享内存是不错的方法。
∙套接字的功能与命名管道很相似,但是可以跨主机。
本地套接字(也称为UNIX套接字)只能进行本地(同一主机上的)连接。
Inet和Inet6套接字分别使用IPv4和IPv6协议,它们接受远程连接(也可以通过本地机器的Internet寻址机制接受本地连接)。
网络应用程序显然应该选择套接字,比如分布式处理或web浏览器。
所需的代码比命名管道复杂一点儿,但是模式是固定的,在任何UNIX网络编程书中都有介绍。
现在不考虑主机间应用程序通信,看看如何通过共享内存在同一主机上进行进程间通信。
共享内存的工作方式
顾名思义,共享内存让一段内存可供多个进程访问。
用特殊的系统调用(即对UNIX内核的请求)分配和释放内存并设置权限;
通过一般的读写操作读写内存段中的数据。
共享内存并不是从某一进程拥有的内存中划分出来的;
进程的内存总是私有的。
共享内存是从系统的空闲内存池中分配的,希望访问它的每个进程连接它。
这个连接过程称为映射,它给共享内存段分配每个进程的地址空间中的本地地址。
图1、图2、图3和图4说明此过程:
1.假设在同一系统上有两个进程A和B正在运行(见图1),它们可以通过共享内存进行协作和共享信息。
在图中A和B采用不同大小的图形,以此强调应用程序不必相同。
图1.两个进程在同一个主机上运行,执行不同的代码
2.在图2中,进程A请求一个共享内存段。
进程A对这个内存段进行初始化,让它准备好接受访问。
这个过程还给内存段命名,让其他进程可以找到它。
通常,内存段名称并不是动态分配的;
而是众所周知的,比如使用头文件中的常量,其他代码可以方便地引用它。
图2.一个进程请求共享内存段
3.进程A把共享内存段连接(即映射)到自己的地址空间。
进程B通过它的命名管道找到这个内存段,也把它映射到自己的地址空间,见图3。
两个进程扩大了,表示包含共享内存段。
图3.两个进程连接(即映射)共享内存段
4.最后,在图4中,进程A和B可以随意读写共享内存段。
按照与本地进程内存相同的方式对待共享内存。
read()和write()的作用与一般情况下一样。
图4.两个或更多进程现在可以通过共同的内存共享数据
这些图中所示的许多工作可以通过UNIX共享内存API执行。
实际上,有两套共享内存API:
POSIXAPI和比较老(但是仍然有效)的SystemVAPI。
因为POSIX是UNIX和Linux®
及其衍生系统上的公认标准,所以我们使用此版本。
另外,POSIXAPI使用简单的文件描述符执行读写,大家应该更熟悉。
POSIX为创建、映射、同步和取消共享内存段提供五个入口点:
∙shm_open():
创建共享内存段或连接到现有的已命名内存段。
这个系统调用返回一个文件描述符。
∙shm_unlink():
根据(shm_open()返回的)文件描述符,删除共享内存段。
实际上,这个内存段直到访问它的所有进程都退出时才会删除,这与在UNIX中删除文件很相似。
但是,调用shm_unlink()(通常由原来创建共享内存段的进程调用)之后,其他进程就无法访问这个内存段了。
∙mmap():
把共享内存段映射到进程的内存。
这个系统调用需要shm_open()返回的文件描述符,它返回指向内存的指针。
(在某些情况下,还可以把一般文件或另一个设备的文件描述符映射到内存。
对这些操作的讨论超出了本文的范围;
具体方法请查阅操作系统的mmap()文档。
)
∙munmap():
作用与mmap()相反。
∙msync():
用来让共享内存段与文件系统同步—当把文件映射到内存时,这种技术有用。
使用共享内存的过程是,用shm_open()创建内存段,用write()或ftruncate()设置它的大小,用mmap()把它映射到进程内存,执行其他参与者需要的操作。
当使用完时,原来的进程调用munmap()和shm_unlink(),然后退出。
示例应用程序
清单1给出一个简单的共享内存示例。
(代码取自JohnFusco撰写的TheLinuxProgrammer'
sToolbox一书[由PrenticeHallProfessional于2007年3月出版,ISBN0132198576],已经得到出版商的使用授权。
)代码实现通过共享内存段通信的父进程和子进程。
清单1.共享内存示例
#include<
stdio.h>
string.h>
stdlib.h>
unistd.h>
sys/file.h>
sys/mman.h>
sys/wait.h>
voiderror_and_die(constchar*msg){
perror(msg);
exit(EXIT_FAILURE);
}
intmain(intargc,char*argv[]){
intr;
constchar*memname="
sample"
;
constsize_tregion_size=sysconf(_SC_PAGE_SIZE);
intfd=shm_open(memname,O_CREAT|O_TRUNC|O_RDWR,0666);
if(fd==-1)
error_and_die("
shm_open"
);
r=ftruncate(fd,region_size);
if(r!
=0)
ftruncate"
void*ptr=mmap(0,region_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(ptr==MAP_FAILED)
mmap"
close(fd);
pid_tpid=fork();
if(pid==0){
u_long*d=(u_long*)ptr;
*d=0xdbeebee;
exit(0);
}
else{
intstatus;
waitpid(pid,&
status,0);
printf("
childwrote%#lx\n"
*(u_long*)ptr);
r=munmap(ptr,region_size);
munmap"
r=shm_unlink(memname);
shm_unlink"
return0;
下面是代码中的一些要点:
∙对shm_open()的调用看起来应该很熟悉;
它与open()函数很相似,包括初始化内存段和设置权限的方式。
在这里,内存段是全局可读、全局可写的。
如果调用成功,返回下一个未使用的文件描述符;
否则,返回-1并相应地设置errno。
∙ftruncate()把文件的大小设置为region_size字节,这以前设置为系统的标准页面大小。
sysconf()是libc的组成部分。
(还可以使用shell工具getconf检查系统的配置设置。
∙mmap()连接共享内存段,返回用于对内存段直接读写字节的指针。
PROT_READ和PROT_WRITE分别表示可以读和写这个内存段中的页面。
MAP_SHARED表示对这个内存段的任何修改应该向所有参与共享的进程“公开”。
∙如果您使用过fork(),那么应该熟悉代码的计算部分。
执行fork之后,父进程和子进程获得打开的所有文件描述符和数据值的拷贝,所以指针对于它们都是有效的。
但是,pid不同。
子进程获得0,父进程获得子进程的进程ID,这个变量的值决定执行哪个if/then/else分支。
子进程向指针写一些字节,然后退出。
父进程等待子进程退出,然后读取它写的数据。
∙但是,在父进程退出之前,它必须释放共享内存。
用munmap()和shm_unlink()完成这个步骤。
这个示例非常简单。
真实的应用程序会使用信号量或其他技术控制对共享内存段的读写。
这种控制通常因应用程序而异,如果您的UNIX版本不是开放源码的,可以在BerkeleySoftwareDistribution(BSD)和Linux源代码中找到许多示例。
结束语
因为UNIX同时运行许多应用程序,所以它是非常适合监视、数据收集、协作和分布式计算以及客户机-服务器应用程序的平台。
共享内存是速度最快的进程间通信技术,而且非常灵活。
还可以把文件映射到内存,这是加快数据访问的理想解决方案。
参考资料
学习
∙Linux程序员的工具包:
研究这个工具包。
∙共享内存:
阅读关于共享内存的基础教程,进一步了解各种实现。
∙进程间通信:
进一步了解共享内存和其他进程间通信形式是如何实现的。
∙对话UNIX:
阅读本系列中的其他部分。
∙AIXandUNIX专区:
developerWorks的“AIXandUNIX专区”提供了大量与AIX系统管理的所有方面相关的信息,您可以利用它们来扩展自己的UNIX技能。
∙AIXandUNIX新手入门:
访问“AIXandUNIX新手入门”页面可了解更多关于AIX和UNIX的内容。
∙AIXandUNIX专题汇总:
AIXandUNIX专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。
我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
∙AIXandUNIX下载中心:
在这里你可以下载到可以运行在AIX或者是UNIX系统上的IBM服务器软件以及工具,让您可以提前免费试用他们的强大功能。
∙IBMSystemsMagazineforAIX中文版:
本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。
IBMSystemsMagazine的内容都是由十分资深的业内人士撰写的,包括IBM的合作伙伴、IBM的主机工程师以及高级管理人员。
所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用IBM系统时有一个更好的认识。
∙技术书店:
阅读关于这些和其他技术主题的图书。
讨论
∙developerWorks博客:
阅读我们的博客并加入developerWorks社区。
∙加入MydeveloperWorks社区。
∙参与AIX和UNIX论坛:
oAIX论坛
oAIXfordevelopers论坛
o集群系统管理
oIBMSupportAssistant论坛
o性能工具论坛
o虚拟化论坛
o更多AIX和UNIX论坛
关于作者