进程间通信:IPC-共享内存

共享内存:
操作系统接口标准: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;
}








猜你喜欢

转载自blog.csdn.net/pengchengliu/article/details/80501630