博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

system v 共享内存 shmget shmat shmdt

Posted on 2015-05-27 12:19  bw_0927  阅读(482)  评论(0)    收藏  举报

http://www.cnblogs.com/Anker/archive/2013/01/20/2868357.html

 

1、概述

  posix系统调用mmap通过映射一个普通文件实现共享内存。System V 则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件。执行过程是先调用shmget,再调用shmat。对于每个共享的内存区,内核维护如下的信息结构,定义在<sys/shm.h>头文件中。

复制代码
 1 struct shmid_ds {
 2   struct ipc_perm shm_perm;     /* operation perms */
 3   int shm_segsz;            /* size of segment (bytes) */
 4   time_t shm_atime;          /* last attach time */
 5   time_t shm_dtime;          /* last detach time */
 6   time_t shm_ctime;          /* last change time */
 7   unsigned short shm_cpid;      /* pid of creator */
 8   unsigned short shm_lpid;      /* pid of last operator */
 9   short shm_nattch;          /* no. of current attaches */
10   /* the following are private */
11   unsigned short shm_npages;      /* size of segment (pages) */
12   unsigned long *shm_pages;       /* array of ptrs to frames -> SHMMAX */ 
13   struct vm_area_struct *attaches;   /* descriptors for attaches */
14 };
复制代码

参考网址:http://www.tldp.org/LDP/lpg/node68.html

2、System V 共享内存区API

  使用共享内存的流程:
   1.进程必须首先分配它。
   2.随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中。
   3.当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块(一般由获取共享区域的进程负责释放)

