库函数说明参考来自博友,代码示例属个人原创
一、共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成。下面的表格列出了这四个函数的 函 数 原型及其具体说明。
1. 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。
2. shmat函数原型
shmat(把共享内存区对象映射到调用进程的地址空间) | ||
所需头文件 | #include <sys/types.h> #include <sys/shm.h> | |
函数说明 | 连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问 | |
函数原型 | void *shmat(int shmid, const void *shmaddr, int shmflg) | |
函数传入值 | msqid | 共享内存标识符 |
shmaddr | 指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置 | |
shmflg | SHM_RDONLY:为只读模式,其他为读写模式 | |
函数返回值 | 成功:附加好的共享内存地址 | |
出错:-1,错误原因存于error中 | ||
附加说明 | fork后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach) | |
错误代码 | EACCES:无权限以指定方式连接共享内存 EINVAL:无效的参数shmid或shmaddr ENOMEM:核心内存不足 |
3. shmdt函数原型
shmat(断开共享内存连接) | |
所需头文件 | #include <sys/types.h> #include <sys/shm.h> |
函数说明 | 与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存 |
函数原型 | int shmdt(const void *shmaddr) |
函数传入值 | shmaddr:连接的共享内存的起始地址 |
函数返回值 | 成功:0 |
出错:-1,错误原因存于error中 | |
附加说明 | 本函数调用并不删除所指定的共享内存区,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)目前的进程 |
错误代码 | EINVAL:无效的参数shmaddr |
4. shmctl函数原型
shmctl(共享内存管理) | ||
所需头文件 | #include <sys/types.h> #include <sys/shm.h> | |
函数说明 | 完成对共享内存的控制 | |
函数原型 | int shmctl(int shmid, int cmd, struct shmid_ds *buf) | |
函数传入值 | msqid | 共享内存标识符 |
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:标识符为msqid的共享内存已被删除 EINVAL:无效的参数cmd或shmid EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行 |
二、代码如下
分为两个程序,一个程序获取共享内存,写入数据;另一个程序获取共享内存,读出数据;
当按下ctrl+c键结束程序,由shmset从系统中释放掉共享内存。
shmset.c
/*************************************************************************
> File Name: shmset.c
> Author: TANG
> Mail: tjcmail@126.com
> Created Time: 2018年04月02日 星期一 16时29分10秒
************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
/*共享内存结构*/
typedef struct{
char name[12];
char age;
}msg_t;
/*共享内存地址*/
typedef struct{
int shmid;
void *addr;
}addr_t;
/*变量声明*/
int RUNING = 1;
/* 功能:自定义信号处理函数
* 参数:
* 返回值:无
* */
void handler(int snum)
{
RUNING = 0;
}
/* 功能:初始化共享内存
* 参数:无
* 返回值:
* */
addr_t *init_memory(addr_t *m_addr)
{
key_t key = 0;
int shmid = 0;
void *addr = NULL;
int flag = IPC_CREAT|0664;
/*获取键值*/
if(-1 == (key=ftok(".",64)))
{
perror("ftok");
return NULL;
}
/*申请一块共享内存*/
if(-1 == (shmid=shmget(key,sizeof(msg_t),flag)))
{
perror("shmget");
return NULL;
}
/*建立映射关系*/
if(NULL == (addr=shmat(shmid,NULL,0)))
{
perror("shmat");
return NULL;
}
m_addr->shmid = shmid;
m_addr->addr = addr;
return m_addr;
}
/* 功能:释放共享内存
* 参数:共享内存编号,地址
* 返回值:无
* */
void del_memory(addr_t *m_addr){
printf("开始注销共享内存!\n");
/*解除共享内存与当前进程的映射关系*/
if(-1 == (shmdt(m_addr->addr)))
{
perror("shmdl");
return;
}
/*删除共享内存*/
int ret = 0;
if(-1 == (ret = shmctl(m_addr->shmid, IPC_RMID, NULL))) //IPC_RMID为删除内存段
{
perror("shmctl");
return;
}
}
/* 函数名:main
* 参数:无
* 返回值:正常返回0 异常返回-1
* */
int main(void){
msg_t *message;//共享内存存储的内容
addr_t *m_addr;//共享内存的地址
char age = 0;
/*信号处理函数*/
signal(SIGINT,handler);
/*初始化共享内存*/
m_addr = init_memory(m_addr);
/*获取共享内存用户空间地址*/
message = m_addr->addr;
/*循环设置共享内存内容*/
while(RUNING)
{
strcpy(message->name,"TANG");
message->age = age;
age++;
sleep(2);
}
/*注销共享内存*/
del_memory(m_addr);
return 0;
}
shmget.c------------------------------------------------------------------------------------------------------------------------
/*************************************************************************
> File Name: shmget.c
> Author: TANG
> Mail: tjcmail@126.com
> Created Time: 2018年04月02日 星期一 16时29分10秒
************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
/*共享内存结构*/
typedef struct{
char name[12];
char age;
}msg_t;
/*共享内存地址*/
typedef struct{
int shmid;
void *addr;
}addr_t;
/*变量声明*/
int RUNING = 1;
/* 功能:自定义信号处理函数
* 参数:
* 返回值:无
* */
void handler(int snum){
RUNING = 0;
}
/* 功能:初始化共享内存
* 参数:无
* 返回值:
* */
addr_t *init_memory(addr_t *m_addr)
{
key_t key = 0;
int shmid = 0;
void *addr = NULL;
int flag = IPC_CREAT|0664;
/*获取键值*/
if(-1 == (key=ftok(".",64)))
{
perror("ftok");
return NULL;
}
/*申请一块共享内存*/
if(-1 == (shmid=shmget(key,sizeof(msg_t),flag)))
{
perror("shmget");
return NULL;
}
/*建立映射关系*/
if(NULL == (addr=shmat(shmid,NULL,0)))
{
perror("shmat");
return NULL;
}
m_addr->shmid = shmid;
m_addr->addr = addr;return m_addr;
}
/* 功能:释放共享内存
* 参数:共享内存编号,地址
* 返回值:无
* */
void del_memory(addr_t *m_addr){
printf("开始注销共享内存!\n");
/*解除共享内存与当前进程的映射关系*/
if(-1 == (shmdt(m_addr->addr)))
{
perror("shmdl");
return;
}
}
/* 函数名:main
* 参数:无
* 返回值:正常返回0 异常返回-1
* */
int main(void){
msg_t *message;//共享内存存储的内容
addr_t *m_addr;//共享内存的地址
int age = 0;
/*信号处理函数*/
signal(SIGINT,handler);
/*初始化共享内存*/
m_addr = init_memory(m_addr);
/*获取共享内存用户空间地址*/
message = m_addr->addr;
/*循环获取共享内存内容*/
while(RUNING)
{
if(age != message->age)
{
printf("Read message success!The name is %s,age is %d\n",message->name,message->age);
age = message->age;
}
}
del_memory(m_addr);
return 0;
}