int shmget(key_t key,size_t size,int shmflg);//创建共享内存
第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1。不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。
第二个参数指定了所申请的内存块的大小。因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍,但是最后一页的剩余部分内存是不可用的。
第三个参数是一组标志,通过特定常量的按位或操作来shmget。这些特定常量包括:
IPC_CREAT:这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。
IPC_EXCL:这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相同键值的共享内存块,shmget会返回这个已经建立的共享内存块,而不是重新创建一个。
void *shmat(int shm_id,const void *shm_adder,int shmflg);//绑定到共享内存
将 shmget 返回的共享内存标识符传递给这个函数作为第一个参数。
该函数的第二个参数是一个指针,指向您希望用于映射该共享内存块的进程内存地址;如果您指定NULL则Linux会自动选择一个合适的地址用于映射。
第三个参数,shm_flg是一组标志位,通常为0。调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1。shmat函数成功执行会将shm_id段的shmid_ds结构的shm_nattch计数器的值加1.
int shmdt(const void * shmaddr);//与共享内存脱离
当一个进程不再使用一个共享内存块的时候应通过调用 shmdt(Shared Memory Detach,脱离共享内存块)函数与该共享内存块脱离。将由 shmat 函数返回的地址传递给这个函数。如果当释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1。int shmctl(int shm_id,int command,struct shmid_ds *buf);//控制共享内存
第一个参数,shm_id是shmget函数返回的共享内存标识符。
第二个参数,command是要采取的操作,可以取IPC_STA、IPC_SETT、IPC_RMID三个值
要获取一个共享内存块的相关信息,则为该函数传递 IPC_STAT 作为第二个参数,同时传递一个指向一个 struct shmid_ds对象的指针作为第三个参数。
要删除一个共享内存块,则应将 IPC_RMID 作为第二个参数,而将 NULL 作为第三个参数。当最后一个绑定该共享内存块的进程与其脱离时,该共享内存块将被删除。
在结束使用每个共享内存块的时候都使用 shmctl 进行释放,以防止超过系统所允许的共享内存块的总数限制。调用 exit 和 exec 会使进程脱离共享内存块,但不会删除这个内存块。
共享内存的结构体struct ipc_perm
{
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
struct shmid_ds//描述共享内存的id
{
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 */
};
命令
#pragma once
#include <stdio.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <unistd.h>
#define __PATH__ "."
#define __PROJECT__ 8888
#define __SHM_SIZE__ 4*1024
int get_shm();
char* at_shm();
int delete_shm();
int rm_shm();
#include "shm.h"
int get_shm()
{
key_t key = ftok(__PATH__, __PROJECT__);
int flag = IPC_CREAT | 0666;
int shm_id = shmget(key, __SHM_SIZE__, flag);
if(shm_id == -1)
{
printf("get share memory error!\n");
}
else
{
printf("get share memory success!\n");
}
return shm_id;
}
char* at_shm(int shm_id)
{
return (char*)shmat(shm_id, NULL,0);
}
int delete_shm(char *addr)
{
return shmdt(addr);
}
int rm_shm(int shm_id)
{
return shmctl(shm_id, IPC_RMID, NULL);
}
int main()
{
int shm_id = get_shm();
pid_t id = fork();
if(id < 0)
{
printf("fork error\n");
return 1;
}
else if(id == 0)//child
{
char *buf = at_shm(shm_id);
int i = 0;
while(i < 4096)
{
buf[i] = 'X';
i++;
}
buf[4096] = '\0';
delete_shm(buf);
}
else//father
{
char *buf = at_shm(shm_id);
sleep(5);
printf("%s\n", buf);
delete_shm(buf);
waitpid(id, NULL, 0);
rm_shm(shm_id);
}
return 0;
}
共享内存的特点
在匿名管道(pipe),命名管道(mkfifo),消息队列(msgget)、信号量(semget)、共享内存(shmget)这五种进程间通信的方式中,前两种生命周期随进程,后三种生命周期随内核。而共享内存是这五种方式中效率最高的。虽然共享内存提供了进程间通信的方式,但是他没有相应的互斥机制,所以一般共享内存和信号量配合起来使用。
因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。