• Linux 内核fork()函数创建进程 (续)之copy_mem(int nr, struct task_struct *p)


    本问分析基于Linux 0.11内核,转载请标明出处http://blog.csdn.net/yming0221/archive/2011/06/06/6528490.aspx

    copy_mem(int nr, struct task_struct *p)函数是为进程设置段基址,限长,并复制页表。下面是其代码

    1. // 设置新任务的代码和数据段基址、限长并复制页表。   
    2. // nr 为新任务号;p 是新任务数据结构的指针。   
    3. int copy_mem (int nr, struct task_struct *p)  
    4. {  
    5.   unsigned long old_data_base, new_data_base, data_limit;  
    6.   unsigned long old_code_base, new_code_base, code_limit;  
    7.   code_limit = get_limit (0x0f);    // 取局部描述符表中代码段描述符项中段限长。   
    8.   data_limit = get_limit (0x17);    // 取局部描述符表中数据段描述符项中段限长。   
    9.   old_code_base = get_base (current->ldt[1]);    // 取原代码段基址。   
    10.   old_data_base = get_base (current->ldt[2]);    // 取原数据段基址。   
    11.   if (old_data_base != old_code_base)   // 0.11 版不支持代码和数据段分立的情况。   
    12.     panic ("We don't support separate I&D");  
    13.   if (data_limit < code_limit)   // 如果数据段长度 < 代码段长度也不对。   
    14.     panic ("Bad data_limit");  
    15.   new_data_base = new_code_base = nr * 0x4000000;   // 新基址=任务号*64Mb(任务大小)。   
    16.   p->start_code = new_code_base;  
    17.   set_base (p->ldt[1], new_code_base);   // 设置代码段描述符中基址域。   
    18.   set_base (p->ldt[2], new_data_base);   // 设置数据段描述符中基址域。   
    19.   if (copy_page_tables (old_data_base, new_data_base, data_limit))  
    20.     {               // 复制代码和数据段。   
    21.       free_page_tables (new_data_base, data_limit); // 如果出错则释放申请的内存。   
    22.       return -ENOMEM;  
    23.     }  
    24.   return 0;  
    25. }  
     

    其中get_limit()函数是利用内嵌汇编取特定段描述符中段限长,其中用到指令lsll

    1. // 取段选择符segment 的段长值。   
    2. // %0 - 存放段长值(字节数);%1 - 段选择符segment。   
    3. #define get_limit(segment) ({ /   
    4. unsigned long __limit; /  
    5. __asm__( "lsll %1,%0/n/tincl %0""=r" (__limit): "r" (segment)); /  
    6. __limit;})  
     

    将指定描述符段的段限长返回,其中由于段限长是从0开始,所以在lsll之后需要增一。

    至于ldt数据段描述符为什么是0x17,而ldt中代码段描述符是0x0f原因是段选择子的格式,一共16位,高13位表示描述符在描述符表的索引

    [2]位表示这项是GDT还是LDT,0表示LDT;[1][0]表示RPL权限位。所以,0x17=0B0000 0000 0001 0111,其中10表示第二

    项,0x0f=0B0000 0000 0000 1111,表示位于描述符表中的第一项。基地址在LDTR寄存器中。

    get_base(addr)取描述符的中指向段的及地址,其宏定义如下:

    1. // 取局部描述符表中ldt 所指段描述符中的基地址。   
    2. #define get_base(ldt) _get_base( ((char *)&(ldt)) )   
    3. // 从地址addr 处描述符中取段基地址。功能与_set_base()正好相反。   
    4. // edx - 存放基地址(__base);%1 - 地址addr 偏移2;%2 - 地址addr 偏移4;%3 - addr 偏移7。   
    5. #define _get_base(addr) ({/   
    6. unsigned long __base; /  
    7. __asm__( "movb %3,%%dh/n/t" /   // 取[addr+7]处基址高16 位的高8 位(位31-24)??dh。   
    8.   "movb %2,%%dl/n/t" /      // 取[addr+4]处基址高16 位的低8 位(位23-16)??dl。   
    9.   "shll $16,%%edx/n/t" /    // 基地址高16 位移到edx 中高16 位处。   
    10.   "movw %1,%%dx" /      // 取[addr+2]处基址低16 位(位15-0)??dx。   
    11. :"=d" (__base) /        // 从而edx 中含有32 位的段基地址。   
    12. :"m" (*((addr) + 2)), "m" (*((addr) + 4)), "m" (*((addr) + 7)));  
    13. __base;  
    14. }  
    15. )  
     

    下图表示描述符格式:

    copy_page_tables()函数复制页表,据说是内存管理中最复杂函数之一,以后研究,待续........

  • 相关阅读:
    极光推送SDK通过泰尔终端实验室检测,符合统一推送接口标准
    极光小课堂|手把手教你做接口测试
    一键登录怎么在iOS端实现?这篇文章教会你!
    一键登录已成大势所趋,Android端操作指南来啦!
    极光一键登录:更快捷、安全的登录认证方式,简单集成即可实现
    跨浏览器问题的五种解决方案
    Laravel 搭建 Composer 包,实现配置 Config、门面 Facade、服务 Service、发布到 Packagist
    How to Install ClamAV on Ubuntu 20.04 and Scan for Vulnerabilities
    单点登录(SSO)看这一篇就够了
    一口气说出 OAuth2.0 的四种授权方式
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332883.html
Copyright © 2020-2023  润新知