Linux学习笔记(14)-进程通信|共享内存

 

  在Linux中,共享内存是允许两个不相关的进程访问同一个逻辑内存的进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。

  不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。

  就好像它们是由用C语言malloc()分配的内存一样。

 

  建立一个共享内存大概有五个步骤:

  1.调用shm_open()函数,在指定一个内存的名字,用来创建或者打开一个共享内存

  2.调用mmap()函数,把共享内存映射在进程的空间里

  3.调用fturncate()函数来分配共享内存的大小

  在使用完共享内存之后,还得经过下面两个步骤,将内存关闭并释放

  4.调用munmap()函数,取消共享内存的映射

  5.调用shm_unlink()函数来删除共享内存

  函数介绍

  shm_open()

  这个函数关联的头文件是sys/mman.h,sys/stat.h,fcntl.h

  函数原形如下:fd shm_open(const char *name,int oflag, mode_t mode)

  参数name很好理解,就是指定一个名字,用来标识共享内存

  参数oflag的作用在于指定属性,比如新建,打开,只读,只写等等

  参数mode的作用那就很明显了,是用来指定新建内存的权限,这个参数只有在新建共享内存时才有用

————————————————————————————————————————————————

  新创建或者打开的共享内存的大小是0.必须在指定大小之后才能正常的使用,这里用来指定内存大小的函数是:

  ftruncate()

  这个函数关联的头文件有unistd.h,sys/types.h

  函数原形如下:ftruncate(int fd,off_t length)

  参数fd自然就是内存文件的句柄(Linux中,一切都被当做文件来使用,内存当然也不例外)

  length参数便是用来指定内存的大小了,单位是字节

————————————————————————————————————————————————

  在设置完内存的大小后,依然还是不能直接使用,还需要一个步骤,那就是设置内存映射。

  mmap()

  这个函数关联的头文件有sys/mman.h

  函数原形如下:void * mmap(void *addr, size_t length, int port, int flags, int fd, off_t offset)

  参数addr 设置共享内存的起点,如果将其设置为NULL的话,那就意味是由系统自动分配

  参数length就很好理解了,就是共享内存的大小

  参数port是用来设置共享内存的权限,设置属性和open差不多,但不能超过shm_open所设置的权限

  flag是设置一些特殊的要求,比如MAP_FIXED,要求返回值必须等于addr。MAP_SHARED多个进程对同一文件的映射是共享的,

  其中一个进程对映射做了修改,那么别的进程也能看到(这段话暂时还不理解)。

  参数fd,不多解释,就是内存文件的句柄

  参数offset,要映射字节在文件中的偏移

————————————————————————————————————————————————

  在有了以上三个函数之后,基本上就能在进程之间建立一个共享内存了。至于取消内存和删除内存的函数,那就更加简单,这里不在多说。

  程序实例

 

  现在实现一个功能,在两个完全不相干的进程中建立一个共享内存,其中一个进程在向一段内存中写入一个数字,另个一进程读取这个数字。

  

#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<unistd.h>


#define SHM_SIZE 10
#define SHM_NAME "shmname"

int main(void)
{
    int fd;
    char * prt;

    fd = shm_open(SHM_NAME,O_RDWR|O_CREAT,0777);
    if (fd < 0)
    {
        printf("创建共享内存失败!\n");
        exit(-1);
    }

    fturncate(fd,SHM_SIZE);//设置共享内存的大小为10
    prt = mmap(NULL, SHM_SIZE, 0x777, MAP_SHARED, fd,0);
    if (prt == MAP_FAILED)
    {
        printf("创建共享内存映射失败!\n");
        exit(-1);
    }

    *prt = 20;
    munmap(prt,SHM_SIZE);
    shm_unlink(SHM_NAME);

    return 0;

  以上便是代码,现在写完了Makefile之后进行编译,但是在编译中出现了很奇怪的问题。

  

  难道是我把这三个函数的名字打错了吗?

  仔细检查之下,还真有打错的!!!(ftruncate函数打成了fturncate,真是粗心啊!!!)

  把这个函数修改之后,依然不通过,这是什么原因?

  经过我的分析,从编译结果上来看,应该不在是语法上出的问题了……

  开始调查……

  在一个大神的资料中看见这么一句话,我仿佛是看见了救星:

  

     好吧,原来是这个问题!那么开始修正Makefile。

  

  编译还是没有通过,这是怎么回事?

  继续调查……

  半小时后,终于发现了愿意,原来Linux在编译中,首先是从最后面开始进行连接,所以把-lrt放在编译指令的中间是不可以的,一定要放在最后面。

  (不要问我为什么,我也不知道)

  等我把它放在最后,就能编译通过了!

  

  现在写数据的进程已经写完了,开始写读数据的进程。

  

#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>

#define SHM_SIZE 10
#define SHM_NAME "shmname"

int main(void)
{
    int fd;
    char * prt;

    fd = shm_open(SHM_NAME, O_CREAT|O_TRUNC|O_RDWR, 0777);
    if (fd < 0)
    {
        printf("进程2创建共享内存失败!%d\n",errno);
        exit(-1);
    }

    ftruncate(fd,SHM_SIZE);//设置共享内存的大小为10
    prt = mmap(NULL, SHM_SIZE, 0x777, MAP_SHARED, fd,0);
    if (prt == MAP_FAILED)
    {
        printf("创建共享内存映射失败!\n");
        exit(-1);
    }
    while(*prt != 20)
    {
        sleep(1);
    }
    printf("读出数据prt为=%d.\n",*prt);
    munmap(prt,SHM_SIZE);

    return 0;

}

  两个代码都做完了,现在开始编译……完美!

  开始运……啊!!!

  这又怎么啦?

  

  怎么会创建内存失败呢?

  创建共享内存,为什么回返回errno 13呢?

  找了半天,终于找到了问题的原因,原来是因为我在之前代码还不完善的时候,创建了一个名为shmname的内存,

  

  然后又没能把他删掉,而且它的权限还高的吓人,导致它一直存在于/dev/shm路径下,所以等代码完善之后,在运行程序就会发现因为没有权限而运行失败了。

  手动把那个名为shmname的东西删掉后,再次运行程序……OK!!

  

  ————————————————————————————————————————————————————

  本节重点:

  1.在编译共享内存的代码时,要在编译命令的最后面加上-lrt,用来连接库

  2.如果在运行创建共享内存程序时发生errno = 13的错误,可能就是我刚才遇见的错误,在/dev/shm路径下已经有一段同名的共享内存了,

    需要手动删除,或者换一个名字。

  

  

 

    

posted @ 2016-11-23 23:04  波子木木  阅读(1544)  评论(0编辑  收藏  举报