zoukankan      html  css  js  c++  java
  • IPC进程间通信---共享内存

    共享内存

      共享内存:共享内存就是分配一块能被其它进程访问的内存。每个共享内存段在内核中维护着一个内部结构shmid_ds,

    该结构定义在头文件linux/shm.h中,其结构如下:

    struct shmid_ds
    {
       struct ipc_perm  shm_perm;        //操作许可,里面包含共享内存的用户ID、组ID等信息
       int  shm_segsz;                   //共享内存段的大小,单位为字节
       __kernel_time_t  shm_atime;       //最后一个进程访问共享内存的时间
       __kernel_time_t  shm_dtime;       //最后一个进程离开共享内存的时间
       __kernel_time_t  shm_ctime;       //最后一次修改共享内存的时间
       __kernel_ipc_pid_t  shm_cpid;     //创建共享内存的进程ID
       __kernel_ipc_pid_t  shm_lpid;     //最后操作共享内存的进程ID
       ushort  shm_nattch;               //当前使用该共享内存段的进程数量
       ushort  shm_unused;
       void  *shm_unused2;
       void  *shm_unused3;
    };

      要获得一个共享内存的描述符,提供该共享内存的键值即可,该键值通常由函数ftok返回,该函数原形为:

       #include <sys/ipc.h>

       key_t ftok(const char *pathname,int proj_id);

       创建一个新的共享内存或访问一个已存在的共享内存前需要使用ftok函数得到key值,下面是ftok函数的包裹函数:

    key_t Ftok(const char *pathname,int proj_id)
    {
      key_t key= ftok(pathname,proj_id);
      if(key== -1)
      {
        perror("ftok.");
        exit(1);
      }
      return key;
    }

        共享内存的创建或打开:

       Linux下使用shmget函数来创建一个共享内存区,或者访问已经存在的共享内存区。该函数定义在头文件<sys/shm.h>

    中,该函数原形为:

       #include <sys/ipc.h>
       #include <sys/msg.h>
       int shmget(key_t key,size_t size,int shmflg);

       函数中,第一个参数是由ftok()函数得到的键值,第二个参数size为以字节为单位指定内存的大小,第三个参数shmflg为

    操作标志位,它的值为一些宏,如下所示:

    • IPC_CREAT:调用shmget时,系统将此值与其它所有共享内存区的key值进行比较,如果存在相同的key,说明共享内

    存区已存在,此时返回该共享内存区的标识符,否则新建一个共享内存区并返回其标识符。

    • IPC_EXCL:该宏必须和IPC_CREAT一起使用,否则没有意义。当shmget取IPC_CREAT|IPC_EXCL时,表示如果发现信号

    集已经存在,则返回-1,错误码为EEXIST。

       共享内存区的操作:

       在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。shmat调用成

    后就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败返回-1。该函数声明在头文件

    <sys/shm.h>中,该函数原形为:

       #include <sys/types.h>
       #include <sys/shm.h>
       void* shmat(int shmid,const void *shmaddr,int shmflg);

       函数中参数shmid为shmget函数的返回值,参数shmaddr为共享内存的附加点,参数shmflg为存取权限标志,参数shmaddr

    不同取值情况的含义说明如下:

    • 如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给shmflg参数指定了SHM_RND值,

    如果没有指定,则共享内存区附加到由shmaddr指定的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址

    后地址(SHMLBA,一个常址)。

    • 通常将参数shmaddr设置为NULL。

       当进程结束使用共享内存区时,要通过函数shmdt断开与共享内存区的连接。该函数声明在<sys/shm.h>头文件中,该

    原形为:

       #include <sys/types.h>
       #include <sys/shm.h>
       int shmdt(const void *shmaddr);

       参数shmaddr为shmat函数的返回值。该函数调用成功后,返回0,否则返回-1。进程脱离共享内存区后,数据结构shmid_ds

    中的shm_nattch就会减1。但是共享内存段依然存在,只有shm_nattch为0后,即没有任何进程再使用共享内存区,共享内存

    才在内核中被删除。一般来说,当一个进程终止时,它所附加的共享内存区都会自动脱离。

      共享内存区的控制:

      对共享内存区的控制是通过函数shmctl来完成的,该函数定义在<sys/shm.h>中,该函数原形为:

      #include <sys/ipc.h>

      #include <sys/shm.h>

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

      函数中,参数shmid为共享内存区的标识符;参数buf为指向shmid_ds结构体的指针;cmd为操作标志位,支持以下控制操作:

    • IPC_RMID:从系统中删除由shmid标识的共享内存区。
    • IPC_SET:设置共享内存区的shmid_ds结构。
    • IPC_STAT:读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址中。

       下面是使用共享内存区发送消息的例子:

    // myipc.h
    #pragma once
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <sys/shm.h>
    #include <sys/msg.h>
    
    union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *array;
        struct seminfo *__buf;
    };
    
    key_t Ftok(const char *pathname,int proj_id)
    {
        key_t key= ftok(pathname,proj_id);
        if(key== -1)
        {
            perror("ftok.");
            exit(1);
        }
        return key;
    }
    //server.c
    #include "myipc.h"
    
    int main(int argc,char *argv[])
    {
        key_t shm_key= Ftok(argv[1],atoi(argv[2]));
        int shm_id= shmget(shm_key,1024,IPC_CREAT|0755);
        if(shm_id== -1)
        {
            perror("shmget");
            exit(1);
        }
        char *addr= (char*)shmat(shm_id,NULL,0);
        if((void*)addr== (void*)-1)
        {
            perror("shmat");
            shmctl(shm_id,IPC_RMID,NULL);
            exit(1);
        }
        key_t sem_key= shm_key;
        int sem_id= semget(sem_key,2,IPC_CREAT|0755);
        struct sembuf p= {0,-1,0};
        struct sembuf v= {1,1,0};
        while(1)
        {
            printf("Ser: ");
            scanf("%s",addr);
            semop(sem_id,&v,1);
    
            semop(sem_id,&p,1);
            printf("Cli: %s
    ",addr);
        }
        shmdt(addr);
        semctl(sem_id,0,IPC_RMID);
        semctl(sem_id,1,IPC_RMID);
        shmctl(shm_id,IPC_RMID,NULL);
        return 0;
    }
    //client.c
    #include "myipc.h"
    
    int main(int argc,char *argv[])
    {
        key_t shm_key= Ftok(argv[1],atoi(argv[2]));
        int shm_id= shmget(shm_key,0,0);
        char *addr= (char*)shmat(shm_id,NULL,0);
        key_t sem_key= shm_key;
        int sem_id= semget(sem_key,0,0);
        struct sembuf p= {1,-1,0};
        struct sembuf v= {0,1,0};
        while(1)
        {
            semop(sem_id,&p,1);
            printf("Ser: %s
    ",addr);
            printf("Cli: ");
            scanf("%s",addr);
            semop(sem_id,&v,1);
        }
        shmdt(addr);
        return 0;
    }
  • 相关阅读:
    .net 下webservice 的WebMethod的属性
    做一个项目,平时都用到哪些工具提高效率(James Li)
    Android之解析Android Map地图返回的Json数据
    歌词文件LRC的解析,可用于音乐播放器实现歌词同步操作
    Android之创建程序快捷方式
    Android之Bitmap使用心得(持续更新)
    Socket编程之旅(服务器与客户端沟通)
    Android之应用自定义相机拍照并且对拍照文字(英文)进行识别
    android之App widget实际应用Demo
    Android之创建实时文件夹
  • 原文地址:https://www.cnblogs.com/XNQC1314/p/9148018.html
Copyright © 2011-2022 走看看