C programming in the UNIX environment的编程手册,一般都会为进程间用共享内存的方法通信提供两组方法:
int shmget(key_t key, int size, int shmflg); //得到一个共享内存标识符或创建一个共享内存对象
void *shmat(int shmid, const void *shmaddr, int shmflg); //把共享内存区对象映射到调用进程的地址空间
int shmdt(const void *shmaddr); //断开共享内存连接
int shmctl(int shmid, int cmd, struct shmid_ds *buf); //完成对共享内存的控制可以修改共享内存信息,也可以删除这片贡献内存。
例子代码如下:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <error.h> #define SIZE 1024 int main() { int shmid ; char *shmaddr ; struct shmid_ds buf ; int flag = 0 ; int pid ; shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ; if ( shmid < 0 ) { perror("get shm ipc_id error") ; return -1 ; } pid = fork() ; if ( pid == 0 ) { shmaddr = (char *)shmat( shmid, NULL, 0 ) ; if ( (int)shmaddr == -1 ) { perror("shmat addr error") ; return -1 ; } strcpy( shmaddr, "Hi, I am child process!\n") ; shmdt( shmaddr ) ; return 0; } else if ( pid > 0) { sleep(3 ) ; flag = shmctl( shmid, IPC_STAT, &buf) ; if ( flag == -1 ) { perror("shmctl shm error") ; return -1 ; } printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ; printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ; printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ; shmaddr = (char *) shmat(shmid, NULL, 0 ) ; if ( (int)shmaddr == -1 ) { perror("shmat addr error") ; return -1 ; } printf("%s", shmaddr) ; shmdt( shmaddr ) ; shmctl(shmid, IPC_RMID, NULL) ; }else{ perror("fork error") ; shmctl(shmid, IPC_RMID, NULL) ; } return 0 ; }
编译 gcc shm.c –o shm。
执行 ./shm,执行结果如下:
shm_segsz =1024 bytes
shm_cpid = 9503
shm_lpid = 9504
Hi, I am child process!
int shm_open(const char *name, int oflag, mode_t mode); //打开
参数:name 共享内存区的名字(不能为空), cflag 标志位 mode 权限位 返回值:成功返回0,出错返回-1
oflag参数必须含有O_RDONLY和O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC.
mode参数指定权限位,它指定O_CREAT标志的前提下使用。 shm_open的返回值是一个整数描述字,它随后用作mmap的第五个参数。
int shm_unlink(const char *name); //删除一个共享内存区,返回值,0成功,-1失败。
int ftruncate(int fd, off_t length); //分配大小
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); //将一个文件或者其它对象映射进内存
返回值:如果mmap成功则返回映射首地址,如果出错则返回常数MAP_FAILED。
参数:
addr 指向映射存储区的起始地址;
len 映射的字节
prot 对映射存储区的保护要求
flag 标志位
filedes 要被映射文件的描述符
off 要映射字节在文件中的起始偏移量
参数解释如下:整体相当于磁盘文件的对应长度搬移到内存中。如果addr参数为NULL,内核会自己在进程地址空间中选择合适的地址建立映射。
如果addr不是NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择addr之上的某个合适的地址开始映射。建立映射后,真正的
映射首地址通过返回值可以得到。off参数是从文件的什么位置开始映射,必须是页大小的
整数倍(在32位体系统结构上通常是4K)。
prot参数有四种取值:
PROT_EXEC表示映射的这一段可执行,例如映射共享库
PROT_READ表示映射的这一段可读
PROT_WRITE表示映射的这一段可写
PROT_NONE表示映射的这一段不可访问
flag参数有很多种取值,这里只讲两种,
MAP_SHARED多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
MAP_PRIVATE多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
int fstat(int fd, struct stat *buf) //当打开一个已经从在的共享内存时,我们调用fstat 来获取有关该对象的信息
int munmap(void *addr,size_t len) // 要删除一个已经建立的映射关系
int msync (void *addr,size_t len,int flags) //实现同步或者异步
由于POSIX标准比较通用,一般建议使用该标准定义的方法集。但在编译的时候可能会出现“undefined reference to `shm_open'”的错误,加上--lrt就可以编过,例如:
/* This is just to test if the function is found in the libs. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> int main (void) { int i; i = shm_open ("/tmp/shared", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); printf ("shm_open rc = %d\n", i); shm_unlink ("/tmp/shared"); return (0); }
用命令 gcc -lrt -o test test.c 就可以编过。