共享内存

共享内存

共享内存是一种最为高效的进程间通信方式,进程可以直接读写共享内存而不需要数据的拷贝。

内核专门留出了一块内存区,这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。

由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。

  • 创建共享内存

SYNOPSIS

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

key: IPC_PRIVATE - 会建立新共享内存对象
size: 共享内存大小
shmflg: 权限位
return - 共享内存段标识符

  • 映射共享内存

把创建的共享内存映射到具体的进程空间去。

SYNOPSIS

void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid: 要映射的共享内存标识符
shmaddr: 映射的地址
shmflg: SHM_RDONLY: 共享内存只读; 0: 可读写
return - 被映射的段地址

  • 撤销共享内存

SYNOPSIS

int shmdt(const void *shmaddr);

shmaddr: 被映射的共享内存地址

  • 共享内存的控制

SYNOPSIS

   #include <sys/ipc.h>
   #include <sys/shm.h>

   int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid: 共享内存标识符
cmd:
IPC_STAT - 得到共享内存的状态。把共享内存的shmid_ds结构复制到buf中。
IPC_SET - 改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内。
IPC_RMID - 删除这片共享内存
buf: shmid_ds结构体。

struct shmid_ds {
           struct ipc_perm shm_perm;    /* Ownership and permissions */
           size_t          shm_segsz;   /* Size of segment (bytes) */
           time_t          shm_atime;   /* Last attach time */
           time_t          shm_dtime;   /* Last detach time */
           time_t          shm_ctime;   /* Last change time */
           pid_t           shm_cpid;    /* PID of creator */
           pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
           shmatt_t        shm_nattch;  /* No. of current attaches */
           ...
};

实例1:

这里要介绍的一个命令是 ipcs,这是用于报告进程间通信机制状态的命令。它可以查看共享内存、消息队列等各种进程间通信机制的情况。
也可通过shell执行ipcrm -m shmid来删除共享内存。

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSIZE 2048

int main()
{
	int shmid;
	char *shmaddr;

	if((shmid = shmget(IPC_PRIVATE, BUFSIZE, 0666)) < 0){
		perror("shmget");
		exit(1);
	}else{
		printf("created shared-memory: %d\n", shmid);
	}
	system("ipcs -m");

	if((shmaddr = shmat(shmid, 0, 0)) < (char *)0){
		perror("shmat");
		exit(1);
	}else{
		printf("attached shared-memory\n");
	}
	system("ipcs -m");

	if(shmdt(shmaddr) < 0){
		perror("shmdt");
		exit(1);
	}else{
		printf("detached shared-memory\n");
	}
	system("ipcs -m");

	if(shmctl(shmid, IPC_RMID, NULL) < 0){
		perror("shmctl");
		exit(1);
	}else{
		printf("delete shared-memory\n");
	}
	system("ipcs -m");

	exit(0);
}

结果如下:

xxx@xxx-pc:~/Documents$ ./a.out 
created shared-memory: 2162702

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 2162702    xxx        666        2048       0                       

attached shared-memory

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 2162702    xxx        666        2048       1                       

detached shared-memory

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 2162702    xxx        666        2048       0                       

delete shared-memory

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status

ftok()函数

系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

SYNOPSIS

   #include <sys/types.h>
   #include <sys/ipc.h>

   key_t ftok(const char *pathname, int proj_id);

proj_id: 取值(1~255)

在一般的UNIX实现中,是将文件的索引节点号取出(通过ls -i),前面加上子序号(proj_id)得到key_t的返回值。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值。

实例2:

实现一进程写共享内存,一进程读共享内存并且读好之后释放共享内存空间。

写进程p2:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

#define BUFSIZE 2048

int main()
{
	int shmid;
	char *shmaddr;
	key_t key;
	char *str = "shmat, shmdt - System V shared memory operations\n";

	if((key = ftok("/tmp", 0x03)) < 0){
		perror("ftok error");
		exit(1);
	}

	if((shmid = shmget(key, BUFSIZE, IPC_CREAT|IPC_EXCL|0666)) < 0){
		perror("shmget");
		exit(1);
	}

	if((shmaddr = shmat(shmid, 0, 0)) < (char *)0){
		perror("shmat");
		exit(1);
	}
	
	memcpy(shmaddr, str, strlen(str));

	if(shmdt(shmaddr) < 0){
		perror("shmdt");
		exit(1);
	}
/*
	if(shmctl(shmid, IPC_RMID, NULL) < 0){
		perror("shmctl");
		exit(1);
	}
*/

	exit(0);
}

读进程p1:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
	int shmid;
	char *shmaddr;
	key_t key;

	if((key = ftok("/tmp", 0x03)) < 0){
		perror("ftok error");
		exit(1);
	}
        
            /* get shared-memory according to key */
	if((shmid = shmget(key, 0, 0)) < 0){
		perror("shmget");
		exit(1);
	}

	if((shmaddr = shmat(shmid, 0, 0)) < (char *)0){
		perror("shmat");
		exit(1);
	}

	printf("%s\n", shmaddr);

	if(shmdt(shmaddr) < 0){
		perror("shmdt");
		exit(1);
	}

	if(shmctl(shmid, IPC_RMID, NULL) < 0){
		perror("shmctl");
		exit(1);
	}

	exit(0);
}

结果如下:

xxx@xxx-pc:~/Documents$ ./p2
xxx@xxx-pc:~/Documents$ ipcs -m

------ Shared Memory Segments --------
0x03080001 2359308    xxx        666        2048       0                       

xxx@xxx-pc:~/Documents$ ./p1
shmat, shmdt - System V shared memory operations

如果先运行./p1,共享内存不存在,报错:

xxx@xxx-pc:~/Documents$ ./p1
shmget: No such file or directory

如果重复运行./p2,此时共享内存已存在,加了IPC_EXCL标志,会报错:

xxx@xxx-pc:~/Documents$ ./p2
xxx@xxx-pc:~/Documents$ ./p2
shmget: File exists
posted @ 2017-04-28 14:53  fuluwwa  阅读(847)  评论(0编辑  收藏  举报