- 论坛徽章:
- 0
|
本帖最后由 71v5 于 2014-06-25 21:59 编辑
1号进程被调度运行后,开始执行fork_trampoline函数:- ******************************* 内核栈顶部 高地址方向
- * *
- * struct pcb对象 *
- * *
- * *
- ******************************* <--struct thread的td_pcb成员指向这里
- * *
- * 16bytes for vm *
- ******************************* <--- 当切换进程时,TSS的esp0成员指向这里,内核栈的栈顶
- * *
- * *
- * *
- * struct trapframe 对象 * <-陷入内核时,由CPU控制单元和汇编指令等压入,通过td_frame成员访问
- * *
- ******************************* 调用fork_trampoline函数时,esp指向这里
- * esp * pushl %esp
- *******************************
- * NULL * pushl %ebx
- *******************************
- * start_init函数地址 * pushl %esi
- *******************************
- * fork_exit返回地址 *
- ******************************* 执行call fork_exit时,esp指向这里
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- * *
- *******************************------ 内核栈底部 低地址方向----
- /* 276-279:之间的代码执行完后,内核栈如上所示 */
- 275 ENTRY(fork_trampoline)
- 276 pushl %esp /* trapframe pointer */
- 277 pushl %ebx /* arg1 */
- 278 pushl %esi /* function */
- 279 call fork_exit
- 280 addl $12,%esp
- 281 /* cut from syscall */
- 282
- 283 /*
- 284 * Return via doreti to handle ASTs.
- 285 */
- 286 MEXITCOUNT
- 287 jmp doreti
复制代码 fork_exit函数将会调用start_init函数。
假设thread1指向进程1对应的struct thread对象。
假设proc1指向进程1对应的struct proc对象。- 682 /*
- 683 * Start the initial user process; try exec'ing each pathname in init_path.
- 684 * The program is invoked with one argument containing the boot flags.
- 685 */
- 686 static void
- 687 start_init(void *dummy)
- 688 {
- /**************************************************************
- * addr: 函数vm_map_find在进程地址空间中查找一段特定大小的线
- 性地址区间,addr就保存了这段地址区间的起始地址,这段
- 线性地址区间座位init进程的用户栈。
- args:传递给execve函数的参数
- options:标识是否传递选项给init程序,形如-cfd等
- 692-693:start_init函数定义的局部变量
- ******************************/
- 689 vm_offset_t addr;
- 690 struct execve_args args;
- 691 int options, error;
- 692 char *var, *path, *next, *s;
- 693 char *ucp, **uap, *arg0, *arg1;
- 694 struct thread *td;
- 695 struct proc *p;
- 696
- 697 mtx_lock(&Giant);
- 698
- 699 GIANT_REQUIRED;
- 700
- /****************************************************
- * 701-702:
- 因为此时是进程1在运行,所以td指向thread1,p指向
- proc1
- *******************************/
- 701 td = curthread;
- 702 p = td->td_proc;
- 703
- /***************************************
- * 704:
- 函数vfs_mountroot挂载根文件系统
- *******************/
- 704 vfs_mountroot();
- 705
- 706
- 707
- /*************************************************************************************
- * Need just enough stack to hold the faked-up "execve()" arguments.
- struct vmspace数据对象用来描述进程的地址空间,该对象包含下面的成员:
-
-
- caddr_t vm_maxsaddr; //vm_maxsaddr以上的地址区间已经被分配
- segsz_t vm_ssize; //stack size (pages) 当前栈的大小
- 因为此时还没有调用execve函数,所以proc1的p_sysent成员从proc0中继承,
- proc0中p_sysent成员被静态初始化为null_sysvec.
- .sv_usrstack = USRSTACK,即为0xBFC00000
- #define VADDR(pdi, pti) ((vm_offset_t)(((pdi)<<PDRSHIFT)|((pti)<<PAGE_SHIFT)))
- #define VM_MAXUSER_ADDRESS VADDR(PTDPTDI, 0)
- #define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE)
- #define USRSTACK SHAREDPAGE
- 709-712:
- addr设置为0xBFC00000 - PAGE_SIZE。
- 调用函数vm_map_find在进程1的线性地址区间中获取一段大小为PAGE_SIZE的地址区间,
- 这段地址区间的起始地址已经被指定为addr,这个段地址区间用来映射进程的1的用户栈。
- 713-714:更新vm_maxsaddr和vm_ssize成员。
- *****************************************/
- 708
- 709 addr = p->p_sysent->sv_usrstack - PAGE_SIZE;
- 710 if (vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr, PAGE_SIZE,
- 711 FALSE, VM_PROT_ALL, VM_PROT_ALL, 0) != 0)
- 712 panic("init: couldn't allocate argument space");
- 713 p->p_vmspace->vm_maxsaddr = (caddr_t)addr;
- 714 p->p_vmspace->vm_ssize = 1;
- 715
- /***********************************************************************************
- * 716-719:
- init_path在/usr/src/sys/kern/init_main.c被定义为下面的数组:
- List of paths to try when searching for "init".
- INIT_PATH=/sbin/init:/stand/sysinstall
-
- static char init_path[MAXPATHLEN] =
- #ifdef INIT_PATH
- __XSTRING(INIT_PATH);
- #else
- "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init:/stand/sysinstall";
- #endif
- init_path标识了用户级初始化程序init所在的目录。
- 获取init程序所在的目录,函数首先确定"init_path"环境变量的值
- 是否有效,如果有效就使用getenv的返回值。
- 所以,init_path或者使用系统静态定义的值,或者使用"init_path"环境变量对应的值.
- 这里假设init_path数组。
- *************************************************/
- 716 if ((var = getenv("init_path")) != NULL) {
- 717 strlcpy(init_path, var, sizeof(init_path));
- 718 freeenv(var);
- 719 }
- 720
- /******************************************************************************
- * 721-796:
- init程序所在的路径是一个字符串,由':'分隔,for循环每次处理其中的一个字符串
- 假设init_path指向下面的字符串:
- "/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init:/stand/sysinstall"。
- 第一次循环:
- path指向"/sbin/init"的起始位置。
- next指向第一个':'字符所在的位置。
- 第二次循环:
- path指向next+1的位置,即"/sbin/oinit"的起始位置。
- next指向第二个':'字符所在的位置。
- 依次类推。
- 722-723:跳过前导字符':'。
- 724-725: 如果path为空,就跳出。
- 726-727:确定':'的位置,即next将指向字符':'
- *********************************************/
- 721 for (path = init_path; *path != '\0'; path = next) {
- 722 while (*path == ':')
- 723 path++;
- 724 if (*path == '\0')
- 725 break;
- 726 for (next = path; *next != '\0' && *next != ':'; next++)
- 727 /* nothing */ ;
- 728 if (bootverbose)
- 729 printf("start_init: trying %.*s\n", (int)(next - path),
- 730 path);
- 731
- /***********************************************************
- * Move out the boot flag argument.
- #define RB_SINGLE 0x002 reboot to single user only
- 735-765:
- 735:options变量标识了进程1是否使用选项参数(形如-cs等)
- ,如果使用选项,就将该变量设置为1
- 737:标识选项参数字符串的结束位置
- 738-757:
- 根据传递给内核的启动参数,确定init程序的选项参数,
- 并将这些选项参数压入到进程1的栈顶,即把-cs等选项
- 压入到进程1的用户栈栈顶,arg1指向选项的起始位置
- ******************************************/
- 735 options = 0;
- 736 ucp = (char *)p->p_sysent->sv_usrstack;
- 737 (void)subyte(--ucp, 0); /* trailing zero */
- 738 if (boothowto & RB_SINGLE) {
- 739 (void)subyte(--ucp, 's');
- 740 options = 1;
- 741 }
- 742 #ifdef notyet
- 743 if (boothowto & RB_FASTBOOT) {
- 744 (void)subyte(--ucp, 'f');
- 745 options = 1;
- 746 }
- 747 #endif
- 748
- 749 #ifdef BOOTCDROM
- 750 (void)subyte(--ucp, 'C');
- 751 options = 1;
- 752 #endif
- 753
- 754 if (options == 0)
- 755 (void)subyte(--ucp, '-');
- 756 (void)subyte(--ucp, '-'); /* leading hyphen */
- 757 arg1 = ucp;
- 758
- /****************************************************
- * Move out the file name (also arg 0).
- 761-765:
- 将init程序的全路径名字符串压入进程1的用户栈,即
- "/sbin/init"等等。
- arg0指向路径名的起始位置
- *******************/
- 761
- 762 (void)subyte(--ucp, 0);
- 763 for (s = next - 1; s >= path; s--)
- 764 (void)subyte(--ucp, *s);
- 765 arg0 = ucp;
- 766
- /**********************************************
- * Move out the arg pointers.
- 770-773:
- 继续操作进程1的用户栈,将'\0',arg1,arg0
- 压入进程1的用户栈
- *****************/
- 770 uap = (char **)((intptr_t)ucp & ~(sizeof(intptr_t)-1));
- 771 (void)suword((caddr_t)--uap, (long)0); /* terminator */
- 772 (void)suword((caddr_t)--uap, (long)(intptr_t)arg1);
- 773 (void)suword((caddr_t)--uap, (long)(intptr_t)arg0);
- 774
- 775
- 776
- /**********************************************
- * Point at the arguments.
- 778-780:
- 初始化传递给execve函数的struct execve_args
- 对象
- ***********************************/
- 777
- 778 args.fname = arg0;
- 779 args.argv = uap;
- 780 args.envv = NULL;
- 781
- 782
- 783
- 784
- 785
- 786
- 787
- /************************************************************
- * Now try to exec the program. If can't for any reason
- * other than it doesn't exist, complain.
- *
- * Otherwise, return via fork_trampoline() all the way
- * to user mode as init!
- 现在尝试调用函数execve,如果执行成功,那么当进程1返回
- 到用户态时,将执行init程序,init程序将派生出其他所有
- 的用户级进程,比如启动shell等等。
- 如果sys_execve执行成功,就不会返回,后续全部工作由init程序
- 接管。
- ************************/
- 788
- 789 if ((error = sys_execve(td, &args)) == 0) {
- 790 mtx_unlock(&Giant);
- 791 return;
- 792 }
- 793 if (error != ENOENT)
- 794 printf("exec %.*s: error %d\n", (int)(next - path),
- 795 path, error);
- 796 }
- 797 printf("init: not found in path %s\n", init_path);
- 798 panic("no init");
- 799 }
复制代码 |
|