POSIXスレッドプログラミングでメモリ漏洩を避ける

5864 ワード

POSIXスレッド(pthread)プログラミングは、標準的なCプログラミング言語タイプ、関数、定数を定義します.pthreadsは強力なスレッド管理ツールを提供します.pthreadsを十分に使用するには、一般的なエラーを回避します.一般的なエラーは、接続可能なスレッドの接続を忘れ、メモリの漏洩を招き、ワークロードを増やすことです.このテクニック記事では、POSIXについて学習します.スレッドベースでは、スレッドメモリの漏洩を識別して検出する方法を理解し、このような状況を回避するための信頼できるアドバイスを得ることができます.
POSIXスレッドの概要
スレッドを使用する主な理由は、プログラムのパフォーマンスを向上させることです.スレッドの作成と管理には、オペレーティングシステムのオーバーヘッドが少なく、システムリソースが少ないだけです.1つのプロセス内のすべてのスレッドが同じアドレス空間を共有することで、スレッド間の通信がより効率的になり、プロセス間の通信よりも容易になります.例えば、1つのスレッドが1つの入出力システム呼び出しの完了を待っている場合、他のスレッドはCPU密集型タスクを処理することができる.スレッドを使用すると、重要なタスクを優先的にスケジュールしたり、中断したりすることができます.優先度の低いタスクです.たまに発生するタスクを定期的にスケジュールされたタスクの間に配置し、スケジュールの柔軟性を作成できます.最後に,pthreadsはマルチCPUコンピュータ上で並列プログラミングを行うのに理想的である.
また、POSIXスレッドまたはpthreadsを使用する主な原因は、標準的なC言語スレッドプログラミングインタフェースの一部として、高移植性である.
POSIXスレッドのプログラミングには多くの利点がありますが、基本的なルールを明確にしないと、デバッグしにくいコードを作成したり、メモリが漏洩したりする可能性があります.まず,POSIXスレッドをレビューし,接合可能スレッドまたは分離可能スレッドに分けた.
接続可能スレッド
新しいスレッドを生成し、どのように終了したかを知る必要がある場合は、結合可能なスレッドが必要です.接合可能なスレッドの場合、システムは、スレッド終了状態を格納するために専用メモリを割り当てる.スレッドが終了するとステータスが更新されます.スレッド終了ステータスを取得するには、pthread_join(pthread_t thread, void** value_ptr)を呼び出します.
スタック、スレッドID、スレッド終了ステータスなど、各スレッドに最下位ストレージを割り当てます.この最下位ストレージは、スレッドが終了し、他のスレッドに結合されるまで、プロセス空間に保持されます(回収できません).
スレッドの分離
ほとんどの場合、スレッドを作成してタスクを割り当て、他のトランザクションの処理を続行するだけです.これらの場合、スレッドがどのように終了するかに注目しないでください.この場合、分離スレッドを使用するのは良い選択です.
分離スレッドの場合、スレッドが終了すると、システムは下位リソースを自動的に回収します.
リークの識別
結合可能なスレッドを作成したが、接続を忘れた場合は、リソースまたはプライベートメモリがプロセススペースに保存され、リサイクルされていません.必ず接合可能なスレッドを接続しなければならない.それ以外の場合、深刻なメモリ漏洩の問題が発生する可能性があります.
たとえば、Red Hat Enterprise Linux(RHEL 4)のスレッドには10 MBのスタックが必要です.これは、接続しないと、少なくとも10 MBのメモリが漏洩することを意味します.マネージャ-ワークスレッドモードのプログラムを設計して、受信したリクエストを処理するとします.次に、各タスクを実行するためにますます多くの作業スレッドを作成し、最後にこれらのスレッドを終了する必要があります.結合可能なスレッドであり、pthread_join()を呼び出して結合していない場合は、スレッドが終了すると、生成されたスレッドごとに大量のメモリ(少なくともスタックあたり10 MB)が漏洩します.作成され、結合されていない状態で終了するワークスレッドが増えるにつれて、漏洩したメモリ量も増加し続けます.また、新しいスレッドを作成するためにメモリがないため、プロセスでは新しいスレッドを作成できません.
インベントリ1には、接続可能なスレッドの接続を忘れたときに発生した深刻なメモリリークが表示されます.また、このコードを使用して、1つのプロセス空間で共存できるスレッドボディの最大量をチェックすることもできます.
リスト1.メモリ漏洩の原因
				
#include<stdio.h>
#include<pthread.h>
void run() {
   pthread_exit(0);
}

int main () {
   pthread_t thread;
   int rc;
   long count = 0;
   while(1) {
      if(rc = pthread_create(&thread, 0, run, 0) ) {
         printf("ERROR, rc is %d, so far %ld threads created/n", rc, count);
         perror("Fail:");
         return -1;
      }
      count++;
   }
   return 0;
}

