免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4600 | 回复: 2

[FreeBSD] freebsd9.2-init进程-被调度运行后 [复制链接]

论坛徽章:
0
发表于 2014-06-24 00:54 |显示全部楼层
本帖最后由 71v5 于 2014-06-25 21:59 编辑

1号进程被调度运行后,开始执行fork_trampoline函数:
  1. ******************************* 内核栈顶部 高地址方向
  2. *                             *
  3. *   struct pcb对象            *
  4. *                             *
  5. *                             *
  6. ******************************* <--struct thread的td_pcb成员指向这里
  7. *                             *
  8. *   16bytes for vm            *
  9. ******************************* <--- 当切换进程时,TSS的esp0成员指向这里,内核栈的栈顶                     
  10. *                             *
  11. *                             *
  12. *                             *
  13. *  struct trapframe 对象      * <-陷入内核时,由CPU控制单元和汇编指令等压入,通过td_frame成员访问
  14. *                             *
  15. *******************************  调用fork_trampoline函数时,esp指向这里                       
  16. *    esp                      *   pushl        %esp
  17. *******************************
  18. *    NULL                     *   pushl        %ebx
  19. *******************************                             
  20. *    start_init函数地址       *   pushl        %esi               
  21. *******************************                             
  22. *  fork_exit返回地址          *
  23. ******************************* 执行call  fork_exit时,esp指向这里                             
  24. *                             *
  25. *                             *
  26. *                             *
  27. *                             *
  28. *                             *
  29. *                             *
  30. *                             *
  31. *                             *
  32. *                             *
  33. *                             *
  34. *                             *
  35. *                             *  
  36. *                             *
  37. *                             *
  38. *******************************------ 内核栈底部 低地址方向----





  39. /* 276-279:之间的代码执行完后,内核栈如上所示 */

  40.    275        ENTRY(fork_trampoline)
  41.    276                pushl        %esp                        /* trapframe pointer */
  42.    277                pushl        %ebx                        /* arg1 */
  43.    278                pushl        %esi                        /* function */
  44.    279                call        fork_exit
  45.    280                addl        $12,%esp
  46.    281                /* cut from syscall */
  47.    282       
  48.    283                /*
  49.    284                 * Return via doreti to handle ASTs.
  50.    285                 */
  51.    286                MEXITCOUNT
  52.    287                jmp        doreti
