Linux(高级编程)8————进程间通信4(共享内存)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FangXiaXin/article/details/84331726

共享内存是什么?
因为进程之间是相互独立的,他们有各自进程地址空间,那么他们需要通信时就要借助内核来为他们建立桥梁,像之前我们了解的管道、消息队列就是内核做的工作来为进程间通信架的桥梁。共享内存也是内核为进程间通信驾的一座桥梁,只不过这座桥梁比其他桥梁更优,共享内存是内核为需要通信的进程开辟了一块公共的空间(内存),而这些需要通信的进程只需将这块空间映射到自己的进程地址空间即可通过操作这块共享内存,便可以通信了。
画一幅图可以加深对共享内存的理解:
在这里插入图片描述
通过这幅,可以让我们对共享内存有了一个初步了解。
关于这幅图,我们就知道了,为什么共享内存是进程间通信的最快方式了:
原因是管道、消息队列都需要将数据从用户态拷到内核态。而共享内存则不需要这一操作。
在这里插入图片描述
管道通信现将数据写入用户态,然后再拷到内核。而共享内存则少了这一动作(从用户态到内核)。
** 共享内存**

  • 共享内存特性:共享内存是直接将一块内存映射到进程地址空间中,进行数据传输相较于其他通信方式少了两次数据拷贝,少了用户与内核之间数据的拷贝。
  • 共享内存的操作步骤:
    1.创建共享内存
    2.映射共享内存到虚拟地址空间。
    3.数据拷贝。
    4.通信结束:a.解除映射。b.删除共享内存。
  • 关于共享内存各个接口的介绍:
    1.共享内存的创建:
	 int shmget(key_t key, size_t size, int shmflg);

参数:
key:内核对共享内存的标识。
size:需要共享内存的大小(字节数)。
shmflg:权限标志位
IPC_CREAT 如果不存在则创建。
IPC_EXCL 如果存在则报错返回。
IPC_PRIVATE 标识只能用于具有亲缘关系的进程间通信。
如:IPC_CREAT|IPC_EXCL|0664
返回值:成功返回操作句柄;失败返回-1

2.建立映射:

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

参数:
shmid:操作句柄
shmaddr:需要映射的起始地址(一般用NULL,让操作系统为我们做)。
shmflg:SHM_RDONLY 标识为只读。否则为读写
返回值:成功返回共享内存首地址(void*)。失败返回-1。
3.解除映射:

 int shmdt(const void *shmaddr);

shmaddr:需要解除映射的共享内存的首地址。
4.共享内存控制:

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

shmid:操作句柄
cmd:需要的操作,常用操作如下:
IPC_RMID删除
IPC_SET 设置共享内存
buf:描述共享内存的信息,关心则接收,不关心则可以忽略。
**注意:**共享并不是cmd使用了IPC_RMID就会立刻删除共享内存,而是这块共享内存的连接数会建1,只有当这块共享内存的连接减为0的时候才会真正的删除这块共享内存。
下面是关于共享内存使用的一个小案例:
还是关于server&client的小程序
server.c:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define KEY 0x123456

int main()
{
        //1.创建共享内存
        int shmid = shmget(KEY,1024,IPC_CREAT|0664);
        if(shmid<0)
        {
                perror("shmget");
                exit(-1);
        }
        //建立连接
        void* shm_start = shmat(shmid,NULL,0);
        while(1)
        {
                sleep(5);
                printf("client say: %s",shm_start);
                printf("\n");
                
        }
        //解除连接
        if(shmdt(shm_start) == 0)
                printf("解除完成!");
        shmctl(shmid,IPC_RMID,NULL);
        return 0;
}

client.c:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define KEY 0x123456

int main()
{
        //1.创建或打开共享内存
        int shmid = shmget(KEY,1024,IPC_CREAT|0664);
        if(shmid<0)
        {
                perror("shmget");
                exit(-1);
        }
        //建立连接
        void* shm_start = shmat(shmid,NULL,0);
        while(1)
        {
                printf("client enter# ");
                fflush(stdout);
                scanf("%s",shm_start);
        }
        //解除连接
        if(shmdt(shm_start) == 0)
                printf("解除完成!");
        shmctl(shmid,IPC_RMID,NULL);
        return 0;
}

效果展示server:
在这里插入图片描述
client:
在这里插入图片描述
我们会发现这小程序存在一些缺点,它只能够单向通信,但是我们知道共享内存是双向通信的,那么这是为什么?因为共享内存直接映射到进程地址空间的那么只要映射了这块共享内存就可以操作这块空间了,势必会造成数据安全问题,那么对于数据安全问题,我们可以配合信号量对这块空间访问进行保护,在这里暂时还不行,下面我们介绍了信号量就可以对这个程序进行优化了。

猜你喜欢

转载自blog.csdn.net/FangXiaXin/article/details/84331726