进程间通信之共享内存

来源:互联网 发布:小米机顶盒下载软件 编辑:程序博客网 时间:2024/06/06 04:18

概述

共享内存就是分配一块能被其他进程访问的内存。


共享内存的数据结构

共享内存和消息队列,信号量一样,在内核中也维护着一个内部结构。

#include<linux/shm.h>{    struct ipc_perm    shm_perm;    int                shm_segsz;    __kernel_time_t    shm_atime;      __kernel_time_t    shm_dtime;         __kernel_time_t    shm_ctime;       __kernel_ipc_pid_t shm_cpid;        __kernel_ipc_pid_t shm_lpid;       unsigned short     shm_nattch;      unsigned short     shm_unused;       void               *shm_unused2;     void               *shm_unused3; }

代码中主要字段含义如下:

  • shm_perm:操作许可,包含共享内存的用户ID,组ID
  • shm_segsz:共享内存段的大小,单位是字节
  • shm_atime:最后一个进程访问共享内存的时间
  • shm_dtime:最后一个进程离开共享内存的时间
  • shm_ctime:最后一次修改共享内存的时间
  • shm_cpid:创建共享内存的进程ID
  • shm_lpid:最后操作共享内存的进程ID
  • shm_nattch:当前使用该共享内存的进程数量

共享内存的创建与操作

共享内存区的创建

#include<linux/shm.h>int shmget(key_t key, size_t size, int shmflg)

各个参数说明如下:

  • key:由ftok函数得到的键值
  • size:指定内存的大小,以字节为单位
  • shmflg:操作标志位

shmflg的宏取值:

  • IPC_CREATE:调用shmget时,系统将此值与其他所有共享区内的key进行比较,key值相同的,返回已存在的内存共享区标识符,否则新建一个内存共享区并返回其标识符
  • IPC_EXCL:单独使用无意义,与上述参数结合使用,不同的是如果共享内存中已有和现在的key值相等的,直接返回-1,错误码为EEXIST

注意:如果创建一个新的内存共享区,必须使size大于0,否则置size为0。


共享内存区的操作

在使用共享内存之前,必须先使用shmat函数将共享内存附加到进程的地址空间中,这样共享内存就和进程建立了联系。

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

该函数调用成功会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,函数调用失败返回-1。

各参数说明如下:

  • shmid:函数shmget的返回值
  • shmflg:存取权限标志
  • shmaddr:共享内存的附加点

参数shmaddr取值:

  • NULL:由内核选择一个空闲的内存区
  • !=NULL:返回地址取决于调用者是否给shmflg参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定的地址,否则附加地址为shmaddr 向下舍入一个共享内存低端边界地址后的地址

注意:通常将参数shmaddr设置为NULL。

当进程结束使用共享内存区时,要通过函数shmdt断开与共享内存区的连接。

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

参数shmaddr为shmat函数的返回值。函数调用成功后,返回0,否则返回-1。进程脱离共享内存区后,数据结构shmid_ds中的shm_nattch就会减一。此时共享内存段仍然存在,只有当shm_nattch为0的时候,共享内存区才会被删除。一般来说,当一个进程终止的时候,它所附加的共享内存区都会自动脱离。


共享内存区的控制

#include<sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf)

各参数说明如下:

  • shmid:共享内存区的标识符
  • buf:指向shmid_ds结构体的指针
  • cmd:操作的标志位

cmd 3种控制操作:

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

共享内存的应用实例

本例通过共享内存和信号量的配合使用,使一个进程读共享内存的时候,其他进程不能写内存;当一个进程写共享内存的时候,其他进程不能读内存。

sharemem.h:(将函数接口写好)

