Linux讲解 进程间通信 共享内存

   今天我们要介绍的是最后一个进程间通信的方式也是最重要的一个共享内存,共享内存是最快的一个IPC形式。所以在实际应用中有着很广泛的应用。

   共享内存的意思是,我们现在把同一块物理内存映射到进程A和进程B两个进程的地址空间,两个进程都可以看到这一块内存。共享内存之所以要比其他的进程间通信快是因为,我们的进程可以直接读取内存的数据而不需要进行任何拷贝,不需要调用任何系统调用来传递数据。而我们的管道和消息队列这种是需要经过:服务器端接收到我们的数据、通过消息队列把数据拷贝到我们的内核中,客户端再从数据从内核中拷贝出来。之后再拷贝到我们的输出文件。但是我们的共享内存只需要两次:输出数据到我们的共享内存中,读取端从共享内存中读取数据。共享内存不涉及内核操作所以速度是IPC中最快的一种形式。但是需要注意的是共享内存并没有提供同步和互斥机制的这就需要我们自己来实现对共享内存访问的同步和互斥。

  我们通过代码来实现一下共享内存的通信方式:

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<errno.h>

#include<sys/ipc.h>

#include<sys/shm.h>



#define IPC_KEY 0X12345678



int main()

{

    int shmid=-1;

    shmid=shmget(IPC_KEY,32,IPC_CREAT|0664);

    //通过shmat函数来创建共享内存

    //第一个参数是建立共享对象的key值

    //第二个参数是创建共享内存的大小

    //第三个参数是如果不存在则创建,存在的话就打开,0664是创建文件的权限

    if(shmid<0)

    {

     perror("shmget errno\n");

     return -1;

    }

    void* shm_start=shmat(shmid,NULL,0);

    //shmat函数是将我们共享内存映射到虚拟空间地址

    //第一个参数是我们的操作句柄

    //第二个参数是映射的地址空间,如果传入NULL,操作系统会自动给分配一个

    //第三个参数是读写权限,如果是SHM_RDONLY为只读,否则是读写

    if(shm_start==(void*)  -1)

    {

        perror("shmat error\n");

        return -1;

    }

    while(1)

    {

     printf("%s\n",(char *)shm_start);

     sleep(1);

    }

    //通过while循环来实现从共享内存中读取数据

    shmdt(shm_start);

    //shmdt是用来解除我们的内存映射,参数是我们共享内存的地址空间

    shmctl(shmid,IPC_RMID,NULL);

    //shmctl是来删除我们创建的共享内存

    //第一个参数shmid是我们的描述符,第二个参数就代表着删除,第三个参数用来接收共享内存描述的信息,不关系就传入NULL

    return 0;

}

这是我们的客户端,这里我们实现的是单向通信,也就是接受数据的一端。

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<errno.h>

#include<sys/ipc.h>

#include<sys/shm.h>



#define IPC_KEY 0x12345678



int main()

{

  int shmid=-1;

  shmid=shmget(IPC_KEY,32,IPC_CREAT|0664);

  if(shmid<0)

  {

   perror("shmget error\n");

   return -1;

  }

  void* shm_start=shmat(shmid,NULL,0);

  if(shm_start==(void*)-1)

  {

      perror("shmat error\n");

      return -1;

  }

  while(1)

  {

     printf("please input \n");

     fflush(stdout);

     memset(shm_start,0x00,32);

     scanf("%s",(void *)shm_start);

     sleep(1);

  }

  shmdt(shm_start);

  shmctl(shmid,IPC_RMID,NULL);

  return 0;

}

这是我们的服务端,也就是我们的发送数据的一端。

我们来运行两个程序

Linux讲解 进程间通信 共享内存

Linux讲解 进程间通信 共享内存

我们可以在一端输入数据,在另一端可以输出数据。

Linux讲解 进程间通信 共享内存

共享内存实际上就是让我们两个进程来访问同一块内存地址的空间

我们可以通过 ipcs -m来查看我们创建的共享内存

Linux讲解 进程间通信 共享内存

可以看到key是0x12345678的共享内存,大小是32.

还是可以通过ipcrm来删除我们已经创建的共享内存

Linux讲解 进程间通信 共享内存

这里我们简单的总结一下到目前三种进程间通信的优缺点:

  1. 管道:匿名管道简单方便.但局限于单向通信的工作方式.并且只能在有亲缘关系的进程之间实现管道通信;命名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,可以直接打开文件访问等容易导致出现问题

  2. 消息队列:消息缓冲可以不再局限于父子进程.而允许任意进程通过共享消息队列来实现进程间通信.并由系统调用函数来实现消息发送和接收之间的同步.从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题.使用方便,但是信息的复制需要额外消耗CPU的时间.不适宜于信息量大或操作频繁的场合

  3. 共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的.因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中.所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。

Linux讲解 进程间通信 共享内存

这张图片就可以很好地理解为什么我们的共享内存要比别的通信方式要快。我们直接把我们的内容写入到了对应的内存,读内容的时候直接把我们从哪内存中读取的数据展示出来。