インベントリ1では、pthread_create()が呼び出され、デフォルトのスレッド属性を含む新しいスレッドが作成される.デフォルトでは、新しく作成されたスレッドは結合可能です.障害が発生するまで、新しい結合可能なスレッドを作成し続けます.エラーコードと障害原因を出力します.
リスト1のコードをRed Hat Enterprise Linux Server 5.4でコンパイルするには、次のコマンドを使用します.[root@server ~]# cc -lpthread thread.c -o threadで、リスト2に示す結果が得られます.
リスト2.メモリリーク結果
				
[root@server ~]# ./thread
ERROR, rc is 12, so far 304 threads created
Fail:: Cannot allocate memory

コードが304スレッドを作成した後、より多くのスレッドを作成できません.エラーコードは12です.これは、より多くのメモリが使用できないことを示します.
インベントリ1およびインベントリ2に示すように、結合可能なスレッドが生成されたが、結合されていないため、終了した各結合可能なスレッドはプロセススペースを占有し、プロセスメモリを漏洩する.
RHEL上のPOSIXスレッドは、10 MBのサイズのプライベートスタックを有する.言い換えれば、システムは、pthreadごとに少なくとも10 MBの専用ストレージを割り当てる.我々の例では、304個のスレッドは、プロセスが停止する前に作成される.これらのスレッドは304*10 MBのメモリを占有し、合計は約3 GBである.1つのプロセスの仮想メモリのサイズは4 GBで、プロセス空間の4分の1がLinuxカーネルのために予約されています.これにより,ユーザ空間として3 GBのメモリ空間が利用できる.そのため、3 GBのメモリはデッドスレッドで消費されます.これは深刻なメモリ漏れです.発生速度がなぜこんなに速いのか理解しやすい.
漏洩を修復するには、コードコールpthread_join()を追加します.この方法では、各結合可能なスレッドを結合できます.
リーク検出
他のメモリ漏洩と同様に、プロセスの開始時に問題はそれほど顕著ではない可能性があります.ここでは、ソースコードにアクセスすることなく、このような問題を検出する方法について説明します.
  • プロセス内のスレッドスタックの数を計算します.これには、実行中のアクティブなスレッドと終了したスレッドの数が含まれます.
  • プロセスで実行中のアクティブなスレッドの数を計算します.
  • 両者を比較する.既存のスレッドスタックの数が実行中のアクティブなスレッドの数より大きく、プログラムの実行時にこの2つの数値の差が増加している場合、漏れが発生します.

  • このメモリ漏洩は、接続可能なスレッドが接続されていないため可能性が高い.
    pmapを使用してスレッドスタック数を計算する
    実行中のプロセスでは、スレッドスタックの数はプロセス内のスレッドボディの数に等しい.スレッドボディには、実行中のアクティブなスレッドと結合可能なデッドスレッドが含まれます.pmapは、プロセスメモリを報告するためのLinuxツールです.スレッドスタック数を取得するには、次のコマンドを使用します.[root@server ~]# pmap PID | grep 10240 | wc -l
    (10240 KBはRed Hat Enterprise Linux Server 5.4のデフォルトスタックサイズです.)
    /proc/PID/taskを使用してアクティブスレッド数を計算する
    スレッドが作成され、スレッドが実行されるたびに、/proc/PID/taskにエントリが埋め込まれます.スレッドが終了すると、スレッドが結合可能であっても分離されていても、エントリは/proc/PID/taskから削除されます.したがって、アクティブなスレッド数は、次のコマンドを実行することによって得られます.[root@server ~]# ls /proc/PID/task | wc -l .
    ひかくしゅつりょく pmap PID | grep 10240 | wc -lの出力を確認し、ls /proc/PID/task | wc -lの出力と比較します.すべてのスレッドスタックの数がアクティブなスレッドの数より大きく、プログラムの実行時に両者の差が増加し続けている場合は、メモリ漏洩の問題が確実に発生していることを確認できます.
    漏れを防ぐ
    プログラミング中に結合可能なスレッドを接続する必要があります.プログラムで結合可能なスレッドを作成する場合は、pthread_join(pthread_t, void**)を呼び出してスレッドに割り当てられた専用ストレージを回収することを忘れないでください.そうしないと、深刻なメモリ漏洩の問題が発生します.
    プログラミング後のテストフェーズでは、pmapおよび/proc/PID/taskを使用して、この漏洩が存在するかどうかを検出できます.存在する場合は、ソースコードをチェックして、すべての結合可能なスレッドが接続されているかどうかを確認します.
    これらの内容だけです.わずかな予防作業で、後続の作業を大幅に削減し、メモリの漏洩を回避できます.