共享内存:
操作系统接口标准:system V 标准、posix标准。System V共享内存机制: shmget shmat shmdt shmctl
共享内存的原理 :
同一块物理内存映射到了多个进程的虚拟地址空间。
pathname->key->shmid->连接->使用->解除连接。
系统中每一个文件都是唯一的,所以可以用路径来产生key。同一个路径产生同一个key。同一个key可以打开或产生同一段共享内存。
每一段共享内存有一个ID:shmid。
共享内存在创建以后,随着进程结束不会释放。
因为是共享内存啊,你这个进程结束了,可能其他进程还要用。
key_t ftok(const char *pathname, int proj_id);
//产生key。proj_id通常传入一非0字符。
int shmget(key_t key, int size, int shmflg);
//根据key产生或打开一共享内存,得到shmid。key也可以自己指定。
void *shmat(int shmid, const void *shmaddr, int shmflg);
//attach.根据shmid连接共享内存。后面一般填NULL和0。连接共享内存得到的是
共享内
存在本进程的虚拟内存的首地址。共享内存在虚拟内存是连续的,但实
际物理
内存是否连续,不管你的事,管他呢~
int shmdt(const void *shmaddr);
//解除连接
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//参数cmd是对共享内存段的操作方式,可选为
IPC_STAT,IPC_SET,IPC_RMID
。 为IPC_RMID,表示删除共享内存段。这是buf填NULL即可。
为IPC_STAT,表示获取共享内存的信息。buf用来存放共享内存的信息。
为IPC_SET,表示设置。
例1:共享内存的使用
#include <sys/types.h> //这三个是这一堆函数的头文件
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define SIZE 20
int main(int argc,char* argv[]){
if(argc !=2){
printf("error args\n");
return -1;
}
key_t key=
ftok(argv[1],1);
//根据传入的路径,产生key。同一个路径,产生同一个key。
int shmid=
shmget(key,SIZE,IPC_CREAT|0600);
//根据key产生或打开一共享内存,共享内存大小为20个字节。返回共享内存ID,
即shmid。每一段共享内存有
唯一一个ID。
同一个key,产生同一个ID。如果写:
IPC_CREAT|IPC_EXCL|0600,则
已经有相应的共享内存时,报错。
if(-1==shmid){
perror("shmget");
return -1;s
}
//此时若用ipcs命令查看共享内存。可以看到已经创建好了,但是没有连接。
char* p;
p=(char*)
shmat(shmid,NULL,0);
//连接:将共享内存段映射到进程空间的某一地址。ipcs命令可以看到连接数为1。
if((char*)-1==p){
perror("shmat");
return -1;
}
p[0]='H';
int ret=
shmdt(p);
//解除连接。
if(ret!=0){
perror("shmdt");
return -1;
}
return 0;
}
命令:
查看系统共享内存 ipcs
删除共享内存 ipcrm -m shmid
共享内存
perm:权限
uid:有效的uid
例2:创建只用于父子通信的共享内存
如果key使用宏IPC_PRIVATE,这时候
别人就拿不到自己的key,这样别人就没法连接到自己创建的共享内存了。如果我在函数里面fork(),因为子进程完美复制了我的信息,所以子进程也就可以连接到该共享内存了。
通过ipcs看到:前面的shmid显示:0x00000000,就表示我创建的共享内存是私有的。
例:
int main(){
int shmid=shmget(
IPC_PRIVATE,20,IPC_CREAT|0600);
//新建共享内存的key使用IPC_PRIVATE,此时别人就无法拿到自己的
key,也就无法连接这个共享内存了。
if(-1==shmid){
perror("shmget");
return -1;
}
if(!fork()){
//fork()出来的子进程,因为完美复制了父进程,所以也就有相应的key。
也就可以连上共享内存。
char* p;
p=(char*)shmat(shmid,NULL,0);
if((char*)-1==p){
perror("child shmat");
return -1;
}
while(1);
}else{
char* p;
p=(char*)shmat(shmid,NULL,0);
if((char*)-1==p){
perror("parent shmat");
return -1;
}
while(1);
}
}
例3:删除共享内存
如果没有人连接,会立即删除共享内存。但是当有别人正在连接共享内存的时候,不能删除。此时,shmdi只给共享内存做个标识,当没人用
了立马就会删除。
情况一:
int main(){
int shmid=shmget(
(key_t)1234,20,IPC_CREAT|0600);
//不通过路径生成key,而是自己制定 key的值
if(-1==shmid){
perror("shmget");
return -1;
}
int ret=shmctl(shmid,
IPC_RMID,
NULL);
//删除共享内存。
if(-1==ret){
perror("shmctl");
return -1;
}
printf("shmctl delete shm success\n");
return 0;
}
情况二:
int main(){
int shmid=shmget(
(key_t)1234,20,IPC_CREAT|0600);
//不通过路径生成key,而是自己制定 key的值
if(-1==shmid){
perror("shmget");
return -1;
}
char *p=(char *)shmat(shmid,NULL,0); //连接到共享内存。
if((char *)-1==p){
perror("shmat");
return -1;
}
int ret=shmctl(shmid,
IPC_RMID,
NULL);
//删除共享内存。
if(-1==ret){
perror("shmctl");
return -1;
}
printf("shmctl delete shm success\n");
// while(1);
//如果不注释掉while(1),程序会一直执行。虽然打印了"shmctl delete shm sucess",
但是实际上共享内存并没有删除,通过ipcs可以看到共享内存依然存在,且连接数
为1,说明shmctl只是做了标记。如果注释掉这一句,在执行到这个地方的时候并没
有删除共享内存,而是在程序运行完,退出main时,函数断开了共享内存的连接,
这时候因为共享内存做了删除的标记,所以共享内存被删除。
return 0;
}
例4:查看共享内存的状态
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//cmd为
IPC_STAT,表示获取共享内存的信息。buf用来存放共享内存的信息。
当后面的形参是指针的时候,需要自己先弄好。返回的时结构体指针。
shmid_ds结构体
struct shmid_ds {
struct ipc_perm shm_perm; //Ownership and permissions //perm是权限的意思。
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
现在的连接数
...
};
struct ipc_perm {
key_t __key; // Key supplied to shmget(2)
key一般不看,对我们没啥用。
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 andSHM_LOCKED flags
unsigned short __seq; // Sequence number
};
int main(){
int shmid=shmget((key_t)1234,20,IPC_CREAT|0600);
if(-1==shmid){
perror("shmget");
return -1;
}
struct shmid_ds buf;
//因为结构体指针作为函数的形参传进去,所以要先定义结构体。如果函数的返回值是结 构体指针,就不需要定义结构体。
int ret=shmctl(shmid,
IPC_STAT,&buf);
//IPC
_STAT,表示获取共享内存的信息。buf用来存放共享内存的信息。
if(-1==ret){
perror("shmctl");
return -1;
}
printf("cuid=%d,mode=%o,size=%d,nattch=%ld\n",buf.shm_perm.cuid,buf.shm_perm.mode,buf.shm_segsz,buf.shm_nattch);
return 0;
}
例5:修改共享内存的参数
一般也很少该,更多是看,要改一般也就修改一下mode。
int main(){
int shmid=shmget((key_t)1234,20,IPC_CREAT|0600);
if(-1==shmid){
perror("shmget");
return -1;
}
struct shmid_ds buf;
int ret=shmctl(shmid,
IPC_STAT,&buf);
//首先把共享内存的信息读到buf中。
if(-1==ret){
perror("shmctl");
return -1;
}
buf.shm_perm.mode=0666;
//修改buf中的内容。
ret=shmctl(shmid,
IPC_SET,&buf);
//通过buf设置共享内存的信息。设置完之后ipcs可以看到已经做了修改。
if(-1==ret){
perror("shmctl1");
return -1;
}
return 0;
}