2012-02-06 11 views
17

this answerとRobert Loveの "Linux Kernel Development"を読んだ後、clone()システムコールで、Linuxのプロセスとスレッドが(ほとんど)カーネルと区別できないことがわかりました。それらの間にはいくつかの調整があります(引用されたSOの質問では、「より多くの共有」または「少ない共有」として議論されています)。Linuxのプロセスとスレッドの区別

私は最近、いくつかのPOSIXスレッドを含むプログラムに取り組み、この前提を実験することに決めました。 2つのスレッドを作成するプロセスでは、すべてのスレッドはpthread_self()ただしで返される一意の値を取得します。getpid()ではありません。

I作成したサンプルプログラムは、以下:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <unistd.h> 
#include <pthread.h> 

void* threadMethod(void* arg) 
{ 
    int intArg = (int) *((int*) arg); 

    int32_t pid = getpid(); 
    uint64_t pti = pthread_self(); 

    printf("[Thread %d] getpid() = %d\n", intArg, pid); 
    printf("[Thread %d] pthread_self() = %lu\n", intArg, pti); 
} 

int main() 
{ 
    pthread_t threads[2]; 

    int thread1 = 1; 

    if ((pthread_create(&threads[0], NULL, threadMethod, (void*) &thread1)) 
     != 0) 
    { 
     fprintf(stderr, "pthread_create: error\n"); 
     exit(EXIT_FAILURE); 
    } 

    int thread2 = 2; 

    if ((pthread_create(&threads[1], NULL, threadMethod, (void*) &thread2)) 
     != 0) 
    { 
     fprintf(stderr, "pthread_create: error\n"); 
     exit(EXIT_FAILURE); 
    } 

    int32_t pid = getpid(); 
    uint64_t pti = pthread_self(); 

    printf("[Process] getpid() = %d\n", pid); 
    printf("[Process] pthread_self() = %lu\n", pti); 

    if ((pthread_join(threads[0], NULL)) != 0) 
    { 
     fprintf(stderr, "Could not join thread 1\n"); 
     exit(EXIT_FAILURE); 
    } 

    if ((pthread_join(threads[1], NULL)) != 0) 
    { 
     fprintf(stderr, "Could not join thread 2\n"); 
     exit(EXIT_FAILURE); 
    } 

    return 0; 
} 

