免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 23625 | 回复: 25
打印 上一主题 下一主题

Linux sys_exec中可执行文件映射的建立及读取 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-05-06 15:38 |只看该作者 |倒序浏览


   1. 创建一个vm_area_struct;
   2. 圈定一个虚用户空间,将其起始结束地址(elf段中已设置好)保存到vm_start和vm_end中;
   3. 将磁盘file句柄保存在vm_file中;
   4. 将对应段在磁盘file中的偏移值(elf段中已设置好)保存在vm_pgoff中;
   5. 将操作该磁盘file的磁盘操作函数保存在vm_ops中;
   6. 注意这里没有为对应的页目录表项创建页表,更不存在设置页表项了;

                          §                               §
                          §                        +------§->+--------------+
                          §                        |      §  |  Disk file   |
                          §                        |      §  |              |
                          §    +----------------+  |  +---§->|--------------|
                          §    | vm_area_struct |  |  |   §  | Seg Content  |
                          §    |----------------|  |  |   §  |--------------|
      +----------------+<-§-------- vm_start    |  |  |   §  |              |
      | 圈定了一个未映  |  §  +----- vm_end      |  |  |   §  |              |
      | 射到物理内存的  |  §  | |    vm_file--------+  |   §  +--------------+
      | vm_area_struct |  §  | |    vm_pgoff ---------+   §
      +----------------+<-§--+ |    vm_ops --------+      §
                          §    |                |  |      §
                          §    +----------------+  |      §
                          §                        |      §
                          § +----------------------+      §
                          § |                             §
                          § +->+-----------------------+  §
                          §    |   file_private_map    |  §
                          §    |-----------------------|  §
                          §    | nopage:filemap_nopage |  §
                          §    |        .....          |  §
                          §    +-----------------------+  §
            user space    §           kernel              §     disk


