Linux (マルチプロセスおよびマルチスレッド)

目次

1. プロセスとスレッドの概念

1.1 プロセス

1.2 スレッド

1.3 プロセスとスレッドの違い

2. マルチプロセス

2.1 マルチプロセスの概念

2.2 プロセス関連API

2.3 マルチプロセスプログラミング

3. マルチスレッド

3.1 マルチスレッドの概念

3.2 マルチスレッド関連の API

3.3 マルチスレッドプログラミング


1. プロセスとスレッドの概念

1.1 プロセス

コンピュータ サイエンスでは、プロセスは実行中のプログラムのインスタンスです。プロセスには、プログラムのコード、データ、実行コンテキスト、およびオペレーティング システムによって割り当てられたリソースが含まれます。 プロセスは、オペレーティング システムの最小の実行単位です。オペレーティング システムは、マルチタスクを実現するためにプロセスを管理およびスケジュールします。

プロセスに関する重要な概念をいくつか示します。

  1. 程序 vs. 进程

    • プログラム: コンピューター命令を含む静的コード ファイルです。プログラム自体はコンピュータ上で実行されるのではなく、単なる命令の集合です。
    • プロセス (Process): コンピュータ上で実際に実行されているプログラムのインスタンスです。プロセスには独自のメモリ空間、レジスタ、実行ステータスなどがあります。
  2. 进程特点

    • 独立性: 各プロセスは独立して実行されるエンティティであり、他のプロセスの影響を受けません。
    • 同時実行性: 複数のプロセスを同時に実行でき、オペレーティング システムはタイム スライシングやその他のメカニズムを通じて同時実行性を実現します。
    • 動的: プロセスは作成、実行、終了でき、ライフサイクルがあります。
  3. 进程状态

    • Ready: プロセスは実行の準備ができており、プロセッサ リソースが割り当てられるのを待っています。
    • 実行中: プロセスは命令を実行しています。
    • ブロック済み: プロセスはイベント (入力と出力の完了など) が発生するのを待ちます。
    • 終了: プロセスは実行を完了したか、終了しました。
  4. プロセス間通信: プロセスは相互に通信し、連携する必要がある場合があります。一般的に使用されるプロセス間通信方法には、パイプ、シグナル、共有メモリ、メッセージ キュー、ソケットなどが含まれます。

  5. プロセス スケジュール: オペレーティング システムは、プロセスの実行の管理とスケジュールを担当します。スケジューリング アルゴリズムは、どのプロセスがプロセッサ リソースを取得するかを決定し、プロセス間の切り替えを制御します。

  6. プロセス コントロール ブロック (PCB): PCB は、プロセス ステータス、プログラム カウンター、レジスタ値、メモリ ポインタ、などの情報。

プロセスはオペレーティング システムの基本概念であり、プロセスの管理とスケジュール設定を通じてコン​​ピュータのマルチタスクとリソースの共有を実現します。

1.2 スレッド

スレッドはオペレーティング システムの最小のスケジューリング単位であり、プロセスの実行プロセスであり、プロセス内のスケジューリングと実行の基本単位です。 スレッドは同じプロセス内のプロセスのメモリ空間とリソースを共有しますが、各スレッドは独自のスタック空間とレジスタ状態を持ちます。

スレッドに関する重要な概念をいくつか示します。

  1. 线程特点

    • スレッドはプロセス内で作成および管理されます。プロセスには、プロセスのリソースを共有する複数のスレッドを含めることができます。
    • スレッド間の切り替えは、同じプロセスのメモリ空間とコンテキストを共有するため、コストが低くなります。
    • スレッドはプロセスのリソースを共有し、スレッドごとに独立したリソースを作成する必要がないため、通常、スレッドはプロセスよりも速く作成および破棄されます。
    • スレッドを使用して同時実行を実装し、システムの応答性とリソース使用率を向上させることができます。
  2. スレッドとプロセスの関係:

    • プロセスには、プロセスのコード、データ、リソースを共有する複数のスレッドを含めることができます。
    • 複数のスレッドが同じプロセス内で実行され、スレッド間で通信および同期することができます。
    • 複数のプロセスは互いに独立しており、独自の独立したメモリ空間とリソースを持っています。
  3. スレッド スケジュール: オペレーティング システムは、スレッドの実行の管理とスケジュールを担当します。さまざまなスレッド スケジューリング アルゴリズムによって、どのスレッドがプロセッサ リソースを取得するかが決まります。

  4. スレッドの同期: マルチスレッド プログラミングでは、スレッドが互いに干渉したり競合したりすることがあります。 ミューテックス、セマフォ、条件変数などのスレッド同期メカニズムは、スレッド間の操作を調整してデータの一貫性と正確性を確保するために使用されます。

  5. ユーザーレベルのスレッドとカーネルレベルのスレッド:

    • ユーザーレベルのスレッド: スレッドの作成、スケジューリング、および管理はユーザー プログラムによって完全に制御され、オペレーティング システムはスレッドを認識しません。
    • カーネル レベルのスレッド: スレッドの作成、スケジューリング、および管理はオペレーティング システムによって制御され、オペレーティング システムがスレッドを直接管理します。

スレッドはオペレーティング システムの基本的なスケジューリング単位であり、同時実行が必要なタスクに適しています。マルチスレッド プログラミングを通じて、マルチコア プロセッサの機能を最大限に活用し、プログラムのパフォーマンスと応答性を向上させることができます。しかし同時に、マルチスレッド プログラミングでは、スレッドの安全性と同期の問題も考慮する必要があります。

1.3 プロセスとスレッドの違い

  1. リソース割り当て: プロセスは独立したリソース (メモリ、ファイル ハンドルなど) を持ちますが、スレッドは属するプロセスのリソースを共有します。
  2. スイッチング オーバーヘッド: プロセス間のスイッチング オーバーヘッドは大きく、スレッド間のスイッチング オーバーヘッドは小さいです。
  3. 分離: プロセスは相互に分離されます。1 つのプロセスでエラーが発生しても他のプロセスには影響しません。スレッドはメモリを共有するため、1 つのスレッドでのエラーが他のスレッドに影響を与える可能性があります。
  4. 通信と同期: プロセス通信はより複雑で、メモリはスレッド間で共有されます。通信と同期は便利ですが、エラーも発生しやすくなります。

一般に、マルチスレッド プログラミングは軽量で、マルチコア プロセッサをより効果的に利用できますが、共有リソースへの同時アクセスをより慎重に処理する必要があります。マルチプロセス プログラミングは比較的安全ですが、コストが高くなります。プロセスまたはスレッドの使用の選択は、アプリケーションのニーズとマルチタスクの優先順位によって異なります。

2. マルチプロセス

2.1 マルチプロセスの概念

複数の独立したプロセスが異なるアドレス空間で実行されるため、比較的安全ですがコストがかかります。

マルチプロセス プログラミングは、オペレーティング システムのマルチプロセス機能を使用して複数のタスクの並列実行を実装する同時プログラミング方法です。マルチプロセス プログラミングでは、各タスクは独立したプロセスとしてカプセル化され、異なるメモリ空間で実行され、互いに比較的独立しています。この方法では、マルチコア プロセッサを効果的に利用し、プログラムのパフォーマンスと応答性を向上させることができます。

2.2 プロセス関連API

  1. fork()関数:

    • 機能: 新しい子プロセスを作成します。子プロセスは親プロセスのコピーであり、同じプログラム コードを実行します。
    • 戻り値: 親プロセスの場合は子プロセスの PID を返し、子プロセスの場合は 0 を返し、失敗した場合は -1 を返します。
    • ヘッド ファイル:<unistd.h>
  2. exec 函数族(如 execl, execv, execle, execve 等):

    • 機能: 現在のプロセスに新しいプログラムをロードして実行するために使用されます。
    • さまざまなパラメータの受け渡し方法をサポートするには、さまざまな関数名とパラメータを使用します。
    • 戻り値: -1 はエラーの場合にのみ返され、正常に実行された後は返されません。
  3. wait()waitpid() 函数

    • 機能: 子プロセスの終了ステータスを取得するために、子プロセスが終了するのを待ちます。
    • wait()子プロセスが終了するまで呼び出しプロセスをブロックします。
    • waitpid()待機する子プロセスの PID を指定して、ノンブロッキング待機を可能にします。
    • 戻り値: 終了した子プロセスの PID、またはエラーの場合は -1。
    • ヘッド ファイル:<sys/wait.h>
  4. exit() 函数

    • 機能: 呼び出し元のプロセスを終了し、親プロセスにステータス コードを返します。
    • パラメータ: 親プロセスに渡されるステータス コード。
    • 戻り値はなく、そのまま処理を終了します。
  5. getpid()関数:

    • 機能: 現在のプロセスの PID (プロセス識別子) を取得します。
    • 戻り値: 現在のプロセスの PID。
    • ヘッド ファイル:<unistd.h>
  6. getppid()関数:

    • 機能: 現在のプロセスの親プロセスの PID を取得します。
    • 戻り値: 親プロセスの PID。
    • ヘッド ファイル:<unistd.h>
  7. kill()関数:

    • 機能: 指定されたプロセスにシグナルを送信します。
    • パラメータ: ターゲットプロセスの PID とシグナル番号。
    • 戻り値: 成功した場合は 0、失敗した場合は -1。
  8. getuid()getgid() 函数:

    • 機能: 現在のプロセスのユーザー ID とグループ ID を取得します。
    • 戻り値: ユーザー ID とグループ ID。
    • ヘッド ファイル:<unistd.h>
  9. setuid()setgid() 函数:

    • 機能: 現在のプロセスのユーザー ID とグループ ID を設定します。
    • パラメータ: 設定するユーザー ID とグループ ID。
    • 戻り値: 成功した場合は 0、失敗した場合は -1。
  10. sleep()関数:

    • 機能: 現在のプロセスを指定された期間スリープさせます。
    • パラメータ: スリープ時間 (秒)。
    • 戻り値: スリープ完了後の残りのスリープ時間を返します (0 は完全なスリープを意味し、-1 はスリープが中断されたことを意味します)。

2.3 マルチプロセスプログラミング

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h> // 包含用于等待子进程的头文件

int main() {
    // 创建第一个子进程
    // 在父进程中,fork() 函数返回子进程的 PID,而在子进程中,fork() 返回 0。
    pid_t child1_pid = fork();

    if (child1_pid == -1) {
        // fork() 失败时返回 -1,输出错误信息并退出程序
        perror("Error creating child process");
        return 1;
    }

    if (child1_pid == 0) {
        // 子进程 1 逻辑
        printf("Child 1: My PID is %d\n", getpid());
        // 子进程 1 执行完毕
        return 0;
    }

    // 创建第二个子进程
    pid_t child2_pid = fork();

    if (child2_pid == -1) {
        // fork() 失败时返回 -1,输出错误信息并退出程序
        perror("Error creating child process");
        return 1;
    }

    if (child2_pid == 0) {
        // 子进程 2 逻辑
        printf("Child 2: My PID is %d\n", getpid());
        // 子进程 2 执行完毕
        return 0;
    }

    // 父进程逻辑
    printf("Parent: My PID is %d\n", getpid());
    printf("Parent: Child 1 PID is %d, Child 2 PID is %d\n", child1_pid, child2_pid);

    // 等待两个子进程执行完毕
    wait(NULL); // 等待第一个子进程
    wait(NULL); // 等待第二个子进程

    return 0;
}

演算結果

3. マルチスレッド

3.1 マルチスレッドの概念

同じプロセス内の複数のスレッドが同じアドレス空間を共有するため、軽量になりますが、同期とデータの共有に注意が必要です。マルチスレッド プログラミングにより、プログラムの同時実行性と効率が向上します。ただし、マルチスレッド プログラミングでは、スレッド セーフや競合状態など、共有リソースへの同時アクセスの問題に注意する必要があります。

マルチスレッド プログラミングとは、同じプロセス内で複数のスレッドを作成および管理し、これらのスレッドが異なるタスクを同時に実行できるようにすることです。各スレッドはプロセスのメモリ空間とリソースを共有しますが、各スレッドには独自のスタック空間と実行コンテキストがあります。

3.2 マルチスレッド関連の API

  1. pthread_create()関数:

    • 機能: 新しいスレッドを作成します。
    • パラメータ: 新しいスレッド、スレッドのプロパティ、スレッド関数およびパラメータへの参照。
    • 戻り値: スレッドの作成に成功した場合は 0 が返され、それ以外の場合はエラー コードが返されます。
    • ヘッド ファイル:<pthread.h>
  2. pthread_join()関数:

    • 機能: スレッドが終了し、その終了ステータスを取得するのを待ちます。
    • パラメータ: 待機するスレッドの参照、スレッドの終了ステータスへのポインタ。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>
  3. pthread_exit()関数:

    • 機能: 現在のスレッドを終了し、スレッドの終了ステータスを指定します。
    • パラメータ: スレッドの終了ステータス。
    • 戻り値はなく、スレッドは直接終了されます。
    • ヘッド ファイル:<pthread.h>
  4. pthread_self()関数:

    • 機能: 現在のスレッドのスレッド ID を取得します。
    • 戻り値: 現在のスレッドのスレッド ID。
    • ヘッド ファイル:<pthread.h>
  5. pthread_mutex_init()関数:

    • 機能: ミューテックスロックを初期化します。
    • パラメータ: ミューテックス参照およびミューテックス属性 (NULL の可能性があります)。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>
  6. pthread_mutex_lock()pthread_mutex_unlock() 函数:

    • 機能: ミューテックス ロックをロックおよびロック解除します。
    • パラメータ: ミューテックス参照。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>
  7. pthread_mutex_destroy()関数:

    • 機能: ミューテックス ロックを破棄します。
    • パラメータ: ミューテックス参照。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>
  8. pthread_cond_init()関数:

    • 機能: 条件変数を初期化します。
    • パラメータ: 条件変数参照および条件変数属性 (NULL の可能性があります)。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>
  9. pthread_cond_wait()pthread_cond_signal() 函数:

    • 機能: 待機およびウェイクアップ条件変数。
    • パラメータ: 条件変数参照とミューテックス参照。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>
  10. pthread_cond_destroy()関数:

    • 機能: 条件変数を破棄します。
    • パラメータ: 条件変数の参照。
    • 戻り値: 成功した場合は 0 を返し、それ以外の場合はエラー コードを返します。
    • ヘッド ファイル:<pthread.h>

3.3 マルチスレッドプログラミング

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

#define NUM_THREADS 4 //创建线程数量

// 共享资源
int shared_counter = 0;
pthread_mutex_t mutex; // 互斥锁

// 增加计数器的函数,多个线程会同时调用这个函数
void* increment_counter(void* arg) {
    for (int i = 0; i < 100000; ++i) {
        pthread_mutex_lock(&mutex); // 加锁,开始临界区
        shared_counter++; // 对共享资源进行操作
        pthread_mutex_unlock(&mutex); // 解锁,结束临界区
    }
    return NULL;
}

int main() {
    pthread_t threads[NUM_THREADS]; // 存储线程的数组
    
    pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
    
    // 创建多个线程
    for (int i = 0; i < NUM_THREADS; ++i) {
        pthread_create(&threads[i], NULL, increment_counter, NULL);
    }
    
    // 等待所有线程执行完毕
    for (int i = 0; i < NUM_THREADS; ++i) {
        pthread_join(threads[i], NULL);
    }

    pthread_mutex_destroy(&mutex); // 销毁互斥锁

    printf("Final counter value: %d\n", shared_counter);
    
    return 0;
}

実行結果 (なぜ 3 回出力されるのですか?)

理由: マルチスレッドとマルチプロセスをテストするための 2 つのソース ファイルは、main.c ファイルでコンパイルされて呼び出されています。サブ関数で作成された新しいプロセスは、サブ関数が呼び出されて実行された後も親の実行を継続します。 . 関数が処理の後に実行されるため、マルチスレッドをテストするコードが 3 回呼び出されます。

おすすめ

転載: blog.csdn.net/qq_57594025/article/details/132525587