APUE读书笔记---进程间通信之POSIX共享内存区

3/8/2017来源:ASP.NET技巧人气:2132

APUE读书笔记—进程间通信之POSIX共享内存区

1. 概述

无亲缘关系进程间共享内存区的方法:

内存映射文件(memory-mapped file),有open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间的一个文件。 共享内存对象(shared-memory object),由shm_open打开一个POSIX ipC名字(路径名),所返回的描述符由mmap函数映射到当前进程的地址空间。 POSIX把两者合称为内存区对象(memory object)

2. shm_open和shm_unlink函数

#include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ int shm_open(const char *name, int oflag, mode_t mode); //返回值:若成功则为非负描述符,若出错则为-1 int shm_unlink(const char *name); //返回值:若成功则为0,若出错,返回-1 name的命名规则: 必须符合已有的路径名规则(必须最多由PATH_MAX个字节构成,包括结尾的空字节) 如果它以斜杠开头,那么对这些函数的不同调用将访问同一个队列。 名字中额外的斜杠符的解释由实现定义。为了可移植性,IPC名字必须以一个斜杠开头,并且不能包含其他的斜杠符。 oflag参数必须指定O_RDONLY或O_RDWR标志,还可以指定O_CREAT、O_EXCL、O_TRUNC标志。 mode参数必须在O_CREAT标志下使用,和open函数类似。 shm_unlink函数删除一个共享内存区对象的名字。删除一个name不会影响对其底层支撑对象的现有引用。只是防止后续的open等等函数的调用取得成功。

3. ftruncate和fstate函数

处理mmap时,可以调用ftruncate函数对普通文件或共享内存对象的大小修改。

#include <unistd.h> #include <sys/types.h> int ftruncate(int fd, off_t length); //返回值:若成功则为0,若出错,返回-1

打开一个存在的共享内存区对象时,可以调用fstat来获取有关该对象的信息。

S #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int fstat(int fd, struct stat *buf); //返回值:若成功,返回0,若出错则为-1

stat结构有12个以上成员,当fd指代一个共享内存区对象时,只含有四个成员信息。

struct stat { mode_t st_mode; /* PRotection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ };

4. 例子

4.1 创建共享内存对象

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <errno.h> #include <unistd.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) typedef struct stu { char name[32]; int age; }STU; int main(void) { int shmid; struct stat buf; shmid = shm_open("/xyz", O_CREAT | O_RDWR, 0666); if (shmid == -1) ERR_EXIT("shm_open"); printf("shm_open succ\n"); if ((ftruncate(shmid, sizeof(STU))) == -1) ERR_EXIT("ftruncate"); if (fstat(shmid, &buf) == -1) ERR_EXIT("fstat"); printf("size = %ld, mode = %o\n", buf.st_size, buf.st_mode & 07777); close(shmid); return 0; } //先将共享区对象大小用ftruncate修改,然后用fstat读出占的大小

运行结果:

➜ shm_open ./shm_open shm_open succ size = 36, mode = 664 //mode为664是因为有umask掩码是002

然后可以在/dev/shm目录中查看到xyz文件

➜ shm ls -l /dev/shm/xyz -rw-rw-r-- 1 menwen menwen 36 3月 3 23:47 /dev/shm/xyz

4.2 写如共享内存区

int main(void) { int shmid; struct stat buf; STU *ptr; shmid = shm_open("/xyz", O_RDWR, 0666); if (shmid == -1) ERR_EXIT("shm_open"); printf("shm_open succ\n"); if (fstat(shmid, &buf) == -1) ERR_EXIT("fstat"); printf("size = %ld, mode = %o\n", buf.st_size, buf.st_mode & 07777); ptr = mmap(NULL, buf.st_size, PROT_WRITE, MAP_SHARED, shmid, 0); if(ptr == MAP_FAILED) ERR_EXIT("mmap"); strcpy(ptr->name, "test"); ptr->age = 20; close(shmid); return 0; } //读写打开xyz文件,mmap函数映射得到内存地址,并且是写保护,写入一个结构的数据。

4.3 读出数据

int main(void) { int shmid; struct stat buf; STU *ptr; shmid = shm_open("/xyz", O_RDWR, 0666); if (shmid == -1) ERR_EXIT("shm_open"); if (fstat(shmid, &buf) == -1) ERR_EXIT("fstat"); printf("size = %ld, mode = %o\n", buf.st_size, buf.st_mode & 07777); ptr = mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, shmid, 0); if(ptr == MAP_FAILED) ERR_EXIT("mmap"); printf("name = %s, age = %d\n", ptr->name, ptr->age); close(shmid); return 0; } //读写打开xyz文件,mmap函数映射得到内存地址,并且是读保护,读出一个结构的数据。

运行结果:

➜ shm_open ./shm_write size = 36, mode = 664 ➜ shm_open ./shm_read size = 36, mode = 664 name = test, age = 20 //读出和写入相同,或者可以用od命令查看文件内容 ➜ shm od -c -A d /dev/shm/xyz 0000000 t e s t \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000016 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000032 024 \0 \0 \0 0000036