[ 本帖最后由 frank_seng 于 2008-5-6 15:45 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2008-05-06 15:44 |只看该作者

回复 #1 frank_seng 的帖子

从上文中可知elf_map时并没有将文件内容读入内存,假设现在程序中有一条指令需要读取上面vm_start---vm_end之间的某内容,例如mov [0x08000011], %eax,那么将会执行如下序列:

    * CPU依据CR3(current->pgd)找到0x08000011地址对应的pgd[ i ],由于该pgd[ i ]内容保持为初始化状态即为0,导致CPU异常;
    * do_page_fault被调用,在该函数中,为pgd[ i]在内存中分配一个页表,并让该表项指向它,如下图所示:

      pgd
      +-----+
      |-----|    pt
      |  i  |--->+-----+
      |-----|    |-----|
      |     |    |  j  |
      +-----+    |-----|
                 |     |
                 +-----+

      注意:这里i为0x08000011高10位,j为其中间10位,此时pt表项全部为0(pte[j]也为0);
    * 为pte[j]分配一个真正的物理内存页面,依据vm_area_struct中的vm_file、vm_pgoff和vm_ops,调用filemap_nopage将磁盘file中vm_pgoff偏移处的内容读入到该物理页面中,如下图所示:

                          +-------------+   
                          |  Disk file  |   
                          |             |   
                          |-------------|   
                          | Seg Content----+  
                          |-------------|  |  
      pgd                 |             |  |  
      +-----+             |             |  |②
      |-----|    pt       +-------------+  |  
      |  i  |--->+-----+                   |  
      |-----|    |-----|   ①   page       |  
      |     |    |  j  |------->+-----+<---+  
      +-----+    |-----|        |     |
                 |     |        |     |
                 +-----+        |     |
                                +-----+      
      ①.分配物理内存页面;
      ②.从磁盘文件中将内容读取到物理内存页面中;

论坛徽章:
0
3 [报告]
发表于 2008-05-07 03:31 |只看该作者
关注

论坛徽章:
0
4 [报告]
发表于 2008-05-07 11:43 |只看该作者
同关注,怎么就没了?
PS:LZ的图画的很好,用什么做的啊?

论坛徽章:
0
5 [报告]
发表于 2008-05-07 11:48 |只看该作者

回复 #1 frank_seng 的帖子

说的不错啊,关注!!

论坛徽章:
0
6 [报告]
发表于 2008-05-07 13:45 |只看该作者

回复 #2 frank_seng 的帖子

父子页面保护共享的处理 ------ COW技术

在do_fork->copy_mm中,如果vm_area_struct的属性中包含了可写属性,但非共享,则将父对应的pte[j](假设pte[j]对应了vm_area_strruct圈定的范围中的某个页面)设置为写保护,随后复制父pte[j]给子pte[j];此处采用了COW技术。

copy_mm之后的的情况如下图,可见并没有真正的复制一个page给子进程:

    father
    +--------+
    |        |
    |--------|
    |pte(w=0)|--+
    |--------|  |
    |        |  |
    +--------+  +-->+------+
                    |      |
    son         +-->|      |
    +--------+  |   | page |
    |        |  |   |      |
    |--------|  |   |      |
    |pte(w=0)|--+   |      |
    |--------|      +------+
    |        |
    +--------+

现在假设父进程向pte中写入,则会引发CPU异常,在异常处理机制中,处理如下:

    * 创建一个新的newpage;
    * 复制page内容到newpage;
    * 让父pte指向newpage,并且设置父pte的w=1(可写);
    * 子pte保持不变;最终如下图:

    father                  

    +--------+  +-->+------+
    |        |  |   |      |
    |--------|  |   | new  |
    |pte(w=1)|--+   | page |
    |--------|      |      |
    |        |      +------+
    +--------+

    son                     
    +--------+  +-->+------+
    |        |  |   |      |
    |--------|  |   | page |
    |pte(w=0)|--+   |      |
    |--------|      |      |
    |        |      +------+
    +--------+

如果现在子进程又向pte中写,同样导致异常,但是由于此时该pte:count==1,则直接将该pte:w=1,然后写即可;
    son                     
    +--------+  +-->+------+
    |        |  |   |      |
    |--------|  |   | page |
    |pte(w=1)|--+   |      |
    |--------|      |      |
    |        |      +------+
    +--------+


[ 本帖最后由 frank_seng 于 2008-5-7 14:02 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2008-05-07 14:05 |只看该作者
原帖由 frank_seng 于 2008-5-7 13:45 发表
父子页面保护共享的处理 ------ COW技术

在do_fork->copy_mm中,如果vm_area_struct的属性中包含了可写属性,但非共享,则将父对应的pte[j](假设pte[j]对应了vm_area_strruct圈定的范围中的某个页面)设置为写 ...

这个地方是什么意思?
COW怎么变成父子都不可写了呢?
我记得此时应该是:父vm_area、pte都是可写;子vm_area可写,pte不可写。
然后子写的时候vm_area和pte不一致断定为COW,重开新页。

论坛徽章:
0
8 [报告]
发表于 2008-05-07 14:12 |只看该作者
原帖由 zx_wing 于 2008-5-7 14:05 发表

这个地方是什么意思?
COW怎么变成父子都不可写了呢?
我记得此时应该是:父vm_area、pte都是可写;子vm_area可写,pte不可写。
然后子写的时候vm_area和pte不一致断定为COW,重开新页。


copy_mm->copy_page_range...
if (cow) {
   ptep_set_wrprotect(src_pte);
   pte = *src_pte;
}

可见父子pte都是均不可写!

论坛徽章:
0
9 [报告]
发表于 2008-05-07 14:48 |只看该作者
原帖由 frank_seng 于 2008-5-7 14:12 发表


copy_mm->copy_page_range...
if (cow) {
   ptep_set_wrprotect(src_pte);
   pte = *src_pte;
}

可见父子pte都是均不可写!

汗,我还真没找到代码在哪儿,不好意思,内存方面实在不懂,太菜了。
我是2.6.25内核,copy_mm如下

  1. static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
  2. {
  3.         struct mm_struct * mm, *oldmm;
  4.         int retval;

  5.         tsk->min_flt = tsk->maj_flt = 0;
  6.         tsk->nvcsw = tsk->nivcsw = 0;

  7.         tsk->mm = NULL;
  8.         tsk->active_mm = NULL;

  9.         /*
  10.          * Are we cloning a kernel thread?
  11.          *
  12.          * We need to steal a active VM for that..
  13.          */
  14.         oldmm = current->mm;
  15.         if (!oldmm)
  16.                 return 0;

  17.         if (clone_flags & CLONE_VM) {
  18.                 atomic_inc(&oldmm->mm_users);
  19.                 mm = oldmm;
  20.                 goto good_mm;
  21.         }

  22.         retval = -ENOMEM;
  23.         mm = dup_mm(tsk);
  24.         if (!mm)
  25.                 goto fail_nomem;

  26. good_mm:
  27.         /* Initializing for Swap token stuff */
  28.         mm->token_priority = 0;
  29.         mm->last_interval = 0;

  30.         tsk->mm = mm;
  31.         tsk->active_mm = mm;
  32.         return 0;

  33. fail_nomem:
  34.         return retval;
  35. }
复制代码

麻烦LZ把代码的路径讲一下,我好结合上下文看一下。
如果父子都不可写,父写的时候分配一个新页面给父,那老页面是不是就给子了呢?如果子先写,分配一个新页面给子,老页面是不是就给父了呢?

论坛徽章:
0
10 [报告]
发表于 2008-05-07 14:57 |只看该作者
原帖由 zx_wing 于 2008-5-7 14:48 发表

汗,我还真没找到代码在哪儿,不好意思,内存方面实在不懂,太菜了。
我是2.6.25内核,copy_mm如下

static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
        struct mm_struct *  ...


真的好对不起,由于历史原因,我们的产品依然跑在Linux 2.4上,因此是对着2.4说的,2.6除了网络部分外,还从没看过,sorry啊!

关于COW,我写了一些总结性文档,贴出来献丑了:假设进程A创建了子进程B,之后进程A和进程B共享A的地址空间,同时该地址空间中的页面全部被标识为写保护。此时B若写address所在的页面,由于写保护的原因会引起写异常,在异常处理中,内核会将address所在的那个写保护页面复制为一个新的页面,让B的address页表项指向该新页面,新页面可写,而A的address页表项依然指向那个写保护的页面。此后当B再访问address是就会直接访问该新的页面了,不再会访问到那个写保护的页面。当A试图写address所在的页面时,由于写保护的原因此时也会引起异常,在异常处理中,内核如果发现该页面只有一个拥有进程,此种情况下也就是A,则直接对该页面取消写保护,此后当A再访问address是不会再有写保护错误了。如果此时A又创建了子进程C,则该address所在的页面又被设置为写保护,拥有进程为A和C,同时其他的页面例如PAGEX依然维持写保护,只是拥有进程为A、B和C。如果此时A访问PAGEX,则异常处理会创建一个新页面并将PAGEX中的内容复制到该页面,同时将A相应的pte指向该新页面。如果此时C也访问PAGEX,也会复制新页面并且让C对应的pte指向新页面。如果B再访问PAGEX,则由于此时PAGEX只有一个拥有进程B,故不再复制新页面,而是直接取消该页面的写保护,由于B的pte本来就直接指向该页面,所以无需再做其他工作了。这也就是COW的实现机制。

[ 本帖最后由 frank_seng 于 2008-5-7 14:59 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP