POSIX 共享内存
虽然管道和 POSIX 命名消息队列都能实现进程间的数据通信,但当数据量较大时,管道和 POSIX 命名消息队列的效率就有点低了,这时建议使用 POSIX 共享内存进行直接的数据通信。
为了避免有多个写者进程对同一块共享内存进行写操作,通常需要使用一个命名信号量作为该共享内存的写锁。
同时为了让读者进程能及时知道写者进程已经修改共享内存的内容,通常需要使用一个命名信号量作为该共享内存的读通知信号。
创建一个 POSIX 共享内存可以使用 shm_open 函数。shm_open 函数指定了 POSIX 共享内存的设备文件路径,其他进程可以使用 shm_open 函数打开该共享内存,shm_open 函数返回一个文件描述符,使用 mmap 函数映射该共享内存到进程的虚拟空间内,mmap 函数返回一个虚拟地址,之后便可以通过这个虚拟地址对共享内存进行读和写操作,从而达到高效的进程间直接大数据量通信的目的。
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
函数 shm_open 与一般的 open 功能是一样的,只是它在共享内存设备上打开或创建一个文件。其原型分析如下:
- 此函数成功返回文件描述符,失败返回-1 并设置错误号。
- 参数 name 为用于内存共享映射的文件名称。共享内存创建成功后会在/dev/shm/name生成对应的文件。
- 参数 oflag 为操作标识,如 O_CREAT、O_RDWR 等。
- 参数 mode 为创建文件的模式。
当一个 POSIX 共享内存使用完毕后,应该调用 close 函数将其关闭,如果共享内存不再有任何用途时,应该调用 shm_unlink 函数将其删除,SylixOS 将回收该共享内存占用的内核资源。
下面程序是两个进程(server 和 client)通过共享内存进行通信的例程。
服务器端:
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int main (int argc, char **argv)
{
int fd;
void *addr;
fd = shm_open("test", O_RDWR | O_CREAT, 0666);
if (fd < 0) {
perror("shm_open");
return (-1);
}
ftruncate(fd, 1024);
addr = mmap(NULL, 1024, PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
return (-1);
}
memcpy(addr, "hello sylixos", strlen("hello sylixos") + 1);
msync(addr, 1024, MS_SYNC);
sleep(2);
munmap(addr, 1024);
close(fd);
shm_unlink("test");
return (0);
}
客户端:
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int main (int argc, char **argv)
{
int fd;
void *addr;
char r_buf[64] = {0};
fd = shm_open("test", O_RDWR, 0);
if (fd < 0) {
perror("shm_open");
return (-1);
}
ftruncate(fd, 1024);
addr = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
return (-1);
}
memcpy(r_buf, addr,strlen("hello sylixos") + 1);
printf("r_buf: %s\n", r_buf);
munmap(addr, 1024);
close(fd);
return (0);
}
在 SylixOS Shell 中执行两个程序,首次启动服务器端,来创建共享内存文件,随后再启动客户端来打开已经创建的共享内存,并读取共享内存中的数据,执行结果如下:
# ./server
# ./client
r_buf: hello sylixos
通过执行结果可以看出服务器写入字符串 "hello sylixos",客户端成功获取了该字符串。