• linux进程间通信--共享内存


    共享内存 :内核空间预留出来的一块内存,用于进程间通信

    共享内存是一种最高效的进程间通信方式,因为进程可以直接读写内存,不需要任何数据的复制,为了在进程间交换信息,内核专门留出了一块内存区,这段内存可以由要访问的进程将其映射到自己的私有地址空间,因此,进程就可以直接读写这一内存而不需要进行数据的复制,从而大大提高了效率,当然,由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

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

    功能:获取共享内存段的ID

    参数:
      @key IPC_PRIVATE 或 ftok()
      @size 申请的共享内存段大小 [4k的倍数]
      @shmflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL

    返回值:
    成功返回ID,失败返回-1

    (2)void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:映射共享内存到用户空间
    参数:
    @shmid 共享内存段ID
    @shmaddr NULL:系统自动完成映射
    @shmflg:   SHM_RDONLY:只读   0:读写
    返回值:
    成功返回映射后的地址,失败返回(void *)-1

    (3)int shmdt(const void *shmaddr);
    功能:撤销映射
    参数:
    @shmaddr 共享内存映射的地址

    注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射


    (4)int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    功能:根据命令控制共享内存
    参数:
    @shmid 共享内存段的ID
    @cmd IPC_STAT[获取属性],IPC_SET[设置属性],IPC_RMID[删除IPC对象]
    @buf 保存属性
    返回值:
    成功返回0,失败返回 -1

    ----------------------------------------------------------------------------
    注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。只有当共享内存映射
    次数为0,才会删除共享内存对象
    -----------------------------------------------------------------------------

    实例如下:

    shm_r.c

    #define SHM_SIZE 4096

    typedef struct{
      int read_flag;
      int write_flag;
      char mtxt[1];//标示数据存放地址
    }data_t;

    int shm_init(const char *pathname,int id)
    {
      int shmid;
      key_t key;

      key = ftok(pathname,id);
      if(key < 0){
        fprintf(stderr,"Fail to ftok : %s! ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
      if(shmid < 0){
        fprintf(stderr,"Fail to shmget : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      return shmid;
    }

    void read_shm(void *addr)
    {
      data_t *pdata;
      pdata = (data_t *)addr;

      pdata->read_flag = 0;
      pdata->write_flag = 1;

    while(1){
        if(pdata->read_flag){
        printf("Read : %s! ",pdata->mtxt);
        pdata->write_flag = 1;
        pdata->read_flag = 0;
      }
     }

    }

    //./a.out pathname
    int main(int argc, const char *argv[])
    {
      int shmid;
      void *addr;

      if(argc < 2){
        fprintf(stderr,"Usage : %s <pathname> ",argv[0]);
        exit(EXIT_FAILURE);
      }

      shmid = shm_init(argv[1],'k');
      addr = (void *)shmat(shmid,NULL,0);
      if(addr == (void *)-1){
        fprintf(stderr,"Fail to shmat : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      read_shm(addr);

      return 0;
    }

     

    shm_w.c

    #define SHM_SIZE 4096

    typedef struct{
      int read_flag;
      int write_flag;
      char mtxt[1];//标示数据存放地址
    }data_t;

    int shm_init(const char *pathname,int id)
    {
      int shmid;
      key_t key;

      key = ftok(pathname,id);
      if(key < 0){
        fprintf(stderr,"Fail to ftok : %s! ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
      if(shmid < 0){
        fprintf(stderr,"Fail to shmget : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      return shmid;
    }

    void write_shm(void *addr)
    {
      data_t *pdata;
      pdata = (data_t *)addr;

      pdata->read_flag = 0;
      pdata->write_flag = 1;

      while(1){
        if(pdata->write_flag){
        fgets(pdata->mtxt,SHM_SIZE - 8,stdin);//读写标志共占8个字节
        pdata->mtxt[strlen(pdata->mtxt) - 1] = '';

        pdata->write_flag = 0;
        pdata->read_flag = 1;
    }
    }

    }

    //./a.out pathname
    int main(int argc, const char *argv[])
    {
      int shmid;
      void *addr;

      if(argc < 2){
        fprintf(stderr,"Usage : %s <pathname> ",argv[0]);
        exit(EXIT_FAILURE);
      }

      shmid = shm_init(argv[1],'k');
      addr = (void *)shmat(shmid,NULL,0);
      if(addr == (void *)-1){
        fprintf(stderr,"Fail to shmat : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      write_shm(addr);

      return 0;
    }

    运行并执行shm_w.c

    运行并执行shm_r.c

    思路还是比较简单

    上面的代码可以改进吗?

    这段代码涉及到信号量的知识,可以查看:linux进程间通信--信号量

    先引进:

    sem.c

    union semun {
      int val; /* Value for SETVAL */
      struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
      unsigned short *array; /* Array for GETALL, SETALL */
      struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */
    };

    void init_sem_value(int sem_id,int sem_num,int value)
    {
      union semun sem_val;

      sem_val.val = value;

      if(semctl(sem_id,sem_num,SETVAL,sem_val) < 0)
      {
        perror("Fail to semctl");
        exit(EXIT_FAILURE);
      }

      return ;
    }

    int init_sems(const char *filename,int nsem)
    {
      key_t key;
      int sem_id;

      key = ftok(filename,'b');
      if(key < 0){
        perror("Fail to ftok");
        exit(EXIT_FAILURE);
      }

      sem_id = semget(key,nsem,IPC_CREAT|IPC_EXCL| 0666);
      if(sem_id < 0)
      {
      //已经存在
      if(errno == EEXIST)
      {
        //重新获得ID
        sem_id = semget(key,nsem,IPC_CREAT | 0666);

      }else{
        //其他错误
        perror("Fail to semget");
        exit(EXIT_FAILURE);
      }  

    //不存在,创建好后,初始化
     }else{

      init_sem_value(sem_id,0,0);//0号信号灯初始化为0
      init_sem_value(sem_id,1,1);//1号信号灯初始化为1
     }

    return sem_id;
    }

    void P(int sem_id,int sem_num)
    {
      struct sembuf sem;

      sem.sem_num = sem_num;
      sem.sem_op = -1;
      sem.sem_flg = 0;

      if(semop(sem_id,&sem,1) < 0)
      {
        perror("Fail to semop for P ");
        exit(EXIT_FAILURE);
      }

      return ;
    }

    void V(int sem_id,int sem_num)
    {
      struct sembuf sem;

      sem.sem_num = sem_num;
      sem.sem_op = 1;
      sem.sem_flg = 0;

      if(semop(sem_id,&sem,1) < 0)
      {
        perror("Fail to semop for V ");
        exit(EXIT_FAILURE);
      }

      return ;
    }

     

    shm_r.c修改如下:

    #define SHM_SIZE 4096

    int shm_init(const char *pathname,int id)
    {
      int shmid;
      key_t key;

      key = ftok(pathname,id);
      if(key < 0){
        fprintf(stderr,"Fail to ftok : %s! ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
      if(shmid < 0){
        fprintf(stderr,"Fail to shmget : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      return shmid;
    }

    void read_shm(void *addr,int semid)
    {
      char *pdata;
      pdata = (char *)addr;


    while(1){
      //P操作读资源
      P(semid,0);//申请读资源

      printf("Read : %s! ",pdata);
      //V操作写资源
      V(semid,1);//释放写资源
     }

    }

    //./a.out pathname
    int main(int argc, const char *argv[])
    {
      int shmid;
      int semid;
      void *addr;

      if(argc < 2){
        fprintf(stderr,"Usage : %s <pathname> ",argv[0]);
        exit(EXIT_FAILURE);
      }

      shmid = shm_init(argv[1],'k');
      addr = (void *)shmat(shmid,NULL,0);
      if(addr == (void *)-1){
        fprintf(stderr,"Fail to shmat : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      //信号灯集
      semid = init_sems(argv[1],2);

      read_shm(addr,semid);

      return 0;
    }

     

    shm_w.c修改如下:

    #define SHM_SIZE 4096

    int shm_init(const char *pathname,int id)
    {
      int shmid;
      key_t key;

      key = ftok(pathname,id);
      if(key < 0){
        fprintf(stderr,"Fail to ftok : %s! ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
      if(shmid < 0){
        fprintf(stderr,"Fail to shmget : %s ",strerror(errno));
        exit(EXIT_FAILURE);
      }

      return shmid;
    }

    void write_shm(void *addr,int semid)
    {
      char *data = (char *)addr;

      while(1){
        P(semid,1);
        fgets(data,SHM_SIZE,stdin);
        data[strlen(data) - 1] = '';
        V(semid,0);
      }
    }

    //./a.out pathname
    int main(int argc, const char *argv[])
    {
      int shmid;
      int semid;
      void *addr;

      if(argc < 2){
        fprintf(stderr,"Usage : %s <pathname> ",argv[0]);
        exit(EXIT_FAILURE);
      }

      shmid = shm_init(argv[1],'k');
      addr = (void *)shmat(shmid,NULL,0);
      if(addr == (void *)-1){
        fprintf(stderr,"Fail to shmat : %s ",strerror(errno));
        exit(EXIT_FAILURE);
    }

      semid = init_sems(argv[1],2);

      write_shm(addr,semid);

      return 0;
    }

     运行结果如上

  • 相关阅读:
    链表
    Wonder团队承接各种Web3D业务
    真我的信息
    一个人独自做长期项目,如何提高工作效率?
    【Java】类的结构
    【长知识】找书攻略
    【长知识】语义化版本控制
    【Java】Debug调试常用技巧
    【Web】Servlet三大作用域、JSP四大作用域
    【Web】Servlet基本概念
  • 原文地址:https://www.cnblogs.com/bwbfight/p/9293682.html
Copyright © 2020-2023  润新知