LINUX进程间通信之共享存储讲解,linux进程通信讲解


LINUX进程间通信

一、匿名管道与命名管道

二、消息队列

三、共享存储


共享存储概述:

共享存储允许两个或更多进程共享一给定的存储区。共享内存区是最快的IPC形式,一旦这样的内存映射到共享它的进程的 地址空间,这些进程间数据传递不再涉及到内核,也就是说进程不再需要切换到内核态来传递数据。所以比起消息队列一直在用户态和内核态的切换,共享存储更高效。

相关命令:

ipcs -m :查看当前内核中的共享内存段;
ipcrm -m +shmid :删除对应shmid的共享内存段


共享内存示意图:

这里写图片描述

也就是说:进程同时拿到同一片共享存储区的shmid,使用shmat将对应的共享存储空间连接到自己的进程地址空间,在进行操作完成之后,再使用shmdt断开连接。在整体使用过程中不需要多次进行状态切换。

共享内存数据结构:

这里写图片描述


函数介绍:

一、shmget:获得共享存储标识符

       #include 
       #include 

       int shmget(key_t key, size_t size, int shmflg);

//返回值:成功返回共享内存的标识符,失败返回-1
参数介绍:
①key:与消息队列相同,都是由ftok函数生成;
②size:共享内存大小(单位/字节);通常将其向上取为系统页长的整数倍;
③shmflg:用法和创建文件使用的mode模式标志一样。可参考消息队列中的介绍。

二、shmctl: 控制共享内存

  #include 
       #include 

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

//成功返回0,失败返回-1

参数介绍:

①shmid:需要进行操作的共享内存标识码;
②cmd:需要进行的操作(分为IPC_STAT,IPC_SET,IPC_RMID)
③buf:一个指向shmid_ds结构体类型的结构体指针,当参数为IPC_STAT和IPC_SET时,需要给定一个结构体,用来指定修改的值。

三、shmat:将共享内存段连接到进程地址空间

  #include 
       #include 

       void *shmat(int shmid, const void *shmaddr, int shmflg);

//返回值:成功返回指向共享内存第一个字节的指针,失败返回-1

参数介绍:

①shmid:之前由ftok生成的共享内存标识符;
②shmaddr:指定连接的地址;
③shmflg:可取SHM_RND和SHM_RDONLY
shmaddr取值为NULL,内核自动选择一个地址
shmaddr不为NULL,且shmflg无SHM_RND标记,则以shmaddr为连接地址;
shmaddr不为NULL,且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍
shmflg = SHM_RDONLY:表示连接操作用来只读共享内存。
注:更详细的介绍可自行man或查看《unix环境高级编程》

四、shmdt:将共享内存段与当前进程脱离

  #include 
       #include 

       void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);

//返回值:成功返回0,失败返回-1

参数介绍:

shmaddr:由shmat所返回的指针。

注意:

1、共享内存无同步互斥机制!!
2、共享内存段随内核,所以我们最好是在进程结束之前不再使用时,就断开与共享内存的连接。

示例:通过一片共享内存存储区,client向其中写数据,server读取数据并打印到屏幕上。

/**********comm.h*************/
#pragma once

#include 
#include 
#include 
#include 

#define PATHNAME "."
#define PROJ_ID 0x66

int createShm(int size);

int destroyShm(int shmid);

int getShm(int size);


/**********comm.c**********/
#include "comm.h"

int commShm(int size, int flags)
{
    key_t key = ftok(PATHNAME, PROJ_ID);
    if(key < 0){
        perror("ftok");
        return -1;
    }

    int shmid = shmget(key, size, flags);
    if(shmid < 0){
        perror("shmget");
        return -2;
    }
    return shmid;
}

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

int creatShm(int size)
{
    return commShm(size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(int size)
{
    return commShm(size, IPC_CREAT);
}

/**********client.c********/
#include "comm.h"

int main()
{
    int shmid = getShm(4094);
    char* addr = shmat(shmid, NULL, 0);
    sleep(1);
    int i = 0;
    while(i < 5){
        addr[i] = 'A' +i;
        i++;
        addr[i] = 0;
        sleep(1);
    }
    shmdt(addr);
    sleep(2);
    return 0;
}


/*********server.c*******/
#include "comm.h"

int main()
{
    int shmid = creatShm(4096);

    char* addr = (char*)shmat(shmid, NULL, 0);
    sleep(5);
    int i = 0;
    while(i < 10){
        i++;
        printf("client> %s\n",addr);
        sleep(1);
    }

    shmdt(addr);
    sleep(2);
    destroyShm(shmid);
    return 0;
}



/*******Makefile********/
.PHONY : all
all : server client

server : server.c comm.c
    gcc $^ -o $@

client : client.c comm.c
    gcc $^ -o $@

.PHONY : clean
clean:
    rm server client

运行结果:
打开server端,再打开client端,经过sleep后,client端向共享内存中写入数据,server端不断读取,但是当client端停止写入时,共享内存区的数据还是最后一次写入的数据,所以又多输出5次。

这里写图片描述

相关内容