#include <sys/ipc.h>
#include <sys/shm.h>
int
shmget(key_t key, size_t size, int shmflg);
key_t key
-----------------------------------------------
key标识共享内存的键值:
0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
Linux系统中的所有表示System V中IPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找System V中IPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。
通常,都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现。
int size(单位字节Byte)
-----------------------------------------------
size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。
int shmflg
-----------------------------------------------
shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。对于用户的读取和写入许可指定
SHM_R和
SHM_W,(SHM_R>3)和(SHM_W>3)是一组读取和写入许可,而(SHM_R>6)和(SHM_W>6)是全局读取和写入许可。
返回值
-----------------------------------------------
成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。
EINVAL 参数size小于SHMMIN或大于SHMMAX。
EEXIST 预建立key所致的共享内存,但已经存在。
EIDRM 参数key所致的共享内存已经删除。
ENOSPC 超过了系统允许建立的共享内存的最大值(SHMALL )。
ENOENT 参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
EACCES 没有权限。
ENOMEM 核心内存不足。
struct shmid_ds
-----------------------------------------------
shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。
include/linux/shm.h
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
struct ipc_perm
-----------------------------------------------
对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。
struct ipc_perm {
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
用于Linux进程通信共享内存。共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成。
shmget函数原型
shmget(得到一个共享内存标识符或创建一个共享内存对象)
| ||
所需头文件
|
#include <sys/ipc.h>
#include <sys/shm.h>
| |
函数说明
|
得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符
| |
函数原型
|
int shmget(key_t key, size_t size, int shmflg)
| |
函数传入值
|
key
|
0(IPC_PRIVATE):会建立新共享内存对象
|
大于0的32位整数:视参数shmflg来确定操作。通常要求此值来源于ftok返回的IPC键值
| ||
size
|
大于0的整数:新建的共享内存大小,以字节为单位
| |
0:只获取共享内存时指定为0
| ||
shmflg
|
0:取共享内存标识符,若不存在则函数会报错
| |
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
| ||
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错
| ||
函数返回值
|
成功:返回共享内存的标识符
| |
出错:-1,错误原因存于error中
| ||
附加说明
|
上述shmflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限
| |
错误代码
|
EINVAL:参数size小于SHMMIN或大于SHMMAX
EEXIST:预建立key所指的共享内存,但已经存在
EIDRM:参数key所指的共享内存已经删除
ENOSPC:超过了系统允许建立的共享内存的最大值(SHMALL)
ENOENT:参数key所指的共享内存不存在,而参数shmflg未设IPC_CREAT位
EACCES:没有权限
ENOMEM:核心内存不足
|
在Linux环境中,对开始申请的共享内存空间进行了初始化,初始值为0x00。
如果用shmget创建了一个新的消息队列对象时,则shmid_ds结构成员变量的值设置如下:
shm_lpid、shm_nattach、shm_atime、shm_dtime设置为0。
msg_ctime设置为当前时间。
shm_segsz设成创建共享内存的大小。
shmflg的读写权限放在shm_perm.mode中。
shm_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。
其他共享内存函数
shmat
shmat(把共享内存区对象映射到调用进程的地址空间)
| ||
所需头文件
|
#include <sys/types.h>
#include <sys/shm.h>
| |
函数说明
|
连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问
| |
函数原型
|
void *shmat(int shmid, const void *shmaddr, int shmflg)
| |
函数传入值
|
shmid
|
共享内存标识符
|
shmaddr
|
指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
| |
shmflg
|
SHM_RDONLY:为只读模式,其他为读写模式
| |
函数返回值
|
成功:附加好的共享内存地址
| |
出错:-1,错误原因存于error中
| ||
附加说明
|
fork后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach)
| |
错误代码
|
EACCES:无权限以指定方式连接共享内存
EINVAL:无效的参数shmid或shmaddr
ENOMEM:核心内存不足
|
shmdt函数原型
shmdt(断开共享内存连接)
| |
所需头文件
|
#include <sys/types.h>
#include <sys/shm.h>
|
函数说明
|
与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
|
函数原型
|
int shmdt(const void *shmaddr)
|
函数传入值
|
shmaddr:连接的共享内存的起始地址
|
函数返回值
|
成功:0
|
出错:-1,错误原因存于error中
| |
附加说明
|
本函数调用并不删除所指定的共享内存区,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)目前的进程
|
错误代码
|
EINVAL:无效的参数shmaddr
|
shmctl函数原型
shmctl(共享内存管理)
| ||
所需头文件
|
#include <sys/types.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
|
共享内存管理结构体。具体说明参见共享内存内核结构定义部分
| |
函数返回值
|
成功:0
| |
出错:-1,错误原因存于error中
| ||
错误代码
|
EACCESS:参数cmd为IPC_STAT,确无权限读取该共享内存
EFAULT:参数buf指向无效的内存地址
EIDRM:标识符为shmid的共享内存已被删除
EINVAL:无效的参数cmd或shmid
EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行
|
2共享内存应用范例
1、父子进程通信范例 父子进程通信范例,shm.c源代码如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#define SIZE 1024
int main()
{
int shmid ;
char *shmaddr ;
struct shmid_ds buf ;
int flag = 0 ;
int pid ;
shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;
if ( shmid < 0 )
{
perror("get shm ipc_id error") ;
return -1 ;
}
pid = fork() ;
if ( pid == 0 )
{
shmaddr = (char *)shmat( shmid, NULL, 0 ) ;
if ( (int)shmaddr == -1 )
{
perror("shmat addr error") ;
return -1 ;
}
strcpy( shmaddr, "Hi, I am child process!\n") ;
shmdt( shmaddr ) ;
return 0;
} else if ( pid > 0) {
sleep(3 ) ;
flag = shmctl( shmid, IPC_STAT, &buf) ;
if ( flag == -1 )
{
perror("shmctl shm error") ;
return -1 ;
}
printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;
printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;
printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;
shmaddr = (char *) shmat(shmid, NULL, 0 ) ;
if ( (int)shmaddr == -1 )
{
perror("shmat addr error") ;
return -1 ;
}
printf("%s", shmaddr) ;
shmdt( shmaddr ) ;
shmctl(shmid, IPC_RMID, NULL) ;
}else{
perror("fork error") ;
shmctl(shmid, IPC_RMID, NULL) ;
}
return 0 ;
}
编译 gcc shm.c –o shm。
执行 ./shm,执行结果如下:
shm_segsz =1024 bytes
shm_cpid = 9503
shm_lpid = 9504
Hi, I am child process!
2、多进程读写范例
多进程读写即一个进程写共享内存,一个或多个进程读共享内存。下面的例子实现的是一个进程写共享内存,一个进程读共享内存。
(1)下面程序实现了创建共享内存,并写入消息。
shmwrite.c源代码如下:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
typedef struct{
char name[8];
int age;
} people;
int main(int argc, char** argv)
{
int shm_id,i;
key_t key;
char temp[8];
people *p_map;
char pathname[30] ;
strcpy(pathname,"/tmp") ;
key = ftok(pathname,0x03);
if(key==-1)
{
perror("ftok error");
return -1;
}
printf("key=%d\n",key) ;
shm_id=shmget(key,4096,IPC_CREAT|IPC_EXCL|0600);
if(shm_id==-1)
{
perror("shmget error");
return -1;
}
printf("shm_id=%d\n", shm_id) ;
p_map=(people*)shmat(shm_id,NULL,0);
memset(temp, 0x00, sizeof(temp)) ;
strcpy(temp,"test") ;
temp[4]='0';
for(i = 0;i<3;i++)
{
temp[4]+=1;
strncpy((p_map+i)->name,temp,5);
(p_map+i)->age=0+i;
}
shmdt(p_map) ;
return 0 ;
}
(2)下面程序实现从共享内存读消息。
shmread.c源代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{
char name[8];
int age;
} people;
int main(int argc, char** argv)
{
int shm_id,i;
key_t key;
people *p_map;
char pathname[30] ;
strcpy(pathname,"/tmp") ;
key = ftok(pathname,0x03);
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key=%d\n", key) ;
shm_id = shmget(key,0, 0);
if(shm_id == -1)
{
perror("shmget error");
return -1;
}
printf("shm_id=%d\n", shm_id) ;
p_map = (people*)shmat(shm_id,NULL,0);
for(i = 0;i<3;i++)
{
printf( "name:%s\n",(*(p_map+i)).name );
printf( "age %d\n",(*(p_map+i)).age );
}
if(shmdt(p_map) == -1)
{
perror("detach error");
return -1;
}
return 0 ;
}
(3)编译与执行
① 编译gcc shmwrite.c -o shmwrite。
② 执行./shmwrite,执行结果如下:
key=50453281
shm_id=688137
③ 编译gcc shmread.c -o shmread。
④ 执行./shmread,执行结果如下:
key=50453281
shm_id=688137
name:test1
age 0
name:test2
age 1
name:test3
age 2
⑤ 再执行./shmwrite,执行结果如下:
key=50453281
shmget error: File exists
⑥ 使用ipcrm -m 688137删除此共享内存。
shmget() -- 建立共享内存