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

一、共享内存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、我们可以查看运行结果

发送端:
这里写图片描述

接收端:
这里写图片描述

可以看到顺利通信(:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值