• linux网络编程之共享内存介绍


    今天是个好日子,洋人之节乃全球同庆,圣诞一来感觉就要过年了,不过今晚心情有点打折扣,给心爱的人打电话没有打通,本想在平安夜送上快乐的祝福给她,糟糕的心情让自己好像泄了气的皮球一样,无精打彩,心情灰暗,不过我爱的人只要幸福快乐,一个电话又值得了几个钱,也许她也在欢庆,那此时的我也要同庆啦,在这美好的夜晚我该如何度过呢,当然是学习啦,集中去干一件自己喜欢的事当然也无比快乐美好的,稍微抒发了一下内心,言归正传~

     

     

    1、用管道或者消息队列传递数据

    这个示意图的功能是服务器向客户端传输文件,如下:

    ①、首先要将文件从内核读取到进程的用户空间当中,所以这里就涉及到了一次read()系统调用。

    ②、服务器需要将读取到的数据拷贝到管道或消息队列当中,涉及到第二次系统调用。

    ③、对于客户端来说,需要从管道或消息队列中读取这些数据,涉及到第三次系统调用。

    ④、读到这些数据到应用的数据缓冲区当中,然后将缓冲区的内容写到输出文件中,涉及到第四次系统调查用。

    从以上步骤来看,总共涉及到了四次系统调用, 四次内存拷贝(从内核空间到用户空间),那共享内容方式又如何呢?

    2、用共享内存传递数据

    下面来学习一下相关函数的使用:

     

    下面来看一下内存映射文件示意图:

    下面则用代码来实践一下:

    下面来创建一个文件:

    下面来运行一下:

    接下来对文件进行映射:

    当映射成功之后,接下来则往文件中写入一些数据,这时候就可以直接通过指针写入了,对文件的操作就好像对内存的访问,如下:

    下面来运行一下:

    可见就通过内存的方式来对文件进行了数据写入,这就是内存映射文件的作用。

    下面来写一个读取文件内容的功能,将写入的五个同学的数据读出来,基于mmap_write.c来写,代码差不多:

    mmap_read.c:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    
    typedef struct stu
    {
        char name[4];
        int age;
    } STU;
    
    int main(int argc, char *argv[])
    {
        if (argc != 2)
        {
            fprintf(stderr, "Usage: %s <file>
    ", argv[0]);
            exit(EXIT_FAILURE);
        }
    
        int fd;
        fd = open(argv[1], O_RDWR);
        if (fd == -1)
            ERR_EXIT("open");
    
        STU *p;
        p = (STU*)mmap(NULL, sizeof(STU)*5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (p == NULL)
            ERR_EXIT("mmap");
    
        //从映射内存中,来读取文件的内容
        int i;
        for (i=0; i<5; i++)
        {
            printf("name = %s age = %d
    ", (p+i)->name, (p+i)->age);
        }
    
        munmap(p, sizeof(STU)*5);
        printf("exit ...
    ");
    
        return 0;
    }

    编译运行:

    【了解既可】

     

    对共享内存进行写操作的时候:

    实际上,这些操作并没有立刻写回到文件当中,内核也会选择一个比较好的时机将这些内容写入到文件当中,如果我们想要立刻写回到文件当中,则就可以用msync函数了。

    ①、映射不能改变文件的大小

    下面来修改一下程序来验证一下:

    ②、可用于进程间通信的有效地址空间不完全受限于被映射文件的大小

    mmap在映射的时候,是将其映射到一个内存页面当中,也就是说,我们可以通讯的区域是以内存页面为单位的,比如我们映射了40个字节,但是内存页面肯定是大于40个字节的,所以能够通信的有效地址空间肯定是超过了40个字节,下面也用程序来说明一下:

    编译运行:

    在写进程还没结束时,再快速地运行读进程时,从中可以发现读到了10个学生信息,这也就论证了这点,为什么能读到10个学生信息,因为我们所映射的内存共享区大于文件的内容,因为映射的时候是基于页面来分配的,我们映射了40个字节,可能分配了4K的空间,只要我们在这4K的地址空间中访问就不会出错,如果我们超过了4K的地址空间,则很可能会产生一个SIGBUS的信号,如果我们访问的大小超过了几个内存页面,则有可能还会产生一个SIGSEGV信号,这取决于我们超出部份的大小。

    而如果写进程结束了,再来读取,从实验结果来看,后面的五个学生信息就读取不到了,为什么呢?因为写进程结束了,也就是先前的那块内存映射区域,对于读端进程来说已经看不到了,这时读端进程又去从文件当中进行映射,只能够看到五个学生的信息了,所以说为啥要用sleep 10来说明这一问题。

    ③、文件一旦被映射后,所有对映射区域的访问实际上是对内存区域的访问。映射区域内容写回文件时,所写内容不能超过文件的大小,

    好了,今天就学到这,有些难理解,可以好好消化,下回见~

  • 相关阅读:
    update语句
    java List和数组相互转换方法
    mysql查最大字符串
    Mybatis各种模糊查询
    mysql 递归查询父节点 和子节点
    String类型根据逗号分隔转为list
    This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de 错误解决办法
    java中String数组和List的互相转化
    实现List集合中数据逆序排列
    String字符串去掉双引号
  • 原文地址:https://www.cnblogs.com/webor2006/p/4183556.html
Copyright © 2020-2023  润新知