web-dev-qa-db-ja.com

ミューテックスを保持しているスレッドを判別することは可能ですか?

まず、pthreadライブラリを使用してマルチスレッドCプログラムを作成します。スレッドは常に待機しているミューテックスによってハングします。 straceユーティリティを使用して、スレッドがFUTEX_WAITステータス、どのスレッドがそのミューテックスを保持しているかを知りたい。しかし、私はそれをどうやって作ることができるかわかりません。それを行うことができるユーティリティはありますか?

誰かが私にJava仮想マシンがこれをサポートしているので、Linuxがこの機能をサポートしているかどうか知りたいです。

62
terry

これを行うには、ミューテックス内部の知識を使用できます。通常、これはあまり良い考えではありませんが、デバッグには適しています。

Linuxでpthread(最新のglibc)のNPTL実装では、___data.__owner_構造体の_pthread_mutex_t_メンバーを調べて、現在ロックされているスレッドを見つけることができます。これは、gdbを使用してプロセスにアタッチした後の方法です。

_(gdb) thread 2
[Switching to thread 2 (Thread 0xb6d94b90 (LWP 22026))]#0  0xb771f424 in __kernel_vsyscall ()
(gdb) bt
#0  0xb771f424 in __kernel_vsyscall ()
#1  0xb76fec99 in __lll_lock_wait () from /lib/i686/cmov/libpthread.so.0
#2  0xb76fa0c4 in _L_lock_89 () from /lib/i686/cmov/libpthread.so.0
#3  0xb76f99f2 in pthread_mutex_lock () from /lib/i686/cmov/libpthread.so.0
#4  0x080484a6 in thread (x=0x0) at mutex_owner.c:8
#5  0xb76f84c0 in start_thread () from /lib/i686/cmov/libpthread.so.0
#6  0xb767784e in clone () from /lib/i686/cmov/libc.so.6
(gdb) up 4
#4  0x080484a6 in thread (x=0x0) at mutex_owner.c:8
8               pthread_mutex_lock(&mutex);
(gdb) print mutex.__data.__owner
$1 = 22025
(gdb)
_

(ハングしたスレッドに切り替え、バックトレースを実行してスタックしているpthread_mutex_lock()を見つけ、スタックフレームを変更してロックしようとしているミューテックスの名前を見つけ、そのミューテックスの所有者を出力します) 。これにより、LWP ID 22025のスレッドが原因であることがわかります。

その後、_thread find 22025_を使用して、そのスレッドのgdbスレッド番号を見つけて切り替えることができます。

102
caf

私はそのような機能を知らないので、あなたがそれを簡単に降りることはないと思います-そして、おそらくあなたがあなたのプログラムをデバッグするのを助けることであなたが考えるほど有益ではないでしょう。ローテクに思えるかもしれませんが、ロギングはこれらのことをデバッグする上での友人です。独自の小さなログ機能の収集を開始します。派手である必要はなく、デバッグ中に仕事を終わらせるだけです。

C++で申し訳ありませんが、次のようなものです:

void logit(const bool aquired, const char* lockname, const int linenum)
{
    pthread_mutex_lock(&log_mutex);

    if (! aquired)
        logfile << pthread_self() << " tries lock " << lockname << " at " << linenum << endl;
    else
        logfile << pthread_self() << " has lock "   << lockname << " at " << linenum << endl;

    pthread_mutex_unlock(&log_mutex);
}


void someTask()
{
    logit(false, "some_mutex", __LINE__);

    pthread_mutex_lock(&some_mutex);

    logit(true, "some_mutex", __LINE__);

    // do stuff ...

    pthread_mutex_unlock(&some_mutex);
}

ロギングは完璧なソリューションではありませんが、何もありません。通常、あなたが知る必要のあるものが得られます。

5
Duck

通常、libc/platforms呼び出しはOS抽象化レイヤーによって抽象化されます。相互排他ロックは、所有者変数とpthread_mutex_timedlockを使用して追跡できます。スレッドがロックするたびに、独自のtid(gettid()で変数を更新する必要があり、pthread idストレージ用に別の変数を持つこともできます)。そのため、他のスレッドがブロックしてpthread_mutex_timedlockでタイムアウトになった場合、所有者tidおよびpthread_idの値を出力できます。これにより、所有者スレッドを簡単に見つけることができます。以下のコードスニペットを見つけてください、すべてのエラー条件が処理されないことに注意してください

pid_t ownerTid;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

class TimedMutex {
    public:
        TimedMutex()
        {
           struct timespec abs_time;

           while(1)
           {
               clock_gettime(CLOCK_MONOTONIC, &abs_time);
               abs_time.tv_sec += 10;
               if(pthread_mutex_timedlock(&mutex,&abs_time) == ETIMEDOUT)
               {
                   log("Lock held by thread=%d for more than 10 secs",ownerTid);
                   continue;
               }
               ownerTid = gettid();
           }
        }

        ~TimedMutex()
        {

             pthread_mutex_unlock(&mutex);  
        }
};

デッドロックを見つける方法は他にもあります。おそらくこのリンクが役立つかもしれません http://yusufonlinux.blogspot.in/2010/11/debugging-core-using-gdb.html

2
Yusuf Khan

以下のリンクをお読みください。これには、ロック所有者を見つけるための一般的なソリューションがあります。ライブラリの内側にロックインし、ソースコードがない場合でも機能します。

https://en.wikibooks.org/wiki/Linux_Applications_Debugging_Techniques/Deadlocks

1
Jossy Sebastian