IPC之IPC_PRIVATE与ftok比较

Linux中,可以使用IPC对象来进行进程间通信。IPC对象存在于内核中,多进程可以操作同一个IPC对象。

每个IPC对象都有一个唯一的编号,该编号是由系统分配的。那么不同的进程如何知道这个编号,进而通过它进行通信呢?下面以共享内存为例,进行分析。

方法一:通过ftok函数,产生相同的键值。

假设,进程p1创建了共享内存。可以在创建时,调用ftok函数,得到一个key值,调用shmget函数,该函数会返回所创建共享内存的编号,并将key和编号关联起来。若进程p2想利用这个共享内存和p1进程通信,也可以调用ftok函数,得到同样的key,再根据key值,调用shmget函数,就可以获得该共享内存的编号。该过程可以通过下面的图来表达。

ftok函数原型如下:
                #include < sys/types.h>
                #include < sys/ipc.h>
                key_t ftok(const char *pathname, int proj_id);

第一个参数pathname,是一个存在的文件或目录名;

第二个参数proj_id,是非0整数(一般用i节点号)

该函数会返回一个key值,先运行的进程根据key来创建对象,后运行的进程根据key来打开对象。示意图如下:

使用 ftok创建共享内存,毫无关系的进程,可以通过得到同样的key,来操作同一个共享内存,对共享内存进行读写时,需要利用信号量进行同步或互斥。

方法二:使用IPC_PRIVATE对象

使用IPC_PRIVATE创建的IPC对象, key值属性为0,和IPC对象的编号就没有了对应关系。这样毫无关系的进程,就不能通过key值来得到IPC对象的编号(因为这种方式创建的IPC对象的key值都是0)。因此,这种方式产生的IPC对象,和无名管道类似,不能用于毫无关系的进程间通信。但也不是一点用处都没有,仍然可以用于有亲缘关系的进程间通信。示例程序如下:

#include < stdio.h>
        #include < stdlib.h>
        #include < errno.h>
        #include < sys/ipc.h>
        #include < sys/types.h>
        #include < sys/shm.h>
        #include < string.h>
        #define MAXSIZE 1024
        int main()
        {
                int shmid;
                        char *p = NULL;
                        pid_t pid;
                #if 0
                        key_t key;
                        if ((key = ftok(".", 'a')) == -1)
                        {
                                perror("ftok");
                                exit(-1);
                        }
                #endif
                if ((shmid = shmget(IPC_PRIVATE, MAXSIZE, 0666)) == -1)
                {
                        perror("shmget");
                        exit(-1);
                }
                if ((pid = fork()) == -1)
                {
                        perror("fork");
                        exit(-1);
                }
                if (pid == 0)
                {
                        if ((p = shmat(shmid, NULL, 0)) == (void *)-1)
                        {
                                perror("shmat");
                                exit(-1);
                        }
                                strcpy(p, "hello\n");
                        system("ipcs -m");
                                if (shmdt(p) == -1)
                                {
                                perror("shmdt");
                                exit(-1);
                        }
                        system("ipcs -m");
                }
                else
                {
                        getchar();
                                if ((p = shmat(shmid, NULL, 0)) == (void *)-1)
                        {
                                perror("shmat");
                                exit(-1);
                        }
                        printf("%s\n", (char *)p);
                        if (shmctl(shmid, IPC_RMID, NULL) == -1)
                        {
                                perror("RM");
                                exit(-1);
                        }
                }
                return 0;
        }

该程序中,父进程使用IPC_PRIVATE方式创建了共享内存,然后fork产生了子进程,由于子进程是复制父进程的方式产生的,因此,子进程也可以操作共享内存。子进程往共享内存里写了内容后,父进程可以读到。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

3.使用IPC_PRIVATE方式注意

(1)int shmID=shmget(IPC_PRIVATE,len,IPC_CREAT|0600);需要在父子进程都可见的地方调用(即在创建子进程之前),否则不能实现内存的共享

 因为通过IPC_PRIVATE这个key获得的id不一样,其他通过ftok获得的key来shmget获得的id在程序每次运行中是一样的。ftok参数一样的话每次程序运行中返回值都一样。

4.ipcs -m status 栏

nattch 是连接数目,dest 表示共享内存段已经被删除,但是仍然有程序在连接着它。

“status栏中列出当前共享内存的状态,当该段内存的mode字段设置了SHM_DEST位时就会显示"dest"字样,
当用户调用shmctl的IPC_RMID时,内核首先看有多少个进程还和这段内存关联着,如果关联数为0,就会销毁(释放)这段内存,否则就设置这段内存的mode位SHM_DEST,”


5.调用shmctl(shmID,IPC_RMID,NULL)或者shell 命令 ipcrm -m 不是会立刻删除共享内存,是向上面那样先置dest,并且此时共享内存中的数据仍然可以使用(即不影响正在使用共享内存的部分,但是若置为dest后再通过shmget通过同样的key来获取shmID时,shmID和删除前就不一样了),然后等待关联数为0才删除。shmdt是用来释放共享内存链接的,进程退出会调用shmdt。

         并且共享内存被置dest后可以成功调用shmctl(shmID,IPC_RMID,NULL)或者shell 命令 ipcrm -m ,但是当关联数为0,共享内存被删除后,再调用   这些命令就会出错。

