POSIX 进程间通信
POSIX 进程间通信 (Interprocess Communication, IPC) 是 System V 进程间通信的变体。它是在 Solaris 7 发行版中引入的。与 System V 对象类似,POSIX IPC 对象的属主、属主的组以及其他用户具有读取和写入权限,但是没有执行权限。POSIX IPC 对象的属主无法将对象分配给其他属主。POSIX IPC 包括以下功能:
-
消息允许进程将已格式化的数据流发送到任意进程。
-
信号量允许进程同步执行。
-
共享内存允许进程共享其部分虚拟地址空间。
与 System V IPC 接口不同,POSIX IPC 接口均为多线程安全接口。
POSIX 共享内存涉及的内存映射函数
mmap 函数
mmap函数把一个文件或一个posix共享内存对象映射到调用进程的地址空间。
//成功返回映射内存的起始地址,失败返回MAP_FAILED void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
mmap参数解析:
- addr指定映射内存的起始地址,通常设为NULL,让内核自己决定起始地址
- len是被映射到调用进程地址空间中的字节数,它从被映射文件fd开头起第offset个字节处开始算,offset通常设为0,下图展示了这个映射关系
- prot指定对映射内存区的保护,通常设为
PROT_READ | PROT_WRITE
- flags必须在
MAP_SHARED
和MAP_PRIVATE
这两个标志中选择指定一个,进程间共享内存需要使用MAP_SHARED - 可移植的代码应把addr设为NULL,并且flags不指定MAP_FIXED
prot | 说明 | flags | 说明 |
---|---|---|---|
PROT_READ | 数据可读 | MAP_SHARED | 变动是共享的 |
PROT_WRITE | 数据可写 | MAP_PRIVATE | 变动是私有的 |
PROT_EXEC | 数据可执行 | MAP_FIXED | 准确地解释addr参数 |
PROT_NONE | 数据不可访问 |
mmap成功返回后,可以关闭fd,这对已建立的映射关系没有影响。
注意,不是所有文件都能进行内存映射,例如终端和套接字就不可以。
munmap 函数
mmap建立的映射关系通过munmap删除,其中addr是mmap返回的地址,len是映射区的大小,同mmap的参数len。
msync 函数
默认情况下,内核采用虚拟内存算法保持内存映射文件与内存映射区的同步,前提是指定了MAP_SHARED标志,但这种同步可能不是立即生效的,而是在随后某个时间进行。
但有时候我们修改完数据并进行下一步操作之前,需要确认数据已经同步完成,这时可调用msync函数。
//成功返回0,失败返回-1 int msync(void *addr, size_t len, int flags);
其中addr和len含义同munmap,flags使用下表中的常值,其中MS_ASYNC
和MS_SYNC
这两个常值中必须选择指定一个。
flags | 说明 |
---|---|
MS_ASYNC | 执行异步写,msync立即返回 |
MS_SYNC | 执行同步写,msync等同步完成才返回 |
MS_INVALIDATE | 使高速缓存的数据失效 |
POSIX 共享内存其他相关函数
shm_open和shm_unlink函数
shm_open用于创建一个新的posix共享内存对象或打开一个已存在的Posix共享内存对象。
shm_unlink用于从系统中删除一个posix共享内存对象。
//成功返回非负描述符,失败返回-1 int shm_open(const char *name, int oflag, mode_t mode); //成功返回0,失败返回-1 int shun_unlink(const char *name);
shm_open参数说明:
- oflag参数不能设置O_WRONLY标志
- 和mq_open、sem_open不同,shm_open的mode参数总是必须指定,当指定了O_CREAT标志时,mode为用户权限位,否则将mode设为0
shm_open的返回值是一个描述符,它随后用作mmap的第五个参数fd。
ftruncate和fstat函数
处理mmap的时候,普通文件或Posix共享内存对象的大小都可以通过调用ftruncate设置。
#include <unistd.h> //成功返回0,失败返回-1 int ftruncate(int fd, off_t length):
- 对于普通文件,若文件长度大于length,额外的数据会被丢弃;若文件长度小于length,则扩展文件大小到length
- 对于Posix共享内存对象,ftruncate把该对象的大小设置成length字节
我们调用ftruncate来指定新创建的Posix共享内存对象大小,或者修改已存在的Posix共享内存对象大小。
- 创建新的Posix共享内存对象时指定大小是必须的,否则访问mmap返回的地址会报bus error错误
- 当打开一个已存在的Posix共享内存对象时,可以调用fstat来获取该对象的信息
#include <sys/stat.h> #include <sys/types.h> //成功返回0,失败返回-1 int fstat(int fd, struct stat *buf);
stat结构有12个或以上的成员,然而当fd指代一个Posix共享内存对象时,只有四个成员含有信息:
struct stat { mode_t st_mode; //用户访问权限 uid_t st_uid; //user id of owner gid_t st_gid; //group id of owner off_t st_size; //文件大小 };
POSIX 共享内存示例代码
示例代码包括一个.h文件和两个.c文件:
- common.h定义server和client共同使用的信息
- server.c创建并初始化Posix共享内存对象,以及同步需要的信号量
- client.c打开Posix共享内存对象,然后给共享内存中的计数器加1
例子1:进程同步使用posix有名信号量
common.h
#ifndef _COMMON_H_ #define _COMMON_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <semaphore.h> #include <sys/mman.h> #define SHM_FILE "/shm_file" #define SEM_PATH "/sem_mmap" struct Shared { int count; }; #endif
server.c
#include "common.h" int main() { struct Shared *ptr; sem_t *mutex; int fd; shm_unlink(SHM_FILE); fd = shm_open(SHM_FILE, O_RDWR | O_CREAT, 0666); ftruncate(fd, sizeof(struct Shared)); ptr = mmap(NULL, sizeof(struct Shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); sem_unlink(SEM_PATH); mutex = sem_open(SEM_PATH, O_CREAT, 0666, 1); sem_close(mutex); pause(); return 0; }
client.c
#include "common.h" int main(int argc, char **argv) { struct Shared *ptr; struct stat buf; sem_t *mutex; int fd; int nloop; int i; fd = shm_open(SHM_FILE, O_RDWR, 0); fstat(fd, &buf); ptr = mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); mutex = sem_open(SEM_PATH, 0); nloop = atoi(argv[1]); for (i = 0; i < nloop; i++) { sem_wait(mutex); printf("pid %d: %d\n", getpid(), ptr->count++); sem_post(mutex); } return 0; }
编译并启动server,阻塞在pasue()中。
后台同时运行三个client进程。
截取进程切换时的部分输出片段,可以看到切换后计数依然是连续的。
例子2:进程同步使用posix无名信号量
common.h
#ifndef _COMMON_H_ #define _COMMON_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <semaphore.h> #include <sys/mman.h> #define SHM_FILE "/shm_file" struct Shared { sem_t mutex; int count; }; #endif
server.c
#include "common.h" int main() { struct Shared *ptr; int fd; shm_unlink(SHM_FILE); fd = shm_open(SHM_FILE, O_RDWR | O_CREAT, 0666); ftruncate(fd, sizeof(struct Shared)); ptr = mmap(NULL, sizeof(struct Shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); sem_init(&ptr->mutex, 1, 1); pause(); return 0; }
client.c
#include "common.h" int main(int argc, char **argv) { struct Shared *ptr; struct stat buf; int fd; int nloop; int i; fd = shm_open(SHM_FILE, O_RDWR, 0); fstat(fd, &buf); ptr = mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); //共享内存中的mutex已由server初始化,client直接使用就可以了,不能重复初始化 nloop = atoi(argv[1]); for (i = 0; i < nloop; i++) { sem_wait(&ptr->mutex); printf("pid %d: %d\n", getpid(), ptr->count++); sem_post(&ptr->mutex); } return 0; }
输出同例子1。
标签:---,include,int,mmap,间通信,fd,POSIX,共享内存,shm From: https://www.cnblogs.com/god-of-death/p/17909480.html