(これは、64ビットのFedoraの[gcc -pthread -o thread_test thread_test.c】コンパイルされた;起因<bits/pthreadtypes.h>から供給pthread_tために使用される64ビットのタイプに、コードがマイナー必要とします32ビット版でコンパイルする変更)

次のように私が手出力は次のようになります。scheduを使用することにより

[[email protected] ~]$ ./thread_test 
[Process] getpid() = 28549 
[Process] pthread_self() = 140050170017568 
[Thread 2] getpid() = 28549 
[Thread 2] pthread_self() = 140050161620736 
[Thread 1] getpid() = 28549 
[Thread 1] pthread_self() = 140050170013440 
[[email protected] ~]$ 

LER gdbにロック、私は、はちょうどプロセスを示している、ので、私はtopが言うキャプチャすることができます生きているプログラムとそのスレッドを維持することができます:

PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
28602 bean  20 0 15272 1112 820 R 0.4 0.0 0:00.63 top 
2036 bean  20 0 108m 1868 1412 S 0.0 0.0 0:00.11 bash 
28547 bean  20 0 231m 16m 7676 S 0.0 0.4 0:01.56 gdb 
28549 bean  20 0 22688 340 248 t 0.0 0.0 0:00.26 thread_test 
28561 bean  20 0 107m 1712 1356 S 0.0 0.0 0:00.07 bash 

とスレッドを示し、こう述べています。

PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
28617 bean  20 0 15272 1116 820 R 47.2 0.0 0:00.08 top 
2036 bean  20 0 108m 1868 1412 S 0.0 0.0 0:00.11 bash 
28547 bean  20 0 231m 16m 7676 S 0.0 0.4 0:01.56 gdb 
28549 bean  20 0 22688 340 248 t 0.0 0.0 0:00.26 thread_test 
28552 bean  20 0 22688 340 248 t 0.0 0.0 0:00.00 thread_test 
28553 bean  20 0 22688 340 248 t 0.0 0.0 0:00.00 thread_test 
28561 bean  20 0 107m 1860 1432 S 0.0 0.0 0:00.08 bash 

プログラムやカーネルには、プロセスとは異なりスレッドを定義する明確な方法があることは明らかです。各スレッドはtopに従って独自のPIDを持っています - なぜですか?

+1

'clone()'は、Linuxが両方のスレッドと 'fork()'をどのように実装しているかです。重要なことは、PIDと話すことで、知る必要があるすべての人にその信号を伝えることです。カーネルがスレッドに追加のIDを割り当てる場合、それはあなたのビジネスのどれでもなく、プロセスとの会話方法にも影響しません。 –

+0

良い[リンク](http://opensourceforgeeks.blogspot.in/2014/03/processes-and-threads-in-linux.html)。 –

+0

"* Linuxのプロセスとスレッドは、カーネルと(ほとんど)区別できません。プロセスとスレッドの両方に当てはまるLinuxカーネルの仕組みについては、ほとんど何も言えません。 VMのビューを所有していますか?プロセスのみ。スケジュールすることはできますか?スレッドのみ。ファイル記述子テーブルを持っていますか?プロセスのみ。優先順位はありますか?スレッドのみ。そして、ラインの下で。 –

答えて

29

これらの混乱は、カーネルがそれらを共有させる方法を提供していれば、カーネルプロセスをプリミティブとして使用してスレッドをほぼ完全にユーザー空間で実装できるという非合理的かつ間違った見方を元来持っていたメモリとファイル記述子。これはPOSIXスレッドのLinuxThreadsの実装が悪いことにつながります。これは、POSIXスレッドのセマンティクスに遠隔的に似ていることがないため、誤った名前でした。最終的にLinuxThreadsが(NPTLによって)置き換えられましたが、紛らわしい用語や誤解の多くが残されています。

「PID」は、カーネル空間とユーザー空間で異なることを意味します。カーネルがPIDを呼び出すのは、カーネルレベルのスレッドID(しばしばTIDと呼ばれる)です。別の識別子であるpthread_tと混同しないでください。システム上の各スレッドは、同じプロセスでも異なるスレッドでも、一意のTID(またはカーネルの用語では「PID」)を持っています。

"プロセス"というPOSIXの意味でPIDと見なされるものは、カーネル内では "スレッドグループID"または "TGID"と呼ばれます。各プロセスは、それぞれ独自のTID(カーネルPID)を持つ1つ以上のスレッド(カーネルプロセス)から構成されますが、すべて同じTGIDを共有します。mainが実行される最初のスレッドのTID(カーネルPID)

topにスレッドが表示されている場合、PID(カーネルのTGID)ではなくTID(カーネルPID)が表示されているため、スレッドごとに別々のスレッドが存在します。 NPTLの出現により

は、呼び出しプロセスにPID引数または行為を取るほとんどのシステムコールはTGIDとしてPIDを扱い、全体の「スレッドグループ」(POSIXプロセス)に作用するように変更されました。

+0

あなたの答えをありがとう、私は間違いなく、あなたがしばらく言っていることを研究するでしょう。今日の私のポストからの最大の疑問はおそらく(なぜ私が知る限り)「なぜ他の人がこれについて尋ねなかったのか」ということです。 (少なくとも、簡単に利用できるリソースを通して)確かに、私のようにマルチスレッドアプリケーションに関与している人にとって、これは大きな話題になるはずです。 – Doddy

+0

POSIXスレッド*を使って、POSIXで指定されているように、アプリケーションプログラマは実際にビジネス*を行うことができますが、ほとんどすべてが正しく動作するため、あまり気にする必要はありません。実装の詳細がそれほど注目を集めていないのはそのためだと思う。 BTW 'man 7 pthreads'は、どのように動作するのか基本的な説明があります。 –

+0

@bean - ここではさまざまな角度から多くの異なる方法が求められています。 RはLinuxの歴史的および技術的な観点からいくつかの混乱のレベルに触れるという点で特に良い答えを与えています。 – Duck

0

Linuxでは、すべてのスレッドはスレッドIDを取得します。メインスレッドのスレッドIDは、プロセスIDとして二重の役割を果たします(ユーザーインターフェイスではよく知られています)。スレッドIDは、Linuxの実装の詳細であり、POSIX IDとは無関係です。詳細については、gettidシステムコールを参照してください(純粋なPythonではシステム固有のものではありません)。

+0

「メインスレッドのスレッドIDがプロセスIDと同じ二重の役割を果たすかどうか」が真であるかどうかはわかりません。上記のコードを実行すると、メインスレッドのスレッドIDはPIDと同じではありませんでした。 – codeforester

1

「メタエンティティ」のようなものを想像してみてください。エンティティが親のリソース(アドレス空間、ファイル記述子など)を共有していない場合、それはプロセスであり、エンティティがその親のすべてのリソースを共有する場合はスレッドです。あなたは、プロセスとスレッドの中間に何かを持つことさえできます(例えば、共有されているリソースや共有されていないものなど)。 "clone()"システムコール(例えばhttp://linux.die.net/man/2/clone)を見てください。これはLinuxが内部的に何をするかを示しています。

ここでは、すべてをプロセスまたはスレッドのように見せる抽象化の背後に隠しています。抽象化が完璧であれば、エンティティとプロセスとスレッドの違いを知ることはできません。抽象化は完全には完全ではありません。実際に見ているPIDは実際には「エンティティID」です。

+0

申し訳ありませんが、あなたの答えは私の質問に全く光を当てていません。私はすでに 'clone()'のマニュアルページを見てきました。プロセスとスレッドの間に抽象化があったという事実が、まず私が質問をした理由です。私が50%未満のリソースを共有して 'clone() 'を呼び出すことを選択したので、プロセスとは対照的にスレッド*を与えなければならないと言っても大変簡単です。 – Doddy

関連する問題