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

これらのレジスタを設定するのではなく、clone が自動的に設定します。関数 (クローンが ip を初期化するために使用する) とスタック (クローンが sp を初期化するために使用する) を提供する必要があります。

関数ポインタは非常に単純ですが (単なる C 関数ポインタです)、スタックはよりトリッキーです。表示する実装では、メモリを割り当て、そのブロックの末尾の下にcloneポインターを提供する必要があります。Linux の clone 呼び出しは似ていますが、少し異なります (ブロックの末尾へのポインターを提供する必要があります)。スタック オーバーフローをキャッチしたい場合は、さらに作業を行う必要があります (おそらく、スタックの下に読み取り/書き込み保護されたガード ページを割り当てます)。PGSIZE

于 2016-11-02T18:13:17.410 に答える