前面总结了POSIX消息队列的8个函数,接下来进入POSIX共享内存的学习,主要的一系列函数有shm_open,shm_unlink等函数。
1.POSIX共享内存相关函数
(1)shm_open函数
- 功能:用来创建或打开一个共享内存对象
- 原型:
int shm_open(const char *name, int oflag, mode_t mode); - 参数:
- name:共享内存对象的名字
- oflag:与open函数类似,可以是O_RDONLY, O_RDWR, 还可以按位或上O_CREAT, O_EXCL, O_TRUNC等。
- mode:此参数总是需要设置,如果oflag没有指定了O_CREAT,可以指定为0
- 返回值:成功返回非负整数文件描述符;失败返回-1
1)使用该函数创建一个共享内存:
int shmid;
shmid = shm_open(“/xyz”, O_CREAT | O_RDWR, 0666);
2)成功创建之后,同POSIX消息队列一样,需要挂载到某个目录下,才能看到。不同点,这些操作由系统自动完成了,挂载到了/dev/shm/目录下。
3)POSIX共享内存不同于POSIX消息队列可以在创建的时候指定大小,这里可以调用下面的函数,在创建共享内存的时候指定其大小:
(2)ftruncate函数
- 功能:修改共享内存对象大小
- 原型:int ftruncate(int fd, off_t length);
- 参数:
- fd:文件描述符
- length:长度
- 返回值:成功返回0;失败返回-1
1)该函数不仅可以用来修改共享内存对象大小,也可以用来修改文件对象的大小
2)用此函数修改上述创建共享内存的程序。
定义一个学生信息结构体:
typedef struct stu {
char name[32];
int age;
}STU;
指定共享内存大小为STU的大小:
ftruncate(shmid, sizeof(STU));
(3)fstat函数
- 功能:获取共享内存对象信息
- 原型:int fstat(int fd, struct stat *buf);
- 参数:
- fd:文件描述符
- buf:返回共享内存状态
- 返回值:成功返回0;失败返回-1
其中结构体struct stat如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ 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 */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; |
1)该函数不仅可以用来获取共享内存状态信息,也可以获取文件状态信息。
2)使用该函数来获取共享内存对象的状态信息:
struct stat buf; //将其状态信息保存在buf中
fstat(shmid, &buf);
printf(“size=%ld mode=%o\n”, buf.st_size, buf.st_mode & 0777);//打印部分信息
综上可以得到一个如下创建共享内存的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) typedef struct stu { char name[32]; int age; }STU; int main() { int shmid; 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"); struct stat buf; 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; } |
(4)shm_unlink函数
- 功能:删除一个共享内存对象
- 原型:int shm_unlink(const char *name);
- 参数:
- name:共享内存对象的名字
- 返回值:成功返回0;失败返回-1
1)使用该函数删除一个共享内存对象:
shm_unlink(“/xyz”);
(5)共享内存对象的映射mmap
我们创建了一块共享内存对象之后,需要将该内存对象映射到进程地址空间中,才可以使用。映射的方法采用的是我们之前学习过的mmap函数:
- 功能:将共享内存对象映射到进程地址空间
- 原型:
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); - 参数:
- addr:要映射的起始地址,通常指定为NULL,让内核自动选择
- len:映射到进程地址空间的字节数
- prot:映射区保护方式
- flags:标志
- fd:文件描述符
- offset:从文件头开始的偏移量
- 返回值:成功返回映射到的内存区的起始地址;失败返回-1
1)该函数不仅可以用来映射共享内存对象,还可以用来映射文件
2)将创建后的共享内存对象映射到地址空间:
STU *p;
p = mmap(NULL, sizeof(STU), PROT_WRITE, MAP_SHARED, shmid, 0);
//如果映射成功,往共享内存中写入数据
strcpy(p->name, “Leo”);
p->age = 18;
3)由上可得到如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
//03shm_write.c #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) typedef struct stu { char name[32]; int age; }STU; int main() { int shmid; shmid = shm_open("/xyz", O_RDWR, 0); if (shmid == -1) ERR_EXIT("shm_open"); printf("shm_open succ\n"); struct stat buf; if (fstat(shmid, &buf) == -1) ERR_EXIT("fstat"); STU *p; p = mmap(NULL, buf.st_size, PROT_WRITE, MAP_SHARED, shmid, 0); if (p == MAP_FAILED) ERR_EXIT("mmap"); strcpy(p->name, "Leo"); p->age = 18; ERR_EXIT("mmap"); close(shmid); return 0; } |
(6)上述程序运行完之后,数据是否写进了共享内存区呢?我们写一个程序观察能否从共享内存区中读出我们所写的数据就知道了(其实也可以使用od -c xyz命令查看):
1)以读的方式打开一个共享内存对象:
int shmid;
shmid = shm_open(“/xyz”, O_RDONLY, 0);
2)然后以读的方式进程映射:
STU *p;
p = mmap(NULL, sizeof(STU), PROT_READ, MAP_SHARED, shmid, 0);
//映射成功之后,得到一个指针p,就可以将p所指的内存信息打印输出
printf(“name=%s age= %d\n”, p->name, p->age);
3)由上分析可得如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//04shm_read.c #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) typedef struct stu { char name[32]; int age; }STU; int main() { int shmid; shmid = shm_open("/xyz", O_RDONLY, 0); if (shmid == -1) ERR_EXIT("shm_open"); printf("shm_open succ\n"); struct stat buf; if (fstat(shmid, &buf) == -1) ERR_EXIT("fstat"); STU *p; p = mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, shmid, 0); if (p == MAP_FAILED) ERR_EXIT("mmap"); printf("name=%s age=%d\n", p->name, p->age); close(shmid); return 0; } |
运行输出:
1 2 3 |
[root@VM_198_209_centos 35_demon_posix]# ./04shm_read shm_open succ name=Leo age=18 |
结果显示,表明映射成功,并写入了数据。
附:
(1)这些函数在编译的时候需要加上-lrt选项来连接librt库。
(2)更有如下选项及对应的库名:
- -lz:libz——压缩库(Z)
- -lrt:librt——实时库(real time)
- -lm:libm——数学库(math)
- -lc:libc——标准C库(C lib)
- -lpthread:libpthread——多线程库(pthread)