xv6 exec 函数

解析 xv6 exec 函数过程,理解内核中 exec 是如何实现的。

下边是从源代码中拷贝出来的,仅保留关键逻辑的代码。

 13 exec(char *path, char **argv)
 14 {
 17   uint64 argc, sz = 0, sp, ustack[MAXARG+1], stackbase;

 21   pagetable_t pagetable = 0, kpagetable = 0, oldpagetable, oldkpagetable;
 22   struct proc *p = myproc();
 24 
 25   if((ip = namei(path)) == 0){
 26     end_op();
 27     return -1;
 28   }
 30 
 31   // Check ELF header
 32   if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))
 33     goto bad;
 34   if(elf.magic != ELF_MAGIC)
 35     goto bad;
 36 
 37   if((kpagetable = proc_kpagetable(p)) == 0)
 38     goto bad;

 43   for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
 44     if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
 53     if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
 54       goto bad;
 58     if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)
 59       goto bad;
 60   }
 64 
 65   p = myproc();
 66   uint64 oldsz = p->sz;
 67 
 68   // Allocate two pages at the next page boundary.
 69   // Use the second as the user stack.
 70   sz = PGROUNDUP(sz);
 71   uint64 sz1;
 72   if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0)
 73     goto bad;
 74   sz = sz1;
 75   uvmclear(pagetable, sz-2*PGSIZE);
 77   sp = sz;
 78   stackbase = sp - PGSIZE;
 79 
 80   // Push argument strings, prepare rest of stack in ustack.
 81   for(argc = 0; argv[argc]; argc++) {
 82     if(argc >= MAXARG)
 83       goto bad;
 84     sp -= strlen(argv[argc]) + 1;
 85     sp -= sp % 16; // riscv sp must be 16-byte aligned
 86     if(sp < stackbase)
 87       goto bad;
 88     if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
 89       goto bad;
 90     ustack[argc] = sp;
 91   }
 92   ustack[argc] = 0;
 93 
 94   // push the array of argv[] pointers.
 95   sp -= (argc+1) * sizeof(uint64);
 96   sp -= sp % 16;
 97   if(sp < stackbase)
 98     goto bad;
 99   if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
100     goto bad;
101 
102   // arguments to user main(argc, argv)
103   // argc is returned via the system call return
104   // value, which goes in a0.
105   p->trapframe->a1 = sp;

115   p->pagetable = pagetable;
117   p->sz = sz;
118   p->trapframe->epc = elf.entry;  // initial program counter = main
119   p->trapframe->sp = sp; // initial stack pointer
120   proc_freepagetable(oldpagetable, oldsz);
127   return argc; // this ends up in a0, the first argument to main(argc, argv)
129  bad:
138   return -1;
139 }

exec 和 fork 函数不同,exec 的函数原型是 exec(BIN, argv...)。exec 并不额外创建进程,
而是在当前进程中直接执行对应的命令和函数。那么就要替换进程的内容。其中最终要的,就是替换新的地址空间及内容。

首先读取可执行文件的在文件系统上的内容,做一些合法校验。ip(inode path)。

 25   if((ip = namei(path)) == 0){
 26     end_op();
 27     return -1;
 28   }

然后创建一个新的地址空间,实际就是创建新的页表

 40   if((pagetable = proc_pagetable(p)) == 0)
 41     goto bad;

根据用户地址空间的布局,这里需要初始化三种,一种是二进制自带的指令和数据段,统称一下 text,用户运行的栈 stack,还有一块跳转代码 trampoline。其中 trampoline 已经由 proc_pagetable 初始完成,置于虚拟地址空间最高处,分配一页内存。接下来就是完成二进制数据文件的布局 text。

这里先进行页表关联,然后将数据加载到对应的物理内存中。

 43   for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
 44     if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
 53     if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
 54       goto bad;
 58     if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)
 59       goto bad;
 60   }

接下来分配栈空间,分配栈空间细分三个步骤,首先需要分配对应的物理内存和进行页表关联。
这里栈地址首先进行页对齐,然后分配两个物理页,将高地址的那一个页作为栈,低地址那一个干什么?保护页吗,还是扩展页呢?

 70   sz = PGROUNDUP(sz);
 71   uint64 sz1;
 72   if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0)
 73     goto bad;
 74   sz = sz1;
 75   uvmclear(pagetable, sz-2*PGSIZE);
 77   sp = sz;
 78   stackbase = sp - PGSIZE;

从栈的高地址开始,依次放置 0~argc 的参数值。每一个值都是 16 字节对齐的,这里可以用 sp & 16 进行计算。

 80   // Push argument strings, prepare rest of stack in ustack.
 81   for(argc = 0; argv[argc]; argc++) {
 82     if(argc >= MAXARG)
 83       goto bad;
 84     sp -= strlen(argv[argc]) + 1;
 85     sp -= sp % 16; // riscv sp must be 16-byte aligned
 86     if(sp < stackbase)
 87       goto bad;
 88     if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
 89       goto bad;
 90     ustack[argc] = sp;
 91   }
 92   ustack[argc] = 0;

然后再将这些参数地址作为一个数组,放在接下来地址对齐的地址上。这里会多4个空字节。不清楚为什么。

 95   sp -= (argc+1) * sizeof(uint64);
 96   sp -= sp % 16;
 97   if(sp < stackbase)
 98     goto bad;
 99   if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
100     goto bad;

至此,新的页表和地址空间分配完毕,进行切换即可。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,014评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,832评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,062评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,620评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,636评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,148评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,522评论 3 427
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,501评论 0 278
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,032评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,069评论 3 344
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,211评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,848评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,541评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,013评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,136评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,712评论 3 380
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,240评论 2 363

推荐阅读更多精彩内容

  • 有3类事件可导致CPU把普通的指令执行搁置在一边,强制把控制权转移到能处理事件的特定代码处。 系统调用用户程序执行...
    橡树人阅读 3,760评论 0 3
  • 地址空间 分页硬件 xv6的VM代码 虚拟内存概述 问题:假设shell程序有一个bug:有时,它会向一个随机的内...
    橡树人阅读 453评论 0 1
  • 操作系统的一个关键要求是要一次支持多个活动。比如,使用系统调用fork,一个进程可以启动新进程。 操作系统必须在这...
    橡树人阅读 600评论 0 1
  • linux库函数mmap()原理 目录 1.mmap基本概念 2.mmap内存映射原理 3.mmap和常规文件操作...
    小乌龟爸阅读 931评论 0 3
  • 操作系统的物理内存: —— 常说的 “内存条”数据从磁盘中加载到内存后,才能被CPU访问。【操作系统的代码和数据...
    helinyu阅读 950评论 0 1