48

Robert Love (ISBN:0-672-32720-1) による「Linux Kernel Development, Second Edition」の第 3 章でcloneシステム コールを使用して Linux でスレッドを作成することを読みました。の構文clone、ルーチン/関数の開始アドレスを渡す必要があるようなものです。

しかし、同じページに、内部的にfork呼び出すことが書かれています。cloneだから私の質問は、によって作成された子プロセスforkが、呼び出し後のコードの一部の実行を開始するfork方法、つまり、開始点として関数を必要としない方法です。

私が提供したリンクに間違った情報が含まれている場合は、より良いリンク/リソースに案内してください。

4

2 に答える 2

87

このような質問については、常にソース コードを読んでください。

glibc のnptl/sysdeps/unix/sysv/linux/fork.c( GitHub ) ( nptl= Linux のネイティブ Posix スレッド) から、 の実装を見つけることができますfork()。これは、syscall ではありません。マクロ内で魔法が発生していることがわかります。マクロは、 in ( GitHub )ARCH_FORKへのインライン呼び出しとして定義されています。 . しかし、待ってください、このバージョンの! それで、ここで何が起こっているのですか?clone()nptl/sysdeps/unix/sysv/linux/x86_64/fork.cclone()

clone()それでは、glibcでの実装を見てみましょう。sysdeps/unix/sysv/linux/x86_64/clone.S( GitHub )にあります。関数ポインタを子のスタックに保存し、clone syscall を呼び出すと、新しいプロセスが関数をスタックから読み込んでポップし、それを呼び出すことがわかります。

したがって、次のように機能します。

clone(void (*fn)(void *), void *stack_pointer)
{
    push fn onto stack_pointer
    syscall_clone()
    if (child) {
        pop fn off of stack
        fn();
        exit();
    }
}

そしてfork()

fork()
{
    ...
    syscall_clone();
    ...
}

概要

実際のclone()syscall は関数の引数を取りません。 のように、戻り点から続行するだけですfork()。したがって、ライブラリ関数clone()fork() ライブラリ関数の両方がシステムコールのラッパーclone()です。

ドキュメンテーション

clone()マニュアルの私のコピーは、ライブラリ関数とシステム コールの両方であるという事実について、いくらか率直です。clone()ただし、セクション 2 とセクション 3 の両方ではなく、セクション 2 にあるのはやや誤解を招くと思います。manページから:

#include <sched.h>

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

/* Prototype for the raw system call */

long clone(unsigned long flags, void *child_stack,
          void *ptid, void *ctid,
          struct pt_regs *regs);

と、

このページでは、glibcclone() ラッパー関数と、それが基づいている基礎となるシステム コールの両方について説明します。本文ではラッパー関数について説明しています。raw システム コールの違いについては、このページの最後で説明します。

ついに、

生のシステム コールは、呼び出しの時点から子での実行が継続するという点で、clone()より密接に対応します。そのため、ラッパー関数fork(2)の fn および arg 引数は省略されます。clone()さらに、引数の順序が変わります。

于 2013-09-19T21:06:00.437 に答える
15

@Dietrich は、実装を見て説明する素晴らしい仕事をしました。すごい!とにかく、それを発見する別の方法があります: strace の "sniffs" 呼び出しを見ることです。

を使用する非常に単純なプログラムを準備し、fork(2)仮説 (つまり、forksyscall が実際に発生していないこと) を確認することができます。

#define WRITE(__fd, __msg) write(__fd, __msg, strlen(__msg))

int main(int argc, char *argv[])
{
  pid_t pid;

  switch (pid = fork()) {
    case -1:
      perror("fork:");
      exit(EXIT_FAILURE);
      break;
    case 0:
      WRITE(STDOUT_FILENO, "Hi, i'm the child");
      exit(EXIT_SUCCESS);
    default:
      WRITE(STDERR_FILENO, "Heey, parent here!");
      exit(EXIT_SUCCESS);
  }

  return EXIT_SUCCESS;
}

次に、そのコード ( ) をコンパイルしてclang -Wall -g fork.c -o fork.outから、次のように実行しstraceます。

strace -Cfo ./fork.strace.log ./fork.out

これにより、プロセスによって呼び出されたシステム コールが傍受され (-f子の呼び出しも傍受されます)、それらの呼び出しが./fork.trace.log;に配置されます。-cオプションを指定すると、最後に要約が表示されます)。私のマシン(Ubuntu 14.04、x86_64 Linux 3.16)での結果は(要約)です:

6915  arch_prctl(ARCH_SET_FS, 0x7fa001a93740) = 0
6915  mprotect(0x7fa00188c000, 16384, PROT_READ) = 0
6915  mprotect(0x600000, 4096, PROT_READ) = 0
6915  mprotect(0x7fa001ab9000, 4096, PROT_READ) = 0
6915  munmap(0x7fa001a96000, 133089)    = 0
6915  clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa001a93a10) = 6916
6915  write(2, "Heey, parent here!", 18) = 18
6916  write(1, "Hi, i'm the child", 17 <unfinished ...>
6915  exit_group(0)                     = ?
6916  <... write resumed> )             = 17
6916  exit_group(0)                     = ?
6915  +++ exited with 0 +++
6916  +++ exited with 0 +++
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 24.58    0.000029           4         7           mmap
 17.80    0.000021           5         4           mprotect
 14.41    0.000017           9         2           write
 11.02    0.000013          13         1           munmap
 11.02    0.000013           4         3         3 access
 10.17    0.000012           6         2           open
  2.54    0.000003           2         2           fstat
  2.54    0.000003           3         1           brk
  1.69    0.000002           2         1           read
  1.69    0.000002           1         2           close
  0.85    0.000001           1         1           clone
  0.85    0.000001           1         1           execve
  0.85    0.000001           1         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000118                    28         3 total

予想通り、fork電話はありません。cloneフラグ、子スタックなどが適切に設定された生のシステムコールだけです。

于 2015-08-30T14:57:44.377 に答える