复制代码
#include <sys/ipc.h>
#include <sys/shm.h>
/*
创建一个新的内存共享区或者访问一个已经存在的共享内存区
返回共享内存区标识符
*/
int shmget(key_t key, size_t size, int shmflg);
/*
创建或打开一个共享内存区后,调用shmat把它连接到调用进程的地址空间
*/
void *shmat(int shmid, const void *shmaddr,int shmflg);
/*
当一个进程完成某个共享内存区的使用时,调用shmdt断开这个内存区
*/
int shmdt(const void *shmaddr);
/*
对内存区进行多种操作
cmd取值:
IPC_RMID:从系统中删除由shmid标识的共享内存区并拆除它
IPC_SET:给指定的共享内存区设置其shmid_ds结果成员
IPC_STAT:通过buff参数向调用者返回所指定共享内存区当前的shmid_ds结构
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
复制代码

调用System V API编写程序进行测试:

程序1:调用shmget函数使用指定的路径名和长度创建一个共享内存区,程序如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/shm.h>
 5 #include <fcntl.h>
 6 
 7 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
 8 
 9 int main(int argc,char *argv[])
10 {
11     int     c,id,oflag;
12     char     *ptr;
13     size_t  length;
14     oflag = SVSHM_MODE | IPC_CREAT;
15     while(( c = getopt(argc,argv,"e")) != -1)
16     {
17         switch(c)
18         {
19             case 'e':
20                 oflag |= O_EXCL;
21                 break;
22         }
23     }
24     if (optind != argc -2)
25     {
26         printf("usage: shmget [-e] <pathname> <length>.\n");
27         exit(0);
28     }
29     length = atoi(argv[optind + 1]);
30     //创建由用户指定其名字和大小的共享内存区
31     id = shmget(ftok(argv[optind],0),length,oflag);
32     //把该内存区连接到当前进程的地址空间
33     ptr = shmat(id,NULL,0);
34     exit(0);
35 }
复制代码

程序2:调用shmctl指定IPC_RMID命令,从系统中删除一个共享内存区,程序如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/shm.h>
 5 
 6 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
 7 
 8 int main(int argc,char* argv[])
 9 {
10     int     id;
11     if(argc != 2)
12     {
13         printf("usage: shmrmid <pathname>\n");
14         exit(0);
15     }
16     //打开共享内存区
17     id = shmget(ftok(argv[1],0),0,SVSHM_MODE);
18     //从系统中删除由id标识的共享内存区
19     shmctl(id,IPC_RMID,NULL);
20     exit(0);
21 }
复制代码

程序3:往共享内存区中写入一个模式,调用shmctl指定IPC_STAT命令格式,程序如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/shm.h>
 5 #include <fcntl.h>
 6 
 7 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
 8 
 9 int main(int argc,char *argv[])
10 {
11     int     i,id;
12     struct shmid_ds buff;
13     unsigned char *ptr;
14     if(argc != 2)
15     {
16         printf("usage: shmwrite  <pathname>.\n");
17         exit(0);
18     }
19     id = shmget(ftok(argv[1],0),0,SVSHM_MODE);
20     ptr = shmat(id,NULL,0);
21     shmctl(id,IPC_STAT,&buff); //获取共享内存区大小
22     for(i=0;i<buff.shm_segsz;i++)
23         *ptr++ = i % 256;
24     exit(0);
25 }
复制代码

程序4:从共享内存去中读出模式,程序如下:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/shm.h>
 5 #include <fcntl.h>
 6 
 7 #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
 8 
 9 int main(int argc,char *argv[])
10 {
11     int     i,id;
12     struct shmid_ds buff;
13     unsigned char c,*ptr;
14     if(argc != 2)
15     {
16         printf("usage: shmread <pathname>.\n");
17         exit(0);
18     }
19     id = shmget(ftok(argv[1],0),0,SVSHM_MODE);
20     ptr = shmat(id,NULL,0);
21     shmctl(id,IPC_STAT,&buff);
22     for(i=0;i<buff.shm_segsz;i++)
23     {
24         c = *ptr++;
25         printf("ptr[%d] = %d\n",i,c);
26     }
27     exit(0);
28 }
复制代码

 3、System V 与Posix 共享内存区

  二者的差别是:

(1)Posix共享内存区是先调用shm_open然后再调用mmap,System V 共享内存区是先调用shmget再调用shmat。

(2)Posix共享内存区对象的大小可在任何时刻通过ftruncate修改,而System V 共享内存区对象的大小是在调用shmget创建时固定下来的。

 

===========================================================

http://blog.chinaunix.net/uid-26833883-id-3230813.html

A .读共享内存

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>

#define READ 0
#define WRITE 1

#define N 2

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

//初始化信号灯集中的信号灯的值
int my_sem_init(int semid)
{
    int i = 0;
    union semun mysemun;

    for(i = 0;i < N;i ++)
    {
        mysemun.val = i;
        if(semctl(semid,i,SETVAL,mysemun) < 0)
        {
            perror("Fail to semctl");
            return -1;
        }
    }

    return 0;
}

//信号灯集中的信号灯释放资源
int my_sem_post(int semid,int sem_num)
{
    struct sembuf op;

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

    if(semop(semid,&op,1) < 0)
    {
        perror("Fail to semop");
        return -1;
    }

    return 0;
}

//信号灯集中的信号灯申请资源 
int my_sem_wait(int semid,int sem_num)
{
    struct sembuf op;

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

    if(semop(semid,&op,1) < 0)
    {
        perror("Fail to semop");
        return -1;
    }

    return 0;
}

//读共享内存
int read_share_memory(int semid,char *addr)
{
    int n;

    while(1)
    {
        my_sem_wait(semid,READ);
        
        printf("Read : %s.\n",addr);

        my_sem_post(semid,WRITE);

        if(strncmp(addr,"quit",4) == 0)
        {
            if(shmdt((void *)addr) < 0)
            {
                perror("Fail to semdt");
                return -1;
            }
            
            break;
        }

    }
    
    return 0;
}

int main(int argc,char *argv[])
{
    key_t key;
    void *shmaddr;
    int shmid,semid;
    
    if(argc < 2)
    {
        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }
    
    //获取键值
    if((key = ftok(argv[1],'a')) < 0)
    {
        perror("Fail to ftok");
        exit(EXIT_FAILURE);
    }
    
    //创建共享内存
    if((shmid = shmget(key,1024,IPC_CREAT | 0666)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE);
    }

    //映射共享内存到进程地址空间 
    if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
    {
        perror("Fail to shmat");
        exit(EXIT_FAILURE);
    }
    
    //创建含有2个的信号灯的信号灯集
    if((semid = semget(key,N,IPC_CREAT | 0666)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE);
    }

    //初始化信号灯集中的信号灯
    my_sem_init(semid);
    
    read_share_memory(semid,(char *)shmaddr);

    exit(EXIT_SUCCESS);
}

 

  

写共享内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>

#define READ 0
#define WRITE 1

#define N 2

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
}mysemun;

//初始化信号灯集中的信号灯的值
int my_sem_init(int semid)
{
    int i = 0;

    for(i = 0;i < N;i ++)
    {
        mysemun.val = i;
        if(semctl(semid,i,SETVAL,mysemun) < 0)
        {
            perror("Fail to semctl");
            return -1;
        }
    }

    return 0;
}

//信号灯集中的信号灯释放资源
int my_sem_post(int semid,int sem_num)
{
    struct sembuf op;

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

    if(semop(semid,&op,1) < 0)
    {
        perror("Fail to semop");
        return -1;
    }

    return 0;
}

//信号灯集中的信号灯申请资源 
int my_sem_wait(int semid,int sem_num)
{
    struct sembuf op;

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

    if(semop(semid,&op,1) < 0)
    {
        perror("Fail to semop");
        return -1;
    }

    return 0;
}

//写共享内存
int write_share_memory(int semid,char *addr)
{
    int n;

    while(1)
    {
        my_sem_wait(semid,WRITE);

        printf(">");
        fgets(addr,1024,stdin);
        addr[strlen(addr)-1] = '\0';

        my_sem_post(semid,READ);

        if(strncmp(addr,"quit",4) == 0)
        {
            if(shmdt((void *)addr) < 0)
            {
                perror("Fail to semdt");
                return -1;
            }
            
            break;
        }

    }
    
    return 0;
}

int main(int argc,char *argv[])
{
    key_t key;
    void *shmaddr;
    int shmid,semid;
    
    if(argc < 2)
    {
        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);
        exit(EXIT_FAILURE);
    }
    
    //获取键值
    if((key = ftok(argv[1],'a')) < 0)
    {
        perror("Fail to ftok");
        exit(EXIT_FAILURE);
    }
    
    //创建共享内存
    if((shmid = shmget(key,1024,IPC_CREAT | 0666)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE);
    }

    //映射共享内存到进程地址空间 
    if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
    {
        perror("Fail to shmat");
        exit(EXIT_FAILURE);
    }
    
    //创建含有2个的信号灯的信号灯集
    if((semid = semget(key,N,IPC_CREAT | 0666)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE);
    }

    //初始化信号灯集中的信号灯
    my_sem_init(semid);
    
    write_share_memory(semid,(char *)shmaddr);

    if(shmctl(shmid,IPC_RMID,NULL) < 0)
    {
        perror("Fail to shmctl");
        return -1;
    }

    if(semctl(semid,0,IPC_RMID,0) < 0)
    {
        perror("Fail to semctl WRITE");
        return -1;
    }

    exit(EXIT_SUCCESS);
}

  

 =============================================

http://blog.chinaunix.net/uid-26833883-id-3230564.html

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>


#define BUFF_SIZE 1024

int father_do_work(int shmid)
{
    char *buf;
    void *shmaddr;
    sem_t *prsem;
    sem_t *pwsem;

    //有名信号量
    if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
    {
        perror("Fail to sem open");
        return -1;
    }
        
        //有名信号量
    if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
    {
        perror("Fail to sem open");
        return -1;
    }

    //映射共享内存
    if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
    {
        perror("Fail to shmat");
        exit(EXIT_FAILURE);
    }

    buf = (char *)shmaddr;

    while(1)
    {
        if(sem_wait(pwsem) < 0)
        {
            perror("Fail to sem wait");
            break;
        }

        printf(">");
        fgets(buf,BUFF_SIZE,stdin);
        buf[strlen(buf) - 1] = '\0';
        
        if(sem_post(prsem) < 0)        //若此时child_do_work进程挂掉,sem_post(prsem)挂出的信号不会丢失,post多少次,child重启后仍能收到几次
        {
            perror("Fail to sem post");
            break;
        }
        
        if(strncmp(buf,"quit",4) == 0)
        {
            if(shmdt(shmaddr) < 0)
            {
                perror("Fail to shmaddr");
                exit(EXIT_FAILURE);
            }

            break;
        }
    
        usleep(500);
    }
    
    return 0;
}

int child_do_work(int shmid)
{
    char *buf;
    void *shmaddr;
    sem_t *prsem;
    sem_t *pwsem;

    //
    if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED)
    {
        perror("Fail to sem open");
        return -1;
    }

    if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED)
    {
        perror("Fail to sem open");
        return -1;
    }

    //映射共享内存
    if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)
    {
        perror("Fail to shmat");
        exit(EXIT_FAILURE);
    }

    buf = (char *)shmaddr;

    while(1)
    {
        if(sem_wait(prsem) < 0)
        {
            perror("Fail to prsem");
            break;
        }

        printf("read buf : %s.\n",buf);

        if(sem_post(pwsem) < 0)
        {
            perror("Fail to pwsem");
            break;
        }

        if(strncmp(buf,"quit",4) == 0)
        {
            if(shmdt(shmaddr) < 0)
            {
                perror("Fail to shmaddr");
                exit(EXIT_FAILURE);
            }

            break;
        }
    }
    
    return 0;
}

int main()
{
    int shmid;
    int pid;
    void *shmaddr;
    
    //创建共享内存
    if((shmid = shmget(IPC_PRIVATE,BUFF_SIZE,0666 | IPC_CREAT)) < 0)
    {
        perror("Fail to shmget");
        exit(EXIT_FAILURE);
    }


    if((pid = fork()) < 0)
    {

        perror("Fail to fork");
        exit(EXIT_FAILURE);
    
    }else if(pid == 0){
    
        child_do_work(shmid);
    
    }else{
        
        father_do_work(shmid);
        wait(NULL);

        if(shmctl(shmid,IPC_RMID,NULL) < 0)
        {
            perror("Fail to shmctl");
            exit(EXIT_FAILURE);
        }
    }

    exit(EXIT_SUCCESS);
}