web-dev-qa-db-ja.com

2.6カーネルLinuxでのフォークとクローンの比較

フォークとクローンに関して混乱しています。私はそれを見ました:

  • forkはプロセス用で、cloneはスレッド用です

  • forkはcloneを呼び出すだけで、cloneはすべてのプロセスとスレッドに使用されます

これらのいずれかは正確ですか? 2.6 Linuxカーネルでのこれら2つのシステムコールの違いは何ですか?

37
Gregg Leventhal

fork()は、元のUNIXシステムコールでした。スレッドではなく、新しいプロセスの作成にのみ使用できます。また、ポータブルです。

Linuxでは、clone()は新しい多目的システムコールであり、新しい実行スレッドを作成するために使用できます。渡されたオプションに応じて、新しい実行スレッドは、UNIXプロセスのセマンティクス、POSIXスレッド、その中間、またはまったく異なるもの(異なるコンテナーのような)に準拠できます。メモリ、ファイル記述子、さまざまな名前空間、シグナルハンドラーなどを共有するかコピーするかを指定するあらゆる種類のオプションを指定できます。

clone()はスーパーセットシステムコールであるため、glibcのfork()システムコールラッパーの実装は実際にclone()を呼び出しますが、これはプログラマーが行わない実装の詳細ですtについて知る必要があります。非常に古いバージョンのlibc、またはglibc以外の別のlibcを使用するプログラムがそれを使用する可能性があるため、実際のfork()システムコールは、下位互換性のためにLinuxカーネルにまだ存在しています。

clone()は、スレッドを作成するためのpthread_create() POSIX関数の実装にも使用されます。

移植可能なプログラムは、fork()ではなくpthread_create()およびclone()を呼び出す必要があります。

52
Celada

Linux 2.6には、2つのclone()が浮かんでいるようです

システムコールがあります:

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

これは、_man 2 clone_を実行することによって記述される "clone()"です。

そのmanページを十分に読むと、次のようになります。

_It is actually a library function layered on top of the
underlying clone() system call.
_

どうやら、あなたは紛らわしく同じ名前のシステムコールに階層化された「ライブラリ関数」を使用してスレッドを実装することになっています。

私は短いプログラムを書いた:

_#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
    pid_t cpid;
    switch (cpid = fork()) {
    case 0:   // Child process
        break;
    case -1:  // Error
        break;
    default:  // parent process
        break;
    }
    return 0;
}
_

_c99 -Wall -Wextra_を使用してコンパイルし、_strace -f_の下で実行して、システムフォークの実際の動作を確認します。私はこれをLinux 2.6.18マシン(x86_64 CPU)のstraceから取り出しました:

_20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0)                     = ?
20098 exit_group(0)
_

「fork」呼び出しはstrace出力に表示されません。 strace出力に表示されるclone()呼び出しには、man-page-cloneとは非常に異なる引数があります。最初の引数がint (*fn)(void *)と異なるため、_child_stack=0_。

fork(2)システムコールはrealclone()の観点から実装されているようです。 "ライブラリ関数" clone()は実装されました。 realclone()には、man-page-cloneとは異なる引数のセットがあります。

単純に言えば、fork()clone()に関する明らかに矛盾するステートメントはどちらも正しいです。ただし、含まれる「クローン」は異なります。

10
Bruce Ediger

fork()は、システムコールclone()に対する特定のフラグセットです。 clone()は、「プロセス」または「スレッド」、あるいはプロセスとスレッドの間にある奇妙なもの(たとえば、同じファイル記述子テーブルを共有する異なる「プロセス」)を作成するのに十分一般的です。

基本的に、カーネルの実行コンテキストに関連付けられているすべての「タイプ」の情報について、clone()を使用すると、その情報にエイリアスを付けるか、コピーするかを選択できます。スレッドはエイリアスに対応し、プロセスはコピーに対応します。 clone()にフラグの中間的な組み合わせを指定することで、スレッドやプロセスではない奇妙なものを作成できます。通常、これを行うべきではありません。Linuxカーネルの開発中に、clone()などの一般的なメカニズムを許可するかどうかについて、いくつかの議論があったと思います。

6
Atsby