1

我想通过使用系统调用“clone()”在 xv6 中创建一个线程,但是我对堆栈创建感到困惑,因为如果我想创建一个线程,我需要创建相应的寄存器指针,例如 ebp、esp , eip。但我不知道如何设置这些寄存器指针的值。

这是xv6中clone()的代码,不知道为什么要这样设置寄存器指针的值......

int clone(void(*fcn)(void*), void *arg, void*stack){

  int i, pid;
  struct proc *np;
  int *ustack = stack + PGSIZE - sizeof(void*);
  //allocate process.
  if((np=allocproc()) == 0)
    return -1;

  //copy process state from p
  np->pgdir = proc->pgdir;
  np->sz = proc->sz;
  np->parent = 0;
  np->pthread = proc;
  *np->tf = *proc->tf;
  np->ustack = stack;

  //initialize stack variables 
  //void *stackArg, *stackRet;
  //stackRet = stack + PGSIZE -2*sizeof(void*);
  //*(uint *)stackRet = 0xffffffff;

  //stackArg = stack + PGSIZE -sizeof(void*);
  //*(uint *)stackArg = (uint)arg;
  *ustack = (int) arg;
  *(ustack - 1) = 0xffffffff;
  *(ustack - 2) = 0xffffffff;


  //Set stack pinter register
  np->tf->eax = 0;
  np->tf->esp = (int) ustack - sizeof(void*);
  np->tf->ebp = np->tf->esp;
  np->tf->eip = (int)fcn;

  for(i = 0; i < NOFILE; i++) {
    if(proc->ofile[i])
      np->ofile[i] = filedup(proc->ofile[i]);
  }

  np->cwd = idup(proc->cwd);
  np->state = RUNNABLE;
  safestrcpy(np->name, proc->name, sizeof(proc->name));
  pid = np->pid;
  return pid;

}
4

2 回答 2

1

在您设置的所有寄存器值中,唯一有用的是:

eip - 告诉线程返回用户空间时从哪里开始执行

esp - 这是指向堆栈的顶部。这意味着如果你做对了,存储在堆栈顶部的 4 个字节应该包含你的返回地址


eax在这里并不是很有用,因为线程跳转到一个新的上下文,而不是创建它的那个。否则,eax将存储最后一次系统调用的返回值。fork如果您仍然对此感到困惑,请参阅实现。

ebp不是由您操作,而是由 x86 函数调用约定操作,通常在调用函数时设置为esp的值。因此,您通常会在大多数函数调用的反汇编中看到这种情况

push ebp      ; Preserve current frame pointer
mov ebp, esp  ; Create new frame pointer pointing to current stack top

ebp对于堆栈跟踪也很有用,因为它在将前一个函数的堆栈顶部更改为指向当前堆栈顶部之前存储它


你不需要这个*(ustack - 2) = 0xffffffff;

于 2016-11-26T05:06:31.730 回答
1

您无需设置这些寄存器——克隆为您设置它们。您需要提供一个函数(克隆用于初始化 ip)和堆栈(克隆用于初始化 sp)。

函数指针非常简单(它只是一个 C 函数指针),但堆栈更棘手。对于clone您展示的实现,您需要分配一些内存并在该块的末尾PGSIZE下方提供一个指针。Linux 的克隆调用类似,但略有不同(您需要提供指向块末尾的指针)。如果要捕获堆栈溢出,则需要做更多工作(可能在堆栈下方分配一个读/写保护的保护页)。

于 2016-11-02T18:13:17.410 回答