Поведение потоков POSIX различно в HP-UX и Solaris 10 - PullRequest
0 голосов
/ 20 октября 2010

Я перевожу многопоточное приложение с HP-UX на Solaris, и пока все в порядке, кроме одной вещи! В приложении есть поток, который обрабатывает сигналы и, когда некоторые из них получены, выполняет некоторую очистку (ведение журнала, уничтожение дочерних процессов и т. Д.).

Я сократил код настолько, насколько это было возможно, чтобы сделать несколько простой пример, показывающий проблему:

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <synch.h>
#include <iostream>
#include <unistd.h>

using namespace std;

pthread_t       m_signalHandlerThread;
sigset_t        m_signalSet;

void    signalHandler()
{
    while ( true )
    {
        cout << "SigWait..." << endl;
        sigwait( &m_signalSet, &sig );
        cout << "Signal!! : " << sig << endl;

        break;
    }

    cout << "OUT" << endl;
}

void*   signalHandlerThreadFunction( void* arg )
{
   signalHandler();

   return (void*)0;
}


int main()  
{
    sigemptyset( &m_signalSet );
    sigaddset( &m_signalSet, SIGQUIT );             //kill -QUIT
    sigaddset( &m_signalSet, SIGTERM );             //kill
    sigaddset( &m_signalSet, SIGINT );              //ctrl-C
    sigaddset( &m_signalSet, SIGHUP );              //reload config

    if ( pthread_create( &m_signalHandlerThread, NULL, signalHandlerThreadFunction, NULL ) )
    {
        cout << "cannot create signal handler thread, system shut down.\n" << endl;
    }

    int iTimeout = 0;
    while (1) 
    {
        if (iTimeout >= 10)
           break;

        sleep(1);
        iTimeout++;
        cout << "Waiting... " << iTimeout << endl;
    }

    cout << "END" << endl;

    exit (0);
}

Использование командных строк компиляции: Solaris:

CC -m64 -g temp.cpp -D_POSIX_PTHREAD_SEMANTICS -lpthread

HP-UX:

/opt/aCC/bin/aCC +p +DA2.0W -AA -g -z -lpthread -mt -I/usr/include  temp.cpp     

Запуск обоих приложений, поведение (нажатие CTRL + C в цикле 10 секунд):

HP-UX:

./a.out

SigWait...
Waiting... 1
Waiting... 2
Signal!! : 2   <---- CTRL + C
OUT
Waiting... 3
Waiting... 4   <---- CTRL + C again to terminate

Solaris:

./a.out

SigWait...
Waiting... 1
Waiting... 2   <---- CTRL + C
^C

Любая помощь будет более чем приветствоваться, так как я уже рву свои волосы (осталось немного) :)!

Спасибо!

Ответы [ 4 ]

4 голосов
/ 20 октября 2010

Не указано, какой из ваших двух потоков будет обрабатывать SIGINT.Если вам нужен только один из ваших потоков для обработки сигнала, вам нужно заблокировать этот сигнал во всех других ваших потоках.

1 голос
/ 20 октября 2010

Единственный способ хорошо обрабатывать сигналы в многопоточном приложении - это сделать следующее:

  1. Заблокировать все сигналы в main() рано, до того, как будут созданы другие потоки, используя pthread_sigmask().
  2. Создать поток обработки сигналов. Используйте sigwait() или sigwaitinfo() для обработки сигналов в простой петле.

Таким образом, никакие потоки, кроме той, которая предназначена для обработки сигналов, не получат сигналы. Кроме того, поскольку передача сигналов осуществляется синхронно, вы можете использовать любые имеющиеся у вас средства связи между потоками, в отличие от классических обработчиков сигналов.

1 голос
/ 20 октября 2010

Вы должны блокировать сигналы для других потоков, используя pthread_sigmask. эта страница также содержит пример программы с потоком обработки сигналов.

0 голосов
/ 20 октября 2010

Это довольно необычный способ обработки сигналов. Если вы хотите объединить сигналы и потоки, лучше выбрать обычные обработчики сигналов , из которых сигнал сериализуется внутри, в другой поток, который отвечает за фактическую обработку события.

Это также лучший вариант, так как не определено, какой поток в приложении MT получает сигнал. Любые потоки, у которых нет заблокированного сигнала, могут получить его. Если у вас есть 2 потока (и в этом примере два потока), то любой из потоков может получить SIGINT.

Возможно, вы захотите проверить sigprocmask() как способ сообщить ОС, что SIGINT должен быть заблокирован в потоке. Это должно быть сделано для каждого потока, даже IIRC, вызывающего sigwait().


Edit1. На самом деле я ошибаюсь по поводу бита "должно быть сделано для каждого потока" выше. Новый поток наследует свою маску сигналов от текущего потока. Я понял, что это не может быть правдой, потому что это привело бы к состоянию гонки: сигнал приходит в то время, когда новый поток создан, но еще не установил свою маску сигнала. Другими словами, достаточно установить маску сигнала в главном потоке.

...