复制代码
fork_exit函数将会调用start_init函数。
假设thread1指向进程1对应的struct thread对象。
假设proc1指向进程1对应的struct proc对象。
  1.    682        /*
  2.    683         * Start the initial user process; try exec'ing each pathname in init_path.
  3.    684         * The program is invoked with one argument containing the boot flags.
  4.    685         */
  5.    686        static void
  6.    687        start_init(void *dummy)
  7.    688        {
  8. /**************************************************************
  9. * addr: 函数vm_map_find在进程地址空间中查找一段特定大小的线
  10.          性地址区间,addr就保存了这段地址区间的起始地址,这段
  11.          线性地址区间座位init进程的用户栈。
  12.    args:传递给execve函数的参数
  13.    options:标识是否传递选项给init程序,形如-cfd等
  14.    692-693:start_init函数定义的局部变量
  15. ******************************/
  16.    689                vm_offset_t addr;
  17.    690                struct execve_args args;
  18.    691                int options, error;
  19.    692                char *var, *path, *next, *s;
  20.    693                char *ucp, **uap, *arg0, *arg1;
  21.    694                struct thread *td;
  22.    695                struct proc *p;
  23.    696       
  24.    697                mtx_lock(&Giant);
  25.    698       
  26.    699                GIANT_REQUIRED;
  27.    700                
  28. /****************************************************
  29. * 701-702:
  30.    因为此时是进程1在运行,所以td指向thread1,p指向
  31.    proc1
  32. *******************************/
  33.    701                td = curthread;
  34.    702                p = td->td_proc;
  35.    703                
  36. /***************************************
  37. * 704:
  38.    函数vfs_mountroot挂载根文件系统
  39. *******************/
  40.    704                vfs_mountroot();
  41.    705       
  42.    706               
  43.    707
  44. /*************************************************************************************
  45. * Need just enough stack to hold the faked-up "execve()" arguments.

  46.    struct vmspace数据对象用来描述进程的地址空间,该对象包含下面的成员:

  47.    
  48.                      
  49.    caddr_t vm_maxsaddr; //vm_maxsaddr以上的地址区间已经被分配
  50.    segsz_t vm_ssize;  //stack size (pages) 当前栈的大小

  51.    因为此时还没有调用execve函数,所以proc1的p_sysent成员从proc0中继承,
  52.    proc0中p_sysent成员被静态初始化为null_sysvec.
  53.    .sv_usrstack        = USRSTACK,即为0xBFC00000
  54.    #define VADDR(pdi, pti) ((vm_offset_t)(((pdi)<<PDRSHIFT)|((pti)<<PAGE_SHIFT)))
  55.    #define VM_MAXUSER_ADDRESS        VADDR(PTDPTDI, 0)
  56.    #define        SHAREDPAGE                (VM_MAXUSER_ADDRESS - PAGE_SIZE)
  57.    #define        USRSTACK                SHAREDPAGE

  58.    709-712:
  59.    addr设置为0xBFC00000 - PAGE_SIZE。
  60.    调用函数vm_map_find在进程1的线性地址区间中获取一段大小为PAGE_SIZE的地址区间,
  61.    这段地址区间的起始地址已经被指定为addr,这个段地址区间用来映射进程的1的用户栈。

  62.    713-714:更新vm_maxsaddr和vm_ssize成员。
  63. *****************************************/
  64.    708                 
  65.    709                addr = p->p_sysent->sv_usrstack - PAGE_SIZE;
  66.    710                if (vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr, PAGE_SIZE,
  67.    711                                FALSE, VM_PROT_ALL, VM_PROT_ALL, 0) != 0)
  68.    712                        panic("init: couldn't allocate argument space");
  69.    713                p->p_vmspace->vm_maxsaddr = (caddr_t)addr;
  70.    714                p->p_vmspace->vm_ssize = 1;
  71.    715                
  72. /***********************************************************************************
  73. * 716-719:
  74.    init_path在/usr/src/sys/kern/init_main.c被定义为下面的数组:                  
  75.    List of paths to try when searching for "init".

  76.    INIT_PATH=/sbin/init:/stand/sysinstall
  77.    
  78.    static char init_path[MAXPATHLEN] =
  79.    #ifdef  INIT_PATH
  80.            __XSTRING(INIT_PATH);
  81.    #else
  82.      "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init:/stand/sysinstall";
  83.    #endif
  84.    init_path标识了用户级初始化程序init所在的目录。

  85.    获取init程序所在的目录,函数首先确定"init_path"环境变量的值
  86.    是否有效,如果有效就使用getenv的返回值。

  87.     所以,init_path或者使用系统静态定义的值,或者使用"init_path"环境变量对应的值.

  88.     这里假设init_path数组。
  89. *************************************************/
  90.    716                if ((var = getenv("init_path")) != NULL) {
  91.    717                        strlcpy(init_path, var, sizeof(init_path));
  92.    718                        freeenv(var);
  93.    719                }
  94.    720               
  95. /******************************************************************************
  96. * 721-796:
  97.    init程序所在的路径是一个字符串,由':'分隔,for循环每次处理其中的一个字符串
  98.    假设init_path指向下面的字符串:
  99.    "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init:/stand/sysinstall"。
  100.    第一次循环:
  101.            path指向"/sbin/init"的起始位置。
  102.            next指向第一个':'字符所在的位置。
  103.    第二次循环:
  104.            path指向next+1的位置,即"/sbin/oinit"的起始位置。
  105.            next指向第二个':'字符所在的位置。
  106.    依次类推。
  107.    722-723:跳过前导字符':'。
  108.    724-725: 如果path为空,就跳出。
  109.    726-727:确定':'的位置,即next将指向字符':'
  110. *********************************************/
  111.    721                for (path = init_path; *path != '\0'; path = next) {
  112.    722                        while (*path == ':')
  113.    723                                path++;
  114.    724                        if (*path == '\0')
  115.    725                                break;
  116.    726                        for (next = path; *next != '\0' && *next != ':'; next++)
  117.    727                                /* nothing */ ;
  118.    728                        if (bootverbose)
  119.    729                                printf("start_init: trying %.*s\n", (int)(next - path),
  120.    730                                    path);
  121.    731                               
  122. /***********************************************************
  123. * Move out the boot flag argument.
  124.    #define RB_SINGLE  0x002  reboot to single user only
  125.    735-765:
  126.    735:options变量标识了进程1是否使用选项参数(形如-cs等)
  127.         ,如果使用选项,就将该变量设置为1
  128.    737:标识选项参数字符串的结束位置
  129.    738-757:
  130.    根据传递给内核的启动参数,确定init程序的选项参数,
  131.    并将这些选项参数压入到进程1的栈顶,即把-cs等选项
  132.    压入到进程1的用户栈栈顶,arg1指向选项的起始位置
  133. ******************************************/
  134.    735                        options = 0;
  135.    736                        ucp = (char *)p->p_sysent->sv_usrstack;
  136.    737                        (void)subyte(--ucp, 0);                /* trailing zero */
  137.    738                        if (boothowto & RB_SINGLE) {
  138.    739                                (void)subyte(--ucp, 's');
  139.    740                                options = 1;
  140.    741                        }
  141.    742        #ifdef notyet
  142.    743                        if (boothowto & RB_FASTBOOT) {
  143.    744                                (void)subyte(--ucp, 'f');
  144.    745                                options = 1;
  145.    746                        }
  146.    747        #endif
  147.    748       
  148.    749        #ifdef BOOTCDROM
  149.    750                        (void)subyte(--ucp, 'C');
  150.    751                        options = 1;
  151.    752        #endif
  152.    753       
  153.    754                        if (options == 0)
  154.    755                                (void)subyte(--ucp, '-');
  155.    756                        (void)subyte(--ucp, '-');                /* leading hyphen */
  156.    757                        arg1 = ucp;
  157.    758       
  158. /****************************************************
  159. * Move out the file name (also arg 0).
  160.    761-765:
  161.    将init程序的全路径名字符串压入进程1的用户栈,即
  162.    "/sbin/init"等等。
  163.     arg0指向路径名的起始位置
  164. *******************/
  165.    761                         
  166.    762                        (void)subyte(--ucp, 0);
  167.    763                        for (s = next - 1; s >= path; s--)
  168.    764                                (void)subyte(--ucp, *s);
  169.    765                        arg0 = ucp;
  170.    766
  171. /**********************************************
  172. * Move out the arg pointers.
  173.    770-773:
  174.    继续操作进程1的用户栈,将'\0',arg1,arg0
  175.    压入进程1的用户栈   
  176. *****************/
  177.    770                        uap = (char **)((intptr_t)ucp & ~(sizeof(intptr_t)-1));
  178.    771                        (void)suword((caddr_t)--uap, (long)0);        /* terminator */
  179.    772                        (void)suword((caddr_t)--uap, (long)(intptr_t)arg1);
  180.    773                        (void)suword((caddr_t)--uap, (long)(intptr_t)arg0);
  181.    774       
  182.    775                       
  183.    776
  184. /**********************************************
  185. * Point at the arguments.
  186.    778-780:
  187.    初始化传递给execve函数的struct execve_args
  188.    对象
  189. ***********************************/
  190.    777                         
  191.    778                        args.fname = arg0;
  192.    779                        args.argv = uap;
  193.    780                        args.envv = NULL;
  194.    781       
  195.    782                       
  196.    783
  197.    784
  198.    785
  199.    786
  200.    787
  201. /************************************************************
  202. * Now try to exec the program.  If can't for any reason
  203. * other than it doesn't exist, complain.
  204. *
  205. * Otherwise, return via fork_trampoline() all the way
  206. * to user mode as init!
  207.    现在尝试调用函数execve,如果执行成功,那么当进程1返回
  208.    到用户态时,将执行init程序,init程序将派生出其他所有
  209.    的用户级进程,比如启动shell等等。
  210.    如果sys_execve执行成功,就不会返回,后续全部工作由init程序
  211.    接管。
  212. ************************/
  213.    788                       
  214.    789                        if ((error = sys_execve(td, &args)) == 0) {
  215.    790                                mtx_unlock(&Giant);
  216.    791                                return;
  217.    792                        }
  218.    793                        if (error != ENOENT)
  219.    794                                printf("exec %.*s: error %d\n", (int)(next - path),
  220.    795                                    path, error);
  221.    796                }
  222.    797                printf("init: not found in path %s\n", init_path);
  223.    798                panic("no init");
  224.    799        }

复制代码

论坛徽章:
3
白羊座
日期:2013-11-05 12:59:14子鼠
日期:2014-01-29 14:14:50戌狗
日期:2014-02-11 16:21:45
发表于 2014-07-05 17:03 |显示全部楼层
这些文章为什么不放到博客上?集中起来容易让大家找。

论坛徽章:
0
发表于 2014-07-05 17:06 |显示全部楼层
回复 2# ahocat


没有玩博客的习惯啊,有机会搬过去   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP