进程间通信之共享内存-SystemV shmxxx详解(IPC范例篇)

 进程间通信之共享内存-SystemV shmxxx详解(IPC范例篇)
(基础知识和基础用例看我前一篇博客)
范例2:两个进程通过指定名字的共享内存进行通信
  适用于任何进程之间;此时,需要打开或创建指定名字的共享内存,然后再调用shmat()进行地址映射;
  shm_write.c首先打开或创建一个指定名字的共享内存,内存大小设置为16个people结构大小。然后从shmat()的返回地址开始,设置了16个people结构的值,然后进程睡眠10秒钟,等待其他进程映射同名字的内存,最后解除映射并删除共享内去区域;
  shm_read.c映射到一个一个指定名字的共享内存上,并以people数据结构的格式从 shmat()返回的地址处读取16个people结构,并输出读取的值,然后解除映射。
源代码:

/* shm_write.c */
#include <stdio.h>
#include  <string.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <unistd.h>

#define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
typedef struct{
  char name[4];
  int  age;
}people;

int main(int argc, char** argv) // map a normal file as shared mem:
{
    int shm_id,i,oflag;
    char c;
    people *p_shm;
    char s[4]={0};
    size_t  length;
    key_t key;
    oflag = SVSHM_MODE | IPC_CREAT;
    /* 解析命令行参数 */
    while(( c = getopt(argc,argv,"e")) != -1)
    {
        switch(c)
        {
            case 'e':
                oflag |= O_EXCL;
                break;
        }
    }
    /* 打印输入参数 */
    for(i=0;i<argc;i++)
    {
        printf("argv[%d] = %s\n",i,argv[i]);
    }
    /* optind—调用 getopt() 时的下一个 argv 指针的索引 */
    if (optind != argc -2)
    {
        printf("usage: shmget [-e] <pathname> <length>.\n");
        exit(0);
    }
    key = ftok(argv[optind],0);
    length = atoi(argv[optind + 1]);    
    /* 创建由用户指定其名字和大小的共享内存区 */
    shm_id=shmget(key,length,oflag);; 
    if(shm_id<0)
    {
        perror("shmget");
        exit(-1);
    }
    printf("key = 0x%08x,shmid = %d,bytes = %d\n",key,shm_id,length);
    /* 把该内存区映射到当前进程的地址空间 */
    p_shm = (people*) shmat(shm_id,NULL,0);
    if(p_shm<0)
    {
        perror("shmat");
        exit(-1);
    }
    printf("shm map address:%#x\n",(unsigned int)&p_shm);

    s[0] = 'B';
    s[1] = '\0';
    for(i=0; i<10; i++)
    {
        s[0] ++;
        memcpy( ( *(p_shm+i) ).name, &s,sizeof(s) );
        ( *(p_shm+i) ).age = 20+i;
    }
    sleep(10);
    printf( "\nparent read first name: %s age %d\n",p_shm[0].name,p_shm[0].age );
    /* 断开和共享内存的映射 */
    if(shmdt(p_shm)<0)
    {
        perror("shmdt");
        exit(-1);
    }
    /* 删除共享内存 */
    shmctl(shm_id,IPC_RMID,NULL);
    return 0;
}
/* shm_read.c */
#include  <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <unistd.h>

#define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
typedef struct{
  char name[4];
  int  age;
}people;

int main(int argc, char** argv)  // map a normal file as shared mem:
{
    int shm_id,i;
    key_t key;
    people *p_shm;

   if(argc != 2)
    {
        printf("usage: shmrmid <pathname>\n");
        exit(0);
    }
    key = ftok(argv[1],0);  
    /* 打开共享内存区 */
    shm_id = shmget(key,0,SVSHM_MODE);
    printf("key = 0x%08x,shmid = %d\n",key,shm_id);
    /* 共享内存区域映射到当前进程空间中 */
    p_shm = (people*) shmat(shm_id, 0, 0 );
    if(p_shm<0)
    {
        perror("shmat");
        exit(-1);
    }   
    for(i = 0;i<10;i++)
    {
        printf( "name: %s age %d\n",p_shm[i].name,p_shm[i].age );
    }
    /* 断开和共享内存的映射 */
    if(shmdt(p_shm)<0)
    {
        perror("shmdt");
        exit(-1);
    }
    return 0;
  }

代码测试:
这里写图片描述

范例3:父子进程通过私有映射实现共享内存
  适用于具有亲缘关系的进程之间; 由于父子进程特殊的亲缘关系,在父进程中先调用shmat(),然后调用fork()。那么在调用fork()之后,子进程继承父进程私有映射后的地址空间,同样也继承shmat()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而shmat()返回的地址,却由父子进程共同维护。
  对于具有亲缘关系的进程实现共享内存最好的方式应该是采用内存私有映射的方式。此时,不必指定具体的共享内存区的名字。
源代码:

/* shm_fork.c */
#include <stdio.h>
#include  <string.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct{
  char name[4];
  int  age;
}people;

int main(int argc, char** argv) 
{
    int shm_id,i;
    people *p_shm;
    char s[4]={0};
    /* 创建一个私有IPC共享内存区域 */
    shm_id=shmget(IPC_PRIVATE, 32, 0644 ); 
    if(shm_id<0)
    {
        perror("shmget");
        exit(-1);
    }
    /* 共享内存区域映射到当前进程空间中 */
    p_shm = (people*) shmat(shm_id, 0, 0 );
    if(p_shm<0)
    {
        perror("shmat");
        exit(-1);
    }
    printf("shm map address:%#x\n",(unsigned int)&p_shm);

    if(fork() == 0)
    {
        /* 子进程读取父进程写入的共享内存的内容 */
        sleep(2);
        for(i = 0;i<10;i++)
        {
            printf( "child read: name: %s age %d\n",p_shm[i].name,p_shm[i].age );
        }
        if(shmdt(p_shm)<0)
        {
            perror("shmdt");
            exit(-1);
        }
        return 0;
    }

    /* 父进程写入共享区内容 */
    s[0] = 'a';
    s[1] = '\0';
    for(i=0; i<10; i++)
    {
        s[0] ++;
        memcpy( ( *(p_shm+i) ).name, &s,sizeof(s) );
        ( *(p_shm+i) ).age = 20+i;
    }
    sleep(5);
    printf( "\nparent read first name: %s age %d\n",p_shm[0].name,p_shm[0].age );
    /* 断开和共享内存的映射 */
    if(shmdt(p_shm)<0)
    {
        perror("shmdt");
        exit(-1);
    }

    return 0;
}

测试:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值