进程间通信的消息复制
在UNIX中,消息服务直接从一个线程的地址空间复制消息到另一个线程的地址空间,没有也不通过中间的缓存,这样的话消息传递的性能就接近了底层硬件所支持的最大内存带宽了。对于消息的内容,系统内核没有赋予其特别的含义,只有发送者与接收者才相互的为其约定了特殊的意义。尽管如此,系统也提供了“良好定义”的消息类型以便用户编写的进程或线程可以用来扩充或替换系统提供的服务。
消息原(message primitives)支持多段传输,也就是说从一个线程的地址空间传送到另一个线程的消息不必一定位于单独、连续的缓存上。发送与接收线程都可以指定一个向量表来记录发送以及接收的消息片断在内存中的地址。发送与接收者的消息的每个片断的大小可以是不同的。
消息的多段传输可以让消息头块与消息数据块分离的消息在传输时没有必要执行数据拷贝操作以使其变为一个连续的消息,从而就避免了浪费性能的拷贝操作。另外,如果底层数据结构是环形缓存的话,指定为三段的消息就能够让在这个环形缓存中一个头与两个不相交范围的数据可以在一个元消息(atomic message)中完成传输。硬件上与其等同的就是DMA的发散/聚集功能。
多段传送的示意图如下:
多段传送也被大量用于文件系统中。在执行读取操作时,通过使用具有n段数据消息,数据被从文件系统缓存中直接读取到应用程序中。每段都指向了缓存并用来补偿每个缓存块在内存中不是连续的实际。
例如,假如每个缓存块大小为512的字节,如果需要读取1454个字节就可以用一个5段的消息完成。示意图如下:

由于数据是在地址空间直接复制的(而不只是做页面表操作),消息可以在在堆栈中轻易的分配而不需要从用于内存管理器页面反转(MMU page flipping)的页面对齐内存池中进行分配。这样,完成客户与服务器进程之间的API的库例程就可以很简单的进行表述,而不用调用IPC特定的内存分配函数了。
例如,一个客户线程用来请求文件系统管理器作为其代理来执行lseek的代码如下:
#include <unistd.h> #include <errno.h> #include <sys/iomsg.h> off64_t lseek64(int fd, off64_t offset, int whence) { io_lseek_t msg; off64_t off; msg.i.type = _IO_LSEEK; msg.i.combine_len = sizeof msg.i; msg.i.offset = offset; msg.i.whence = whence; msg.i.zero = 0; if(MsgSend(fd, &msg.i, sizeof msg.i, &off, sizeof off) == -1) { return -1; } return off; } off64_t tell64(int fd) { return lseek64(fd, 0, SEEK_CUR); } off_t lseek(int fd, off_t offset, int whence) { return lseek64(fd, offset, whence); } off_t tell(int fd) { return lseek64(fd, 0, SEEK_CUR); }
Related posts:
近期评论