前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上相似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open建立共享内存区后调用mmap进行内存区的映射,而System V共享内存区则是调用shmget建立共享内存区而后调用shmat进行内存区的映射。ios
对每一个System V共享内存区,内核会维护一个shmid_ds的数据结构,Linux 2.6.18 中的定义以下:数据结构
<bits/shm.h> /* 链接共享内存区的进程数的数据类型 */ typedef unsigned long int shmatt_t; struct shmid_ds { struct ipc_perm shm_perm; /* operation permission struct */ size_t shm_segsz; /* 共享存储段的最大字节数 */ __time_t shm_atime; /* time of last shmat() */ __time_t shm_dtime; /* time of last shmdt() */ __time_t shm_ctime; /* time of last change by shmctl() */ __pid_t shm_cpid; /* pid of creator */ __pid_t shm_lpid; /* pid of last shmop */ shmatt_t shm_nattch; /* 链接共享内存区的进程数 */ //保留字段 #if __WORDSIZE == 32 unsigned long int __unused1; unsigned long int __unused2; unsigned long int __unused3; #endif unsigned long int __unused4; unsigned long int __unused5; };
下面是shmget函数的接口以及说明:函数
#include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); //成功返回共享内存标识符,失败返回-1
shmget函数用于建立或打开一个共享内存区对象,shmget成功调用会返回一个共享内存区的标识符,供其它的共享内存区操做函数使用。测试
key:用于建立共享内存区的键值,这个在前面其余System IPC建立的时候已经讨论过了,System IPC都有一个key,做为IPC的外部标识符,建立成功后返回的描述符做为IPC的内部标识符使用。key的主要目的就是使不一样进程在同一IPC汇合。key具体说能够有三种方式生成:
ui
size:指定建立共享内存区的大小,单位是字节。若是实际操做为建立一个共享内存区时,必须指定一个非0值,若是实际操做是访问一个已存在的共享内存区,那么size应为0。spa
shmflg:指定建立或打开消息队列的标志和读写权限(ipc_perm中的mode成员)。咱们知道System V IPC定义了本身的操做标志和权限设置标志,并且都是经过该参数传递,这和open函数存在差异,open函数第三个参数mode用于传递文件的权限标志。System V IPC的操做标志包含:IPC_CREAT,IPC_EXCL。读写权限以下图:
指针
图1 System V共享内存区的读写权限标志code
System V共享内存区在建立后,该size大小的内存区会被初始化为0,这和POSIX共享内存不一样,POSIX标准并无规定新建立的POSIX共享内存区的初始内容。server
经过shmctl建立或打开共享内存区对象后,并无将该共享内存区映射到调用进程的地址空间中,因此没法访问该共享内存区,须要经过shmat函数,将该共享内存区链接到调用进程的地址空间中,才能进行访问,当该进程完成对该共享内存区的访问后,能够调用shmdt断接这个共享内存区,固然进程结束后会自动断接全部链接的共享内存区。下面是shmat和shmdt函数的接口以及说明:对象
#include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg); //成功返回映射区的起始地址,失败返回-1 int shmdt(const void *shmaddr); //成功返回0,失败返回-1
shmat用于将一个共享内存区链接到调用进程的地址空间中。
shmid:打开的System V共享内存对象的标识符;
shmaddr和shmflg参数共同决定了共享内存区链接到调用进程的具体地址,规则以下:
shmflg:除了上面说的SHM_RND外,还有能够指定SHM_RDONLY标志,限定只读访问。通常该标志置为0。
shmdt用于将一个共享内存区从该进程内断接,当一个进程终止时,它链接的全部共享内存区会自动断接。
shmctl函数能够对共享内存区进行多种控制操做,下面是shmctl函数的接口以及说明:
#include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf); //成功返回0,失败返回-1
shmid:共享内存区的标识符。
cmd:对共享内存区控制操做的命令,Open Group 的SUS定义了一下三个操做命令:
在Linux中还定义了其余的控制命令,如:IPC_INFO,SHM_INFO,SHM_LOCK,SHM_UNLOCK等,具体能够参考Linux手册。
下面是建立System V共享内存区和查看其属性的测试代码:
#include <iostream> #include <cstring> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/shm.h> using namespace std; #define PATH_NAME "/tmp/shm" int main() { int fd; if ((fd = open(PATH_NAME, O_CREAT, 0666)) < 0) { cout<<"open file "<<PATH_NAME<<"failed."; cout<<strerror(errno)<<endl; return -1; } close(fd); key_t key = ftok(PATH_NAME, 0); int shmID; if ((shmID = shmget(key, sizeof(int), IPC_CREAT | 0666)) < 0) { cout<<"shmget failed..."<<strerror(errno)<<endl; return -1; } shmid_ds shmInfo; shmctl(shmID, IPC_STAT, &shmInfo); cout<<"shm key:0x"<<hex<<key<<dec<<endl; cout<<"shm id:"<<shmID<<endl; cout<<"shm_segsz:"<<shmInfo.shm_segsz<<endl; cout<<"shm_nattch:"<<shmInfo.shm_nattch<<endl; return 0; }
执行结果以下:
shm key:0x80e7 shm id:5898284 shm_segsz:4 //共享内存区的大小 shm_nattch:0 //共享内存区的链接数目
经过ipcs命令查看该新建立的共享内存区对象:
[root@idcserver program]# ipcs -m -i 5898284 Shared memory Segment shmid=5898284 uid=0 gid=0 cuid=0 cgid=0 mode=0666 access_perms=0666 bytes=4 lpid=0 cpid=16422 nattch=0 att_time=Not set det_time=Not set change_time=Tue Aug 13 15:34:35 2013
和其中的System V IPC同样,System V共享内存也存在系统的限制,关于系统范围内对共享内存的限制,在Linux 2.6.18 <bits/shm.h>中定义了shminfo结构,该结构显示了系统内核的限制,以下:
#include <bits/shm.h> struct shminfo { unsigned long int shmmax; //一个共享内存区的最大字节数 unsigned long int shmmin; //一个共享内存区的最小字节数 unsigned long int shmmni; //系统范围内的共享内存区对象的最大个数 unsigned long int shmseg; //每一个进程链接的最大共享内存区的数目 unsigned long int shmall; //系统范围内的共享内存区的最大页数 unsigned long int __unused1; unsigned long int __unused2; unsigned long int __unused3; unsigned long int __unused4; };
在Linux 下shmctl中能够指定IPC_INFO来获取上面结构所示的系统范围内的限制。在Linux下,具体的限制值能够经过sysctl来查看,以下:
[root@idcserver program]# sysctl -a | grep shm ... kernel.shmmni = 4096 //系统范围内的共享内存区对象的最大个数 kernel.shmall = 4294967296 //系统范围内的共享内存区的最大页数 kernel.shmmax = 68719476736 //一个共享内存区的最大字节数
通常状况下不须要对System V共享内存区的系统限制进程修改,由于基本能够知足应用需求,若是要在系统范围内对内核限制进行修改,在Linux下面能够经过修改/etc/sysctl.conf 内核参数配置文件,而后配合sysctl命令来对内核参数进行设置。例以下面示例:
[root@idcserver program]#echo "kernel.shmmni= 1000" >>/etc/sysctl.conf [root@idcserver program]#sysctl -p [root@idcserver program]#sysctl -a |grep shm kernel.shmmni = 1000 kernel.shmall = 4294967296 kernel.shmmax = 68719476736
下面是System V共享内存区的使用示例,进程1经过共享内存区向进程2发送一条消息,对共享内存区的同步采用System V信号量来完成。以下:
//process 1 #include <iostream> #include <cstring> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/shm.h> #include <sys/sem.h> using namespace std; #define PATH_NAME "/tmp/shm" union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; int main() { int fd; if ((fd = open(PATH_NAME, O_RDONLY | O_CREAT, 0666)) < 0) { cout<<"open file "<<PATH_NAME<<"failed."; cout<<strerror(errno)<<endl; return -1; } close(fd); key_t keyShm = ftok(PATH_NAME, 0); key_t keySem = ftok(PATH_NAME, 1); int shmID, semID; if ((shmID = shmget(keyShm, sizeof(int), IPC_CREAT | 0666)) < 0) { cout<<"shmget failed..."<<strerror(errno)<<endl; return -1; } int *buf = (int *)shmat(shmID, 0, 0); if ((semID = semget(keySem, 1, IPC_CREAT | 0666)) < 0) { cout<<"semget failed..."<<strerror(errno)<<endl; return -1; } semun arg; arg.val = 0; //初始化信号量资源的数目为0 if (semctl(semID, 0, SETVAL, arg) < 0) { cout<<"semctl error "<<strerror(errno)<<endl; return -1; } struct sembuf buffer; buffer.sem_num = 0; buffer.sem_op = 1; buffer.sem_flg = 0; *buf = 111; cout<<"process 1:send "<<*buf<<endl; //将信号量资源加1,以表示共享内存区内已有资源 semop(semID, &buffer, 1); return 0; } //process 2 #include <iostream> #include <cstring> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/shm.h> #include <sys/sem.h> using namespace std; #define PATH_NAME "/tmp/shm" union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; int main() { int fd; if ((fd = open(PATH_NAME, O_RDONLY)) < 0) { cout<<"open file "<<PATH_NAME<<"failed."; cout<<strerror(errno)<<endl; return -1; } close(fd); key_t keyShm = ftok(PATH_NAME, 0); key_t keySem = ftok(PATH_NAME, 1); int shmID, semID; if ((shmID = shmget(keyShm, sizeof(int), 0)) < 0) { cout<<"shmget failed..."<<strerror(errno)<<endl; return -1; } int *buf = (int *)shmat(shmID, 0, 0); if ((semID = semget(keySem, 1, 0)) < 0) { cout<<"semget failed..."<<strerror(errno)<<endl; return -1; } struct sembuf buffer; buffer.sem_num = 0; buffer.sem_op = -1; buffer.sem_flg = 0; //得到信号量资源 semop(semID, &buffer, 1); cout<<"process 2:recv "<<*buf<<endl; return 0; }
测试结果为:
# ./send process 1:send 111 # ./recv process 2:recv 111Aug, 13 PM 22:18 @lab 今天貌似是个悲伤的日子。。。大家都在那啪啪啪,咱们只能在这撸呀撸。。。实验室今天还一块儿吃饭,很久没喝酒,喝了不到两瓶啤的就晕了,哎,老了。。。15号就要放假了,好好看书,准备找工做了。。。