Подтвердить что ты не робот

Можно ли определить поток, содержащий мьютекс?

Во-первых, я использую библиотеку pthread для записи многопоточной программы C. Нитки всегда зависали от ожидающих мьютексов. Когда я использую утилиту strace, чтобы найти поток в состоянии FUTEX_WAIT, я хочу знать, какой поток удерживает этот мьютекс в то время. Но я не знаю, как я мог это сделать. Существуют ли какие-либо утилиты?

Кто-то сказал мне, что виртуальная машина Java поддерживает это, поэтому я хочу знать, поддерживает ли Linux эту функцию.

4b9b3361

Ответ 1

Для этого вы можете использовать знание внутренних элементов мьютекса. Обычно это не очень хорошая идея, но это хорошо для отладки.

В Linux с NPTL-реализацией pthreads (который является любым современным glibc) вы можете изучить член __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 для этого потока и переключиться на него.

Ответ 2

Я не знаю ни одного такого объекта, поэтому я не думаю, что вы так легко справитесь - и, вероятно, это будет не так информативно, как вы думаете, помогая отлаживать вашу программу. Как низкотехнологичный, как может показаться, регистрация - ваш друг в отладке этих вещей. Начните собирать свои собственные небольшие функции регистрации. Им не обязательно быть фантазией, им просто нужно выполнить работу во время отладки.

Извините за С++, но что-то вроде:

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);
}

Журналирование - не идеальное решение, но ничего нет. Как правило, вы получаете то, что вам нужно знать.

Ответ 3

Обычно вызовы libc/platform абстрагируются уровнем абстракции OS. Мертвые блокировки мьютексов можно отслеживать с использованием переменной владельца и pthread_mutex_timedlock. Всякий раз, когда поток блокируется, он должен обновлять переменную собственным tid (gettid() и может также иметь другую переменную для хранения идентификатора pthread). Поэтому, когда другие потоки блокируются и выходят на 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.