• 共享内存+互斥量实现linux进程间通信 分类: Linux C/C++ 2015-03-26 17:14 67人阅读 评论(0) 收藏


    一、共享内存简介

        共享内存是进程间通信中高效方便的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针,两个进程可以对一块共享内存进行读写。

        共享内存并未提供进程同步机制,使用共享内存完成进程间通信时,需要借助互斥量或者信号量来完成进程的同步。这里说一下互斥量与信号量的区别。互斥量用于线程的互斥,信号量用于线程的同步,这是互斥量与信号量的本质区别,其次信号量实现互斥量的功能。

        本文结合个人实际项目需求,采用互斥量实现进程间访问共享内存的互斥,即同一时刻只能允许一个进程对共享内存进行写操作,当然也可以用信号量来完成进程间的互斥,这里就不再赘述。

    二、使用系统调用完成共享内存的申请、连接、分离和删除

        共享内存函数由shmgetshmatshmdtshmctl四个函数组成。使用时需要包含#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>

    1.共享内存的申请

        使用shmget()完成共享内存的申请,函数原型如下:

        int shmget(key_t key, size_t size, int shmflg);

        key:共享内存的标识符,大于032位整数。若是父子关系的进程间通信,这个标识符用IPC_PRIVATE,若进程没有关系,可自行定义。

        size:共享内存大小,单位Byte。

        shmflg:共享内存的模式(mode),包括三部分,第一部分是:无指定标示符的共享内存是否创建,由

    0(只获取)或IPC_CREAT(未创建则新建)决定。第二部分:IPC_EXCL(若已创建,则报错)。第三部分:权限标识,由八进制表示,如0640,第一个0是八进制数标识,第一个6(4+2)表示拥有者的权限读和写,第二个4表示同组权限写,第3个0表示他人的权限。这三部分由算数或运算符|拼接组成shmflg,如IPC_CREAT|0640。

         成功时返回共享内存的ID,失败时返回-1。

    2.共享内存的连接

        使用shmat()函数将已经申请好的共享连接到当前进程的地址空间,函数原型如下:

        void *shmat(int shmid, const void *shmaddr, int shmflg); 

        shmid:共享内存标识符。

        shmaddr:指定共享内存连接到进程地址空间的位置,直接指定为NULL让内核自己决定一个合适的地址位置。

        shmflg:SHM_RDONLY为只读模式,其他为读写模式,通常设置为NULL。

        成功时,这个函数返回共享内存的起始地址。失败时返回-1。

    3.共享内存的分离

        使用sdmdt()函数将已连接的共享内存与进程分离,功能与shmat()相反,函数原型如下:

        int shmdt(const void *shmaddr);  

        shmaddr: 连接的共享内存的起始地址。成功时返回0。失败时返回-1。

    4.共享内存的删除

        shmctl() 控制对这块共享内存的使用,包括删除。函数原型如下:

        int shmctl(int shmid, int command, struct shmid_ds *buf);  

        shmid:共享内存的ID。

        command:是控制命令,IPC_STAT(获取共享内存的状态)、IPC_SET(改变共享内存的状态)IPC_RMID(删除共享内存)。

        buf:一个结构体指针。Command设置为IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,由这个结构体指定。

        返回值:成功:0,失败:-1。

    三、 使用互斥量完成父子进程对共享内存的互斥访问

        在共享内存中申明互斥量pthread_mutex_t mutex,需要包含<pthread.h>头文件。使用时的步骤是:

        第一步:初始化互斥量,使用pthread_mutex_init()函数,函数原型如下:

        intpthread_mutex_init(pthread_mutex_t*restrict mutex,constpthread_mutexattr_t *restrict attr);

        使用方法:pthread_mutex_init(&mutex,NULL)。函数成功返回0,失败返回非0。

        第二步:对互斥量进程上锁,使用pthread_mutex_lock()函数,函数原型如下:

        int pthread_mutex_lock(pthread_mutex_t *mutex);

        函数成功返回0,错误返回非0的错误代码。

        第三步:pthread_mutex_unlock()对已经上锁的mutex进行解锁,pthread_mutex_lock()成对出现,函数原型如下:

        int pthread_mutex_unlock(pthread_mutex_t*mutex)。

        函数成功返回0,错误返回非0的错误代码。

        第四步:如果最后不需要使用互斥量的话,使用pthread_mutex_destroy()销毁。函数原型如下:

        int pthread_mutex_destroy(pthread_mutex_t*mutex);

        函数成功返回0,错误返回非0的错误代号。

    四、 示例源码

        鄙人以实际项目开发过程中实现进程间通信的源码,为大家展示如何利用上面总结的系统调用接口

    来实现进程间的通信。

    1.  定义存储成员变量的类,共享内存将用于存放该类的成员数据。

    <pre name="code" class="cpp"><pre name="code" class="cpp">class ALGOriginalTask
    {
    public:
        uint8 algorithm;   //算法,在global.h中定义
    uint8 encodeType;  //编码类型0:UTF8,1:UCS2
        uint32 cipher_num;      //密文数目
        uint32 cipher_len;   //密文长度
        char cipher[128][256];  //密文最多128个
        uint8 salt_len;  //盐的长度
        char salt[128][32];//最多128个,最长32字节
        char re[32][256];
    uint8 plainfile_num;
    pthread_mutex_t mutex;//读写共享内存互斥量
    };
    class ALGResult
    {
    public:
        bool init();
        ALGResult() {};
        ~ALGResult() {};
         struct timeval ts;
    	struct timeval te;
    	uint8  success_num;
    	uint8 algorithm;
    	uint8 cipher_len;
    	char cipher[128][256]; 
    	char salt[128][32];
    	char ret_plain[MAX_CIPHER_NUM][MAX_PLAIN_LEN];    
    };

    
    
    
    

    2. 使用fork()在父进程中产生子进程,通过系统调用execve()调用另外可执行程序ALG.out,将ALGOriginalTask中的数据写入共享内存后,再在ALG.out中通过接收到的shmid获取共享内存进行读取。最后在ALG.out中将ALGResult中的数据写入共享内存,再由父进程读取结果。

    父进程代码:

    int main(int argc, char **argv)
    {
    pid_t pid;//子进程ID
        int shmid;//共享内存ID
        ALGOriginalTask* ALG_shm_addr=NULL;
    	ALGResult* ALG_Result_shm_addr=NULL;
    	void* shm_addr=NULL;//共享内存首地址
        //char buff[sizeof(ALGOriginalTask)];
        if ((shmid=shmget(IPC_PRIVATE,sizeof(ALGOriginalTask)+sizeof(ALGResult),IPC_CREAT|0640))<0)//创建当前进程的父子进程私有共享内存
    	{
          perror("shmget");
          exit(-1);
        } else
    	{//create successfully
          printf("Create shared memory: %d.
    ",shmid);
          printf("Created shared memory status:
    ");
          system("ipcs -m");
    	}
    	printf("start fork
    ");
        switch(pid=fork())
    	{
    	  case -1:
    	    perror("fork");
    		exit(-1);
    	  case 0://child
    	  {
    	    //构造ALG.out参数
    		string shmId=itoa(shmid);
    		string plain_path=ALG_task->plainfile_path;
    		string plain_offset=itoa(ALG_task->plain_offset);
    		string plain_num=itoa(ALG_task->plain_num);
    		string plain_len=itoa(ALG_task->plain_len);
    		cout<<"start execute ALG.out"<<endl;
    		const char* argv[10]={"./ALG.out",shmId.c_str(),plain_path.c_str(),plain_offset.c_str(),plain_num.c_str(),plain_len.c_str()};
    		execlp(argv[0], argv[0], argv[1], argv[2], argv[3],
                   argv[4], argv[5], NULL);
    	    exit(0);
    	  }
    	  default://parent
    	  {
    	    if((shm_addr=shmat(shmid,0,0))==(void*)-1){
               perror("Parent:shmat");//打印函数执行错误原因,Parent:shmat:错误原因
               exit(-1);
             }else
    		 {
    		     //初始化共享内存
    			 memset(shm_addr,NULL,sizeof(ALGOriginalTask)+sizeof(ALGResult));
                 printf("Parent: Attach shared-memory: %p
    ",shm_addr);
                 printf("Parent Attach shared memory status:
    ");
                 system("ipcs -m");
    		 }
    		//设置共享内存格式
    		ALG_shm_addr=static_cast<ALGOriginalTask*>(shm_addr);
    		//向共享内存写入参数
    		pthread_mutex_init(&ALG_shm_addr->mutex,NULL); 
    		pthread_mutex_lock(&ALG_shm_addr->mutex);
    		//初始化originalTask		cout<<"writeALG_original_task->algorithm:"<<(int)ALG_original_task->algorithm<<endl;
    		ALG_shm_addr->algorithm=ALG_original_task->algorithm;
    		ALG_shm_addr->encodeType=ALG_original_task->encodeType;
    		ALG_shm_addr->cipher_num=ALG_original_task->cipher_num;
    		ALG_shm_addr->cipher_len=ALG_original_task->cipher_len;
    		ALG_shm_addr->salt_len=ALG_original_task->salt_len;
    		//memcpy(ALG_shm_addr->task_name,ALG_original_task->task_name,20);
            memcpy(ALG_shm_addr->cipher,ALG_original_task->cipher,128*256);
    		memcpy(ALG_shm_addr->salt,ALG_original_task->salt,128*32);
    	    memcpy(ALG_shm_addr->re,ALG_original_task->re,32*256);
    		pthread_mutex_unlock(&ALG_shm_addr->mutex);
    		
    		waitpid(pid,NULL,0);//等待子进程计算结束,从共享内存读取返回结果
    		ALG_Result_shm_addr=static_cast<ALGResult*>(shm_addr);//设置共享内存格式
    		//读取共享内存数据,即计算结果
    		memcpy(&ALG_Result->ts,&ALG_Result_shm_addr->ts,sizeof(struct timeval));
    		memcpy(&ALG_Result->te,&ALG_Result_shm_addr->te,sizeof(struct timeval));
    		ALG_Result->success_num=ALG_Result_shm_addr->success_num;
    		memcpy(ALG_Result->ret_plain,ALG_Result_shm_addr->ret_plain,128*64);
    		
    		//删除父进程的共享内存映射地址
            if (shmdt(shm_addr)<0) {
                perror("Parent:shmdt");
                exit(1);
            }else
                printf("Parent: Deattach shared-memory.
    ");
    		//删除共享内存
            if (shmctl(shmid,IPC_RMID,NULL)==-1){
              perror("shmct:IPC_RMID");
              exit(-1);
            }else
              printf("Delete shared-memory.
    ");
    	  }
    	}//end fork
    	
    	ALG_Result->algorithm=ALG_original_task->algorithm;
    	memcpy(ALG_Result->cipher,ALG_original_task->cipher,128*256);
        memcpy(ALG_Result->salt,ALG_original_task->salt,128*32);
    	cout<<"computation end"<<endl;//succeed
        return ALG_Result;
    }<strong>
    </strong>

    ALG.out代码:

    int main(int argc, char **argv)
    {
       gettimeofday(&tt1,NULL);
       ALGOriginalTask* ALG_shm_addr=NULL;
       ALGResult* ALG_Result_shm_addr=NULL;
       GPR_Info* gpr_Info=new GPR_Info;
       RST_Info* rst_Info=new RST_Info;
       memset(gpr_Info,NULL,sizeof(GPR_Info));
       memset(rst_Info,NULL,sizeof(RST_Info));
       void* shm_addr=NULL;//共享内存首地址
       int shmid=atoi(argv[1]);
       if((shm_addr=shmat(shmid,0,0))==(void*)-1)
       {
          perror("Child_ALG.out:shmat");
          exit(-1);
       }
       //设置共享内存格式
       ALG_shm_addr=static_cast<ALGOriginalTask*>(shm_addr);
       //waiting for the parent write data to share memory
       cout<<"wait parent write sharememory:"<<endl;
       while(1)
       {
         if((int)ALG_shm_addr->algorithm>=0)
    	   break;
       }
       cout<<"start read gpr_Info from sharememory:"<<endl;
       //get info of GPR
       pthread_mutex_lock(&ALG_shm_addr->mutex);//lock waiting for parent process finish the writing of share memory
       gpr_Info->algorithm=ALG_shm_addr->algorithm;
       gpr_Info->encodeType=ALG_shm_addr->encodeType;
       gpr_Info->cipher_num=ALG_shm_addr->cipher_num;
       gpr_Info->cipher_len=ALG_shm_addr->cipher_len;
       gpr_Info->plain_len=atoi(argv[5]);
       gpr_Info->salt_len=ALG_shm_addr->salt_len;
       memcpy(gpr_Info->salt,ALG_shm_addr->salt,128*32);
       memcpy(gpr_Info->cipher,ALG_shm_addr->cipher,128*256);
       pthread_mutex_unlock(&ALG_shm_addr->mutex);//unlock the mutex
       //获取计算结果
       get_ret_palin(ret_plain, gpr_Info);
    //write result to share memory
    memset(shm_addr,NULL,sizeof(ALGOriginalTask)+sizeof(ALGResult));
    	ALG_Result_shm_addr=static_cast<ALGResult*>(shm_addr);
    	ALG_Result_shm_addr->ts=tt1;
    	ALG_Result_shm_addr->te=tt2;
        ALG_Result_shm_addr->algorithm=gpr_Info->algorithm; 
    	ALG_Result_shm_addr->cipher_len=gpr_Info->cipher_len;
    	ALG_Result_shm_addr->success_num=rst_Info->success_num;
    	memcpy(ALG_Result_shm_addr->salt,gpr_Info->salt,128*32);
    	memcpy(ALG_Result_shm_addr->cipher,gpr_Info->cipher,128*256);
        memcpy(ALG_Result_shm_addr->ret_plain,ret_plain,128*64); 
    	
        //删除子进程的共享内存映射地址
        if (shmdt(shm_addr)<0)
    {
          perror("ALG.out:shmdt");
          exit(1);
        }else
            printf("ALG.out:Detach shared-memory.
    ");		
    	delete []charset;
    	delete []ret_plain;
    	return 1;
    }
    

    参考文献:

    [1]百度百科.共享内存

    [2] http://blog.csdn.net/ljianhui/article/details/10253345

    [3]Unix环境高级编程第三版





    版权声明:本文为博主原创文章,未经博主允许不得转载。

    ---转载或者使用代码,请注明原作者 This is bill
  • 相关阅读:
    16. 3Sum Closest
    17. Letter Combinations of a Phone Number
    20. Valid Parentheses
    77. Combinations
    80. Remove Duplicates from Sorted Array II
    82. Remove Duplicates from Sorted List II
    88. Merge Sorted Array
    257. Binary Tree Paths
    225. Implement Stack using Queues
    113. Path Sum II
  • 原文地址:https://www.cnblogs.com/ScytheWH/p/4622981.html
Copyright © 2020-2023  润新知