System V进程间通信--共享内存

来源:互联网 发布:delphi 数据库 编辑:程序博客网 时间:2024/05/16 19:22

一、共享内存IPC原理

共享内存进程间通信机制主要用于实现进程间大量数据的传输,共享内存是在内存中单独开辟的一段内存空间,这段内存空间有自己特有的数据结构,包括访问权限、大小和最近访问时间。
这里写图片描述

数据结构定义如下:

struct shmid_ds {    struct ipc_perm     shm_perm;   /* operation perms */    int         shm_segsz;  /* size of segment (bytes) */    __kernel_time_t     shm_atime;  /* last attach time */    __kernel_time_t     shm_dtime;  /* last detach time */    __kernel_time_t     shm_ctime;  /* last change time */    __kernel_ipc_pid_t  shm_cpid;   /* pid of creator */    __kernel_ipc_pid_t  shm_lpid;   /* pid of last operator */    unsigned short      shm_nattch; /* no. of current attaches */    unsigned short      shm_unused; /* compatibility */    void            *shm_unused2;   /* ditto - used by DIPC */    void            *shm_unused3;   /* unused */};

两个进程在使用此共享内存空间之前,需要在进程地址空间与共享内存空间之间建立联系,即将共享内存空间挂载到进程中。并且在使用共享内存进行数据的存取时,需要对空间进行同步操作,这个可以使用信号量机制完成。

系统对共享内存做了以下限制:

#define SHMMIN 1             /* min shared seg size (bytes) */#define SHMMNI 4096          /* max num of segs system wide */#define SHMMAX (ULONG_MAX - (1UL << 24)) /* max shared seg size (bytes) */#define SHMALL (ULONG_MAX - (1UL << 24)) /* max shm system wide (pages) */#define SHMSEG SHMMNI            /* max shared segs per process */

二、共享内存的管理

1、创建共享内存

函数原型:
int shmget(key_t key, size_t size, int shmflg)

函数传入值:
key
0(IPC_PRIVATE):会建立新共享内存对象
大于0的32位整数:视参数shmflg来确定操作。通常要求此值来源于ftok返回的IPC键值

size
大于0的整数:新建的共享内存大小,以字节为单位
0:只获取共享内存时指定为0

shmflg
0:取共享内存标识符,若不存在则函数会报错
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错

函数返回值:
成功:返回共享内存的标识符
出错:-1,错误原因存于error中

2、共享内存控制

函数原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf)

函数传入值:
shmid
共享内存标识符

cmd
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
IPC_RMID:删除这片共享内存

buf
共享内存管理结构体。具体说明参见共享内存内核结构定义部分

函数返回值
成功:0
出错:-1,错误原因存于error中

3、映射共享内存对象

函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg)

函数传入值:
shmid
共享内存标识符

shmaddr
指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置

shmflg
SHM_RDONLY:为只读模式,其他为读写模式

函数返回值
成功:附加好的共享内存地址
出错:-1,错误原因存于errno中

4、分离共享内存对象

函数原型:
int shmdt(const void *shmaddr)

函数传入值:
shmaddr:连接的共享内存的起始地址

函数返回值:
成功:0
出错:-1,错误原因存于error中

三、使用共享内存进行进程间通信的实例

1、发送端进程

/* *使用共享内存进行进程间的通信,并运用信号量机制实现同步 *此为信息的发送者 */#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<unistd.h>#include<string.h>#include<sys/ipc.h>#include<sys/shm.h>#include<sys/sem.h>int main(int argc , char* argv[]){    int running = 1 ;    int shid ;    int semid ;    int value ;    void* sharem = NULL ;    struct sembuf sem_b ;    sem_b.sem_num = 0 ;    sem_b.sem_flg = SEM_UNDO ;    if((semid = semget((key_t)123456 , 1 , 0600 | IPC_CREAT)) == -1) //创建信号量(或读ID)    {        perror("semget") ;        exit(EXIT_FAILURE) ;    }    if(semctl(semid , 0 , SETVAL , 0) == -1) //设置初始值为0    {        printf("sem init error\n") ;        if(semctl(semid , 0 , IPC_RMID , 0) != -1) //设置失败则进行删除        {            perror("semctl") ;            exit(EXIT_FAILURE) ;        }        exit(EXIT_FAILURE) ;        }    shid = shmget((key_t)654321 , (size_t)2048 , 0600 | IPC_CREAT) ; //创建共享内存(或读ID)    if(shid == -1)    {        perror("shmget") ;        exit(EXIT_FAILURE) ;    }    sharem = shmat(shid , NULL , 0) ; //挂载共享内存到当前进程    if(sharem == NULL)    {        perror("shmat") ;        exit(EXIT_FAILURE) ;    }    while(running)    {        if(value = semctl(semid , 0 , GETVAL) == 0) //读取值为0则可以写        {            printf("write data operate\n") ;            printf("please input something :") ;            scanf("%s" , sharem) ;            sem_b.sem_op = 1 ;            if(semop(semid , &sem_b , 1) == -1) //实行信号量的自加操作,允许读            {                fprintf(stderr , "semaphore_p failure\n") ;                exit(EXIT_FAILURE) ;            }        }        if(strcmp(sharem , "end") == 0)        {            running-- ;        }    }    shmdt(sharem) ; //解挂    return 0 ;}

2、接收端进程

/* *使用共享内存进行进程间的通信,并运用信号量机制实现同步 *此为信息的接收者 */#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<unistd.h>#include<string.h>#include<sys/ipc.h>#include<sys/shm.h>#include<sys/sem.h>int main(int argc , char* argv[]){    int running = 1 ;    int shid ;    int semid ;    int value ;    void* sharem = NULL ;    struct sembuf sem_b ;    sem_b.sem_num = 0 ;    sem_b.sem_flg = SEM_UNDO ;    if((semid = semget((key_t)123456 , 1 , 0600 | IPC_CREAT)) == -1) //创建信号量(或读ID)    {        perror("semget") ;        exit(EXIT_FAILURE) ;    }    shid = shmget((key_t)654321 , (size_t)2048 , 0600 | IPC_CREAT) ; //创建共享内存(或读ID)    if(shid == -1)    {        perror("shmget") ;        exit(EXIT_FAILURE) ;    }    sharem = shmat(shid , NULL , 0) ; //挂载共享内存到当前进程    if(sharem == NULL)    {        perror("shmat") ;        exit(EXIT_FAILURE) ;    }    while(running)    {        if(value = semctl(semid , 0 , GETVAL) == 1) //读取值为1则可以写        {            printf("read data operate\n") ;            sem_b.sem_op = -1 ;            if(semop(semid , &sem_b , 1) == -1) //实行信号量的自减操作,允许写            {                fprintf(stderr , "semaphore_p failure\n") ;                exit(EXIT_FAILURE) ;            }            printf("%s\n" , sharem) ;        }        if(strcmp(sharem , "end") == 0)        {            running-- ;        }    }    shmdt(sharem) ; //解挂    if(shmctl(shid , IPC_RMID , 0) != 0) //删除共享内存    {        perror("shmctl") ;        exit(EXIT_FAILURE) ;    }    if(semctl(semid , IPC_RMID  , 0) != 0) //删除信号量    {        perror("semctl") ;        exit(EXIT_FAILURE) ;    }    return 0 ;}

3、我们可以查看运行结果

发送端:
这里写图片描述

接收端:
这里写图片描述

可以看到顺利通信(:

0 0