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

2019-10-09 18:54:15 +08:00
 b00tyhunt3r

百度 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 不是该什么都读取不出来吗?

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

1362 次点击
所在节点    程序员
1 条回复
b00tyhunt3r
2019-10-09 23:03:30 +08:00
哈哈 系统编程果然在 v2 没市场🤤

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/607573

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX