IPC之共享内存
man 7 shm_overview
shm_overview - Overview of POSIX shared memory.
同样,SystemV实现的共享内存是旧的机制,但应用广泛;Posix标准提供了新的统一接口。
共享内存是由内核出于在多个进程间交换信息的目的而留出的一块内存区(段)。如果段的权限设置恰当,每个要访问该段内存的进程都可以把它映射到自己私有的地址空间中。如果一个进程更新了段中数据,那么其他进程立即回看到更新。由一个进程创建的段也可以由另一个进程读写。共享内存这一名称表达出是由多个进程分享对段及其保存的数据的访问权这一含义。共享内存很像内存映射文件。
由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
Posix API:
shm_open, ftruncate, mmap, munmap, shm_unlink, close, fstat, fchown, fchmod.
System V API:
shmget,shmat,shmdt
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flags);
flags可以是一个或多个IPC_CREAT、IPC_EXCL和一组权限位(模式)按位“或”的结果。权限位以八进制表示。IPC_EXCL确保如果段已经存在,执行失败,而不是返回一个已经存在的段的标示符。IPC_CREAT指出如果没有和key关联的段就应该创建一个新段。key既可以是IPC_PRIVATE也可以是ftok函数返回的一个关键字。参数size指定段的大小,但它以PAGE_SIZE的值为界,这个值是某种处理器本身页的大小(intel是4KB)。shmget成功返回段标识符,出错返回-1。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #define BUFSZ 4096 int main(void) { int shmid; if((shmid = shmget(IPC_PRIVATE, BUFSZ, 0666)) < 0) { perror("shmget"); exit(EXIT_FAILURE); } printf("segment created: %d\n", shmid); system("ipcs -m\n"); exit(EXIT_SUCCESS); }
~$./a.out segment created: 65538 ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 0 yuxi 666 4096 0 0x00000000 32769 yuxi 666 4096 0 0x00000000 65538 yuxi 666 4096 0
shmget只是创建了共享内存区,进程要把它映射到自己的地址空间才能使用它,调用shmat完成。
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const char *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
在函数shmat中,如果shmaddr为NULL,则内核会把段映射到调用进程的地址空间中它所选定的位置。假如shmaddr不为NULL,并且shmflg指定SHM_RND,则attach发生在附近的最小倍数SHMLBA。否则shmaddr必须是页对齐的地址。一般总是把shmaddr设置为0。flags可以为SHM_RDONLY,这意味着被附加的段是只读的。否则,被附加的段默认是可读写的。如果shmat调用成功,则返回附加了段的地址。否则,它返回-1并且设置errno变量。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int main(int argc, char *argv[]) { int shmid; char *shmbuf; if(argc != 2) { puts("USAGE: atshm <identifier>"); exit(EXIT_FAILURE); } shmid = atoi(argv[1]); if((shmbuf = shmat(shmid, 0, 0)) < (char *)0) { perror("shmat"); exit(EXIT_FAILURE); } printf("segment attached at %p\n", shmbuf); system("ipcs -m"); if((shmdt(shmbuf)) < 0) { perror("shmdt"); exit(EXIT_FAILURE); } puts("segment detached"); system("ipcs -m"); exit(EXIT_SUCCESS); }
~$./a.out 32769 segment attached at 0xb7709000 ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 0 yuxi 666 4096 0 0x00000000 32769 yuxi 666 4096 1 0x00000000 65538 yuxi 666 4096 0 segment detached ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 0 yuxi 666 4096 0 0x00000000 32769 yuxi 666 4096 0 0x00000000 65538 yuxi 666 4096 0
使用举例:
include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/shm.h> #include <sys/ipc.h> #include <fcntl.h> #include <ctype.h> #define BUFSZ 4096 int main(int argc, char **argv) { int shmid; char *shmbuf; int fd; int i; if(argc != 2) { puts("USAGE: opshm <identifier>"); exit(EXIT_FAILURE); } shmid = atoi(argv[1]); if((shmbuf = shmat(shmid, 0, 0)) < (char *)0) { perror("shmat"); exit(EXIT_FAILURE); } if((shmbuf = malloc(sizeof(char) * BUFSZ)) < (char *)0) { perror("malloc"); exit(EXIT_FAILURE); } for(i = 0; i < BUFSZ; ++i) { shmbuf[i] = rand(); } fd = open("opshm.out", O_CREAT | O_WRONLY, 0600); write(fd, shmbuf, BUFSZ); free(shmbuf); exit(EXIT_SUCCESS); }
创建shm举例
void *init_shm(void) { key_t key; int shmid; void *addr = NULL; key = ftok(SHM_PATH, SHM_PROJ); if(key == -1) { perror("ftok()"); return NULL; } shmid = shmget(key, sizeof(struct shm_block), IPC_CREAT | 0600); if(shmid == -1) { perror("shmget: "); return NULL; } addr = shmat(shmid, 0, 0); if (addr == (void *)-1) { perror("shmat()"); return NULL; } return addr; }
通过命令ipcs -m可以查看共享内存信息。
注:大部分内容来自《GNU/LINUX编程指南》