系统V共享内存范例
本部分将给出系统
V
共享内存
API
的使用方法,并对比分析系统
V
共享内存机制与
mmap()
映射普通文件实现共享内存之间的差异,首先给出两个进程通过系统
V
共享内存通信的范例:
第一个是写数据:
/***** testwrite.c *******/
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{ //定义了一个people 数据结构
char name[4];
int age;
} people;
main(int argc, char** argv)
{
int shm_id,i; //定义了共享内存id
key_t key; //关键字key
char temp;
people *p_map;
char* name = "/dev/shm/myshm2";//路径名
key = ftok(name,0);/*ftok 根据路径名 name 和pjojectid 0 创建关键字key*/
/*调用shmget,创建一块共享内存区域*/
if(key==-1)
perror("ftok error");
shm_id=shmget(key,4096,IPC_CREAT);
/*共享内存的大小为4K 内存是以页为单位分配的 */
if(shm_id==-1)
{
perror("shmget error");
return;
}
/*将这块共享内存区附加到自己的内存段*/
p_map=(people*)shmat(shm_id,NULL,0);
temp='a';
for(i = 0;i<10;i++)
{
temp+=1;
memcpy((*(p_map+i)).name,&temp,1);
(*(p_map+i)).age=20+i;
}
/*写数据完毕,将其从自己的内存段中“删除”出去*/
if(shmdt(p_map)==-1)//出错为-1 返回
perror(" detach error ");
}
函数生成的共享内存是跨进程的,其存在时间与内核一样,只要系统没有重新启动,共享内存就一直存在,而
IPC_CREAT|IPC_EXCL
表示要生成新的共享内存
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) 是全局读取和写入许可。
-----------------------------------------------
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 所致的共享内存已经删除。
-----------------------------------------------
成功返回共享内存的标识符;不成功返回 -1 , errno 储存错误原因。
EINVAL 参数 size 小于 SHMMIN 或大于 SHMMAX 。
EEXIST 预建立 key 所致的共享内存,但已经存在。
EIDRM 参数 key 所致的共享内存已经删除。
ENOSPC
超过了系统允许建立的共享内存的最大值
(SHMALL )
。
ENOENT
参数
key
所指的共享内存不存在,参数
shmflg
也未设
IPC_CREAT
位。
EACCES 没有权限。
ENOMEM 核心内存不足。
EACCES 没有权限。
ENOMEM 核心内存不足。
使用
shmat()
放置一个或多个进程
/
线程在共享内存中,也可以用
shmctl()
来获取信息或者控制共享区域。
Shmat(int shmid, void *addr, int flag)
shmat/shmdt
系统调用
功能描述:
共享存储器的执行方式是将一个储存器区段标记为共用,这时各进程可以把这个区段映射到该进程本身的虚拟地址里。建立共享存储器可通过 shmget 系统调用, shmget 执行后,核心程序就保留一块指定大小的空间,同时关于此共享存储器的一切数据,如区段的长度,区段的存取权,区段建立者的进程识别码等存入一个叫 shmid_ds 的结构。现在共享存储器虽然已经建立了,可是仍无法连上它,这时就须通过 shmat 系统调用得到一个指向共享存储器基址的指针,通过此指针,就可以如同于操作一般存储器似的取用共享存储器。 shmdt 进行相反的工作,用来脱离已连上的共享存储器。
功能描述:
共享存储器的执行方式是将一个储存器区段标记为共用,这时各进程可以把这个区段映射到该进程本身的虚拟地址里。建立共享存储器可通过 shmget 系统调用, shmget 执行后,核心程序就保留一块指定大小的空间,同时关于此共享存储器的一切数据,如区段的长度,区段的存取权,区段建立者的进程识别码等存入一个叫 shmid_ds 的结构。现在共享存储器虽然已经建立了,可是仍无法连上它,这时就须通过 shmat 系统调用得到一个指向共享存储器基址的指针,通过此指针,就可以如同于操作一般存储器似的取用共享存储器。 shmdt 进行相反的工作,用来脱离已连上的共享存储器。
用法:
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
参数:
shmid :共享存储器标识。
shmaddr :指向共享存储器连上的起始地址。对于 shmat() 函数,如果为 NULL 将由系统选择合适的地址 。
shmflg :共享存储器相关的标志,可能值有
SHM_RND // 如果 shmaddr 不为 NULL ,将会进位到最低可用 SHMLBA 地址。
SHM_RDONLY // 共享区段以只读的方式映射到进程的地址空间。
SHM_REMAP // 如果本标志被指定,意味着替代掉与指定参数重叠的现存共享区段的映射。
shmid :共享存储器标识。
shmaddr :指向共享存储器连上的起始地址。对于 shmat() 函数,如果为 NULL 将由系统选择合适的地址 。
shmflg :共享存储器相关的标志,可能值有
SHM_RND // 如果 shmaddr 不为 NULL ,将会进位到最低可用 SHMLBA 地址。
SHM_RDONLY // 共享区段以只读的方式映射到进程的地址空间。
SHM_REMAP // 如果本标志被指定,意味着替代掉与指定参数重叠的现存共享区段的映射。
返回说明:
shmat() 成功执行时,返回共享存储器的基地址,失败返回 (void *)-1 ;
shmdt() 成功执行时,返回 0 ,失败返回 -1 。
shmat() 成功执行时,返回共享存储器的基地址,失败返回 (void *)-1 ;
shmdt() 成功执行时,返回 0 ,失败返回 -1 。
EACCES
:对于所请求的连上类型,进程没有足够的权限,并且不
有
CAP_IPC_OWNER
权能
EINVAL
:参数无效
ENOMEM
:内存不足,无法分配描述词或页表。
使用
shmdt()
从共享区域中分离
使用
shmctl()
解除分配空间
memcpy
:
函数原型
void *memcpy(void *dest, const void *src, size_t n)
函数功能 : 字符串拷贝
函数返回 : 指向 dest 的指针
参数说明 : src- 源字符串, n- 拷贝的最大长度
所属文件 : <string.h> , <mem.h>
函数功能 : 字符串拷贝
函数返回 : 指向 dest 的指针
参数说明 : src- 源字符串, n- 拷贝的最大长度
所属文件 : <string.h> , <mem.h>
/*读进程首先要得到那块内存块,这个时候,ftok根据文件名和ID创建的key就
很用了,步骤同创建是一样的*/
/********** testread.c ************/
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
} people;
main(int argc, char** argv)
{
int shm_id,i;
key_t key;
people *p_map;
char* name = "/dev/shm/myshm2";
key = ftok(name,0);
if(key == -1)
perror("ftok error");
shm_id = shmget(key,4096,IPC_CREAT);
if(shm_id == -1)
{
perror("shmget error");
return;
}
p_map = (people*)shmat(shm_id,NULL,0);
for(i = 0;i<10;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 ");
}