shmctl(shmID,IPC_RMID,NULL)可以被每个使用共享内存的进程内调用多次,但至少由一个进程来调用一次,否则不能删除共享的内存,但是shmdt只能对应相应的shmget调用一次。

3.4.5部分的代码如下:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //shmem.cpp   
  2. //g++ shmem.cpp -lpthread -o shmem  
  3. #include <sys/types.h>  
  4. #include <sys/ipc.h>  
  5. #include <sys/shm.h>  
  6. #include<unistd.h>  
  7. #include<iostream>  
  8. #include<cstdlib>  
  9. #include<pthread.h>  
  10. #include <string.h>  
  11. #include <signal.h>  
  12. #include <errno.h>  
  13. #include <stdio.h>  
  14. struct argv_t{  
  15.     int shmID;  
  16. };  
  17. void * thread(void * argv){  
  18.     argv_t tmp=*(argv_t*)argv;  
  19.     char * addr=(char*)shmat(tmp.shmID,0,0);  
  20.     for(int i=0;i<5;i++){  
  21.         //sleep(2);  
  22.         std::cout<<"thread sending"<<std::endl;  
  23.         strcpy(addr,"Hello");  
  24.     }  
  25.     std::cout<<"thread :threadID="<<pthread_self()<<",pid="<<getpid()<<",parent pid ="<<getppid()<<std::endl;  
  26.       
  27.         /*if(-1==shmctl(tmp.shmID,IPC_RMID,NULL)){ 
  28.                 std::cout<<"remove shared memory error:"<<errno<<std::endl;//error<<std::endl; 
  29.                 perror(strerror(errno)); 
  30.         }else{ 
  31.                 std::cout<<"remove shared memory ok"<<std::endl; 
  32.         }*/  
  33.   
  34. }  
  35.   
  36. int main(int argc,char** argv){  
  37.     pthread_t thid;  
  38.     pthread_attr_t attr;  
  39.     pthread_attr_init(&attr);  
  40.     pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);  
  41.   
  42.     argv_t arg;  
  43.     pid_t pid;  
  44.     int len=5;  
  45.     int shmID=shmget(IPC_PRIVATE,len,IPC_CREAT|0600);   
  46.     if((pid=fork())==0){  
  47.         sleep(5);  
  48.             char * addr=(char *)shmat(shmID,0,SHM_RDONLY);  
  49.         for(int i=0;i<5;i++){  
  50.             std::cout<<"sub process pid="<<getpid()<<",parent pid"<<getppid()<<std::endl;  
  51.             std::cout<<"begin read sharedMem"<<std::endl;  
  52.             std::cout<<"s1"<<std::endl;  
  53.             std::cout<<"s2"<<std::endl;  
  54.             //if(i<=2)  
  55.                 system("ipcs -m");  
  56.             std::cout<<"-----"<<i<<std::endl;  
  57.               
  58.             if(i==2){  
  59.                char *cmd=new char[50];  
  60.                sprintf(cmd,"ipcrm -m %d\0",shmID);  
  61.                system(cmd);  
  62.             }  
  63.             std::cout<<addr<<std::endl;  
  64.             //shmdt(addr);  
  65.             sleep(3);  
  66.         }//for  
  67.         std::cout<<"reveive end"<<std::endl;  
  68.         if(-1==shmctl(shmID,IPC_RMID,NULL)){  
  69.             std::cout<<"remove shared memory error:"<<errno<<std::endl;//error<<std::endl;  
  70.             perror(strerror(errno));  
  71.         }else{  
  72.             std::cout<<"remove shared memory ok"<<std::endl;  
  73.         }  
  74.     }else if(pid>0){  
  75.         //shmID=shmget(IPC_PRIVATE,len,IPC_CREAT|0600);   
  76.         std::cout<<"shmID="<<shmID<<std::endl;  
  77.         arg.shmID=shmID;  
  78.         std::cout<<"I am the parent ,pid="<<getpid()<<",parent pid"<<getppid()<<std::endl;  
  79.         if((pthread_create(&thid,&attr,thread,(void*)&arg))!=0)  
  80.             std::cout<<"create thread failed"<<std::endl;  
  81.         else  
  82.             std::cout<<"create thread successed"<<std::endl;  
  83.         sleep(10);  
  84.         pthread_exit(0);//in main for wait  
  85.     }else{  
  86.         std::cout<<"fork sub process failed"<<std::endl;  
  87.     }  
  88.     return EXIT_SUCCESS;  
  89. }  

部分输出结果:


6.信号量中IPC_PRIVATE类似于共享内存中,同样semget(IPC_PRIVATE,1,0666 | IPC_CREAT)需要在父子进程都可见的地方调用(即在创建子进程之前),否则不能实现内存的共享。

信号量中的semctl(semID[index],0,IPC_RMID,sem_union)删除是立即删除,不同于共享内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值