linux进程间通信之System V共享内存详解及代码示例
共享内存是最快最为高效的进程间通信方式,当共享内存映射到共享它的某个进程的地址空间后,进程间的数据传递就不再牵扯到内核,进程可以直接读取内核,不需要通过内核系统调用进行数据拷贝。一般使用情况,从共享内存中写入或读取数据的进程间需要做同步,例如通过信号量,互斥锁去同步。
共享内存有System V 共享内存和Posix共享内存,本文介绍System V 共享内存。
System V共享内存头文件及相关函数原型:
#include <sys/shm.h>
int shmget(key_t key, size_t size, int oflag);
功能:创建一个新的共享内存区,或者访问一个已存在的共享内存区。
返回值:若成功返回共享内存区的标识符,若出错返回-1
参数:key是键值,可以是用ftok生成,也可以用IPC_PRIVATE; size是以字节为单位指定的内存区大小,当创建一个新的共享内存区时,需指定不为0的size值,并且初始化这些size字节为0,当使用一个已存在的共享内存区时,size值需为0; oflag是读写权限,还可以与IPC_CREAT或IPC_CREAT|IPC_EXCL按位或。
void *shmat(int shmid, const void *shmaddr, int flag);
功能:把由shmget创建的共享内存区映射到调用进程的地址空间。
返回值:若成功返回映射区的起始地址,若出错返回-1。
参数:shmid是由shmget返回的标识符;shmaddr是指定地址指针,如果是一个空指针,系统会自动分配地址,这是可移植性最好用法,是推荐用法,如果shmaddr是一个非空指针,返回的映射区地址取决于第三个参数flag是否指定了SHM_RND,如果没有指定SHM_RND,映射地址附接到由 shmaddr参数指定的地址,如果指定了SHM_RND,映射地址附接到由shmaddr参数指定的地址向下舍一个SHMLBA常值;flag默认为0表示可读写,若为SHM_RDONLY表示只读访问。
int shmdt(const void *shmaddr);
功能:关闭这个共享内存区映射,当进程终止时,共享内存映射会自动关闭,但并不是删除,可以通过shmctl函数的IPC_RMID命令删除。
返回值:成功返回0,出错返回-1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:操作共享内存区
返回值:成功返回0,出错返回-1。
参数: cmd参数有三个命令,IPC_RMID从系统中删除shmid标示的共享内存区,IPC_SET给所指定的共享内存区设置shmid_ds结构的三个成员shm_perm.uid,shm_perm.gid和shm_perm.mode,这三个成员的值来自buf参数指向的结构中的对应成员,IPC_STAT是从共享内存区取出shmid_ds结构并保存在参数buf结构。
一般的编程步骤如下:
1. 创建共享内存:
通过函数shmget()创建共享内存
2. 映射共享内存:
通过函数shmget()把创建的共享内存映射到进程
3. 使用共享内存:
4. 撤销共享内存映射
函数shmdt()
5. 删除共享内存映射:
函数shmctl()
代码举例shm_test.c: 父进程从终端读取数据放到共享内存,子进程从共享内存读取数据显示到终端
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #define BUFFER_SIZE 1024
- int main() {
- pid_t pid;
- int shmid;
- char *shm_addr;
- char flag[]="Parent";
- char buff[BUFFER_SIZE];
- // 创建当前进程的私有共享内存
- if ((shmid=shmget(IPC_PRIVATE,BUFFER_SIZE,0666))<0) {
- perror("shmget");
- exit(1);
- } else
- printf("Create shared memory: %d.\n",shmid);
- // ipcs 命令往标准输出写入一些关于活动进程间通信设施的信息
- // -m 表示共享内存
- printf("Created shared memory status:\n");
- system("ipcs -m");
- if((pid=fork())<0) {
- perror("fork");
- exit(1);
- }else if (pid==0) {
- // 自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr
- if ((shm_addr=shmat(shmid,0,0))==(void*)-1) {
- perror("Child:shmat");
- exit(1);
- }else
- printf("Child: Attach shared-memory: %p.\n",shm_addr);
- printf("Child Attach shared memory status:\n");
- system("ipcs -m");
- // 比较shm_addr,flag的长度为strlen(flag)的字符
- // 当其内容相同时,返回0
- // 否则返回(str1[n]-str2[n])
- while (strncmp(shm_addr,flag,strlen(flag))) {
- printf("Child: Waiting for data...\n");
- sleep(10);
- }
- strcpy(buff,shm_addr+strlen(flag));
- printf("Child: Shared-memory: %s\n",buff);
- // 删除子进程的共享内存映射地址
- if (shmdt(shm_addr)<0) {
- perror("Child:shmdt");
- exit(1);
- }else
- printf("Child: Deattach shared-memory.\n");
- printf("Child Deattach shared memory status:\n");
- system("ipcs -m");
- }else{
- sleep(1);
- // 自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr
- if ((shm_addr=shmat(shmid,0,0))==(void*)-1) {
- perror("Parent:shmat");
- exit(1);
- }else
- printf("Parent: Attach shared-memory: %p.\n",shm_addr);
- printf("Parent Attach shared memory status:\n");
- system("ipcs -m");
- // shm_addr为flag+stdin
- sleep(1);
- printf("\nInput string:\n");
- fgets(buff,BUFFER_SIZE-strlen(flag),stdin);
- strncpy(shm_addr,flag,strlen(flag));
- strncpy(shm_addr+strlen(flag),buff,strlen(buff));
- // 删除父进程的共享内存映射地址
- if (shmdt(shm_addr)<0) {
- perror("Parent:shmdt");
- exit(1);
- }else
- printf("Parent: Deattach shared-memory.\n");
- printf("Parent Deattach shared memory status:\n");
- system("ipcs -m");
- // 保证父进程在删除共享内存前,子进程能读到共享内存的内容
- waitpid(pid,NULL,0);
- // 删除共享内存
- if (shmctl(shmid,IPC_RMID,NULL)==-1) {
- perror("shmct:IPC_RMID");
- exit(1);
- }else
- printf("Delete shared-memory.\n");
- printf("Child Delete shared memory status:\n");
- system("ipcs -m");
- printf("Finished!\n");
- }
- exit(0);
- }
运行结果:
$ ./a.out
Create shared memory: 65536.
Created shared memory status:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 65536 dongxw 666 2048 0
Child: Attach shared-memory: 0x7fc3519fe000.
Child Attach shared memory status:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 65536 dongxw 666 2048 1
Child: Waiting for data...
Parent: Attach shared-memory: 0x7fc3519fe000.
Parent Attach shared memory status:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 65536 dongxw 666 2048 2
Input string:
Child: Waiting for data...
Child: Waiting for data...
this is data
Parent: Deattach shared-memory.
Parent Deattach shared memory status:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 65536 dongxw 666 2048 1
Child: Shared-memory: this is data
Child: Deattach shared-memory.
Child Deattach shared memory status:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 65536 dongxw 666 2048 0
Delete shared-memory.
Child Delete shared memory status:
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
Finished!
可以看到红色标注从终端输入的数据“this is data”, 父进程读取写入共享内存区,子进程从共享内存读取再显示到终端红色标注“Child:Shared-memory:this is data”