V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
b00tyhunt3r
V2EX  ›  程序员

百度百科 mmap 示例代码有误?

  •  
  •   b00tyhunt3r · 2019-10-09 18:54:15 +08:00 · 1244 次点击
    这是一个创建于 1632 天前的主题,其中的信息可能已经有所发展或是发生改变。

    百度 mmap ()函数: baike.baidu.com/item/mmap/1322217?fr=aladdin

    下面是通过 mmap ()映射普通文件实现进程间的通信的范例,我们通过该范例来说明 mmap()实现共享内存的特点及注意事项。   范例包含两个子程序:map_normalfile1.c 及 map_normalfile2.c。编译两个程序,可执行文件分别为 map_normalfile1 及 map_normalfile2。两个程序通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。map_normalfile1 试图打开命令行参数指定的一个普通文件,把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。map_normalfile2 把命令行参数指定的文件映射到进程地址空间,然后对映射后的地址空间执行读操作。这样,两个进程通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

    /*-------------map_normalfile1.c-----------*/
    #include<sys/mman.h>
    #include<sys/types.h>
    #include<fcntl.h>
    #include<string.h>
    #include<stdio.h>
    #include<unistd.h>
    #include <errno.h>
    typedef struct{
        char name[4];
        int age;
    }people;
    void main(int argc,char **argv)//map a normal file as shared mem:
    {
        int fd,i;
        people *p_map;
        char temp;
        fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
        lseek(fd,sizeof(people)*5-1,SEEK_SET);
        write(fd,"",1);
        p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if (p_map == (void *)-1)
        {
            fprintf(stderr, "mmap: %s\n", strerror(errno));
            return ;
        }
        close(fd);
        temp='a';
        for(i=0;i<10;i++)
        {
            temp+=1;
            (*(p_map+i)).name[1] = '\0';
            memcpy((*(p_map+i)).name,&temp,1);
            (*(p_map+i)).age=20+i;
        }
        printf("initializeover\n");
        sleep(10);
        munmap(p_map,sizeof(people)*10);
        printf("umapok\n");
    }
     
    /*-------------map_normalfile2.c-----------*/
    #include<sys/mman.h>
    #include<sys/types.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<unistd.h>
    #include <errno.h>
    typedef struct{
        char name[4];
        int age;
    }people;
    void main(int argc,char **argv)//map a normal file as shared mem:
    {
        int fd,i;
        people *p_map;
        fd=open(argv[1],O_CREAT|O_RDWR,00777);
        p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if (p_map == (void *)-1)
        {
            fprintf(stderr, "mmap: %s\n", strerror(errno));
            return ;
        }
        for(i=0;i<10;i++)
        {
            printf("name:%s age%d;\n",(*(p_map+i)).name,(*(p_map+i)).age);
        }
        munmap(p_map,sizeof(people)*10);
    }
    
    

    代码解释: map_normalfile1.c 首先定义了一个 people 数据结构,(在这里采用数据结构的方式是因为,共享内存区的数据往往是有固定格式的,这由通信的各个进程决定,采用结构的方式有普遍代表性)。map_normfile1 首先打开或创建一个文件,并把文件的长度设置为 5 个 people 结构大小。然后从 mmap()的返回地址开始,设置了 10 个 people 结构。然后,进程睡眠 10 秒钟,等待其他进程映射同一个文件,最后解除映射。    map_normfile2.c 只是简单的映射一个文件,并以 people 数据结构的格式从 mmap()返回的地址处读取 10 个 people 结构,并输出读取的值,然后解除映射。   分别把两个程序编译成可执行文件 map_normalfile1 和 map_normalfile2 后,在一个终端上先运行./map_normalfile1 /tmp/test_shm,程序输出结果如下:    initialize over    umap ok   在 map_normalfile1 输出 initialize over 之后,输出 umap ok 之前,在另一个终端上运行 map_normalfile2 /tmp/test_shm,将会产生如下输出(为了节省空间,输出结果为稍作整理后的结果):

      name: b age 20;
      name: c age 21;
      name: d age 22;
      name: e age 23;
      name: f age 24;
      name: g age 25;
      name: h age 26;
      name: I age 27;
      name: j age 28;
      name: k age 29;
    

    在 map_normalfile1 输出 umap ok 后,运行 map_normalfile2 则输出如下结果:

      name: b age 20;
      name: c age 21;
      name: d age 22;
      name: e age 23;
      name: f age 24; 
      name: age 0;
      name: age 0;
      name: age 0;
      name: age 0;
      name: age 0;
    

    ===================以上引用自百度百科 mmap 页面=====================================

    本小白有三个疑问 1 )截断文件之后再使用 lseek 有意义吗?

    fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
        lseek(fd,sizeof(people)*5-1,SEEK_SET);
        write(fd,"",1);
    

    按照我的理解 open 添附 O_TRUNC 参数形成截断后,文件长度不是被截为 0 了吗? 为什么这个时候还可以接着使用 lseek 重新定位光标呢

    2 )还是这句 lseek(fd,sizeof(people)*5-1,SEEK_SET); 其中 sizeof(people)*5-1 那个-1 起什么作用?

    3 ) file1.c 在最后执行 munmap 之后,讲道理此时 file1 映射到内存中的 fd 应该已经被消除了, 但运行 file2 仍然可以得到输出。且输出总是:

        name: b age 20;
        name: c age 21;
        name: d age 22;
        name: e age 23;
        name: f age 24;
        name: g age 25;
        name: h age 26;
        name: I age 27;
        name: j age 28;
        name: k age 29;
        ```
        并不能得到百科中记载的第二种结果(其实根本不明白为什么会得到这个结果
         ```
        name: b age 20;
        name: c age 21;
        name: d age 22;
        name: e age 23;
        name: f age 24; 
        name: age 0;
        name: age 0;
        name: age 0;
        name: age 0;
        name: age 0;
    

    我理解是 file1 munmap 后,file2 再将文件映射进内存读取,但相同内存区域已经被 munmap 了,难道 file2 不是该什么都读取不出来吗?

    问题有点多,但愿都讲清楚了,谢谢大佬解答。

    1 条回复    2019-10-09 23:03:30 +08:00
    b00tyhunt3r
        1
    b00tyhunt3r  
    OP
       2019-10-09 23:03:30 +08:00 via iPhone
    哈哈 系统编程果然在 v2 没市场🤤
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4320 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 01:03 · PVG 09:03 · LAX 18:03 · JFK 21:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.