#ifndef _SHAREMEM_H#define _SHAREMEM_H#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>#include<sys/shm.h>#include<errno.h>#define SHM_SIZE 1024                          //共享内存的大小union semun                                    //信号集的结构体{    int val;    struct semid_ds *buf;    unsigned short *array;};/*创建信号量函数*/int createsem(const char *pathname, int proj_id, int members, int init_val){    key_t msgkey;    int index,sid;    union semun semopts;    if((msgkey = ftok(pathname, proj_id)) == -1)                     //创建键值    {        perror("ftok error");        exit(1);    }    if((sid = semget(msgkey, members, IPC_CREAT | 0666)) == -1)      //创建信号集    {        perror("semget error");        exit(1);    }    /*初始化操作*/    semopts.val = init_val;    for(index = 0; index < members; index++)    {        semctl(sid, index, SETVAL, semopts);    }    return sid;                                                      //返回信号集标志符}/*打开信号量函数*/int opensem(const char *pathname, int proj_id){    key_t msgkey;    int sid;    if((msgkey = ftok(pathname, proj_id)) == -1)    {        perror("ftok error");        return -1;    }    if((sid = semget(msgkey, 0, IPC_CREAT | 0666)) == -1)    {        perror("semget call failed");        return -1;    }    return sid;}/*P操作函数*/int sem_P(int semid, int index){    struct sembuf buf = {0, -1, IPC_NOWAIT};    if(index < 0)    {        perror("No index");        return -1;    }    buf.sem_num = index;    if(semop(semid, &buf, 1) == -1)               //第三个参数表示要操作信号的个数    {        perror("semop error");        return -1;    }    return 0;}/*V操作函数*/int sem_V(int semid, int index){    struct sembuf buf = {0, 1, IPC_NOWAIT};    if(index < 0)    {        perror("No index");        return -1;    }    buf.sem_num = index;    if(semop(semid, &buf, 1) == -1)               //第三个参数表示要操作信号的个数    {        perror("semop error");        return -1;    }    return 0;}/*删除信号集函数*/int sem_delete(int semid){    return (semctl(semid, 0, IPC_RMID));}/*等待信号为1*/int wait_sem(int semid, int index){    while(semctl(semid, index, GETVAL) == 0)    {        sleep(1);    }    return 1;}/*常见内存共享函数*/int createshm(char *pathname, int proj_id, size_t size){    key_t shmkey;    int sid;    if((shmkey = ftok(pathname, proj_id)) == -1)                 //获取键值    {        perror("ftok error");        return -1;    }    if((sid = shmget(shmkey, size, IPC_CREAT | 0666)) == -1)    {        perror("shmget error");        return -1;    }    return sid;                                                  //返回共享内存标识符}#endif

writer.c:(给共享内存写东西的进程)

#include<stdio.h>#include<string.h>#include<sharemem.h>int main(){    int semid,shmid;    char *shmaddr;    char write_str[SHM_SIZE];    if((shmid = createshm(".", 'm', SHM_SIZE)) == -1)             //创建共享内存区    {        exit(1);    }    if((shmaddr = shmat(shmid, NULL, 0)) == NULL)                 //得到指向共享内存的指针    {        perror("shmat error");        exit(1);    }    if((semid = createsem(".", 's', 1, 1)) == -1)                 //创建信号集    {        exit(1);    }    while(1)    {        wait_sem(semid, 0);                                       //等待信号集中的信号值是否为1        sem_P(semid, 0);                                          //P操作        printf("writer: ");        fgets(write_str, 1024, stdin);        int len = strlen(write_str) -1;        write_str[len] = '\0';                                    //将'\n'去掉        strcpy(shmaddr, write_str);        sleep(3);                                                 //使reader,读进程处于阻塞状态        sem_V(semid, 0);                                          //V操作        sleep(5);                                                 //等待reader进行读操作    }    return 0;}

reader.c:(从共享内存读东西的进程)

#include<stdio.h>#include"sharemem.h"int main(){    int semid, shmid;    char *shmaddr;    if((shmid = createshm(".", 'm', SHM_SIZE)) == -1)    {        exit(1);    }    if((shmaddr = shmat(shmid, NULL, 0)) == NULL)    {        perror("shmat error");        exit(1);    }    if((semid = opensem(".", 's')) == -1)    {        exit(1);    }    while(1)    {        printf("reader:");        wait_sem(semid, 0);        sem_P(semid, 0);        printf("%s\n", shmaddr);        sleep(3);        sem_V(semid, 0);        sleep(5);    }    return 0;}

运行结果:
写端:
这里写图片描述

读端:
这里写图片描述

内存共享实现了进程间通信,并且信号量可以实现锁的功能。

0 0
原创粉丝点击