簡體   English   中英

gettimeofday異步信號安全嗎? 如果在信號處理程序中使用它會導致死鎖嗎?

[英]Is gettimeofday async signal safe ? and can it cause deadlock if used in signal handler?

以前我問了一個關於如何終止阻塞I / O的線程的問題 考慮到一些優點,我使用了pthread_kill()而不是pthread_cancel()或寫入管道。 在使用pthread_kill()實現將信號(SIGUSR2)發送到目標線程的代碼之后,最初它工作正常並且沒有遇到任何問題。

static void signalHandler(int signum) {
    LogInfo("SIGNAL HANDLER : %d",signum);  //print info message using logger utility
}

void setSignalHandler() {
    struct sigaction actions;

    memset(&actions, 0, sizeof(actions));
    sigemptyset(&actions.sa_mask);
    actions.sa_flags = 0;
    actions.sa_handler = signalHandler;

    int rc = sigaction(SIGUSR2,&actions,NULL);
    if(rc < 0) {
        LogError("Error in sigaction");  //print error using logger utility
    }
}

void stopThread() {
    fStop = 1;  //set the flag to stop the thread
    if( ftid ) { //ftid is pthread_t variable of thread that needs to be terminated.
        LogInfo("Before pthread_kill()");
        int kill_result = pthread_kill(ftid,SIGUSR2);  // send signal to interrupt the thread if blocked for poll() I/O
        LogInfo("pthread_kill() returned : %d ( %s )",kill_result,strerror(kill_result));
        int result = pthread_join( ftid, NULL );
        LogInfo("Event loop stopped for %s", fStreamName);
        LogInfo( "pthread_join() -> %d ( %s )\n", result, strerror(result) );
        if( result == 0 ) ftid = 0;
    }
}

最近我注意到一個死鎖問題,其中幾個線程(試圖記錄語句)被下面的stacktrace阻塞

#0  0x00007fc1b2dc565c in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007fc1b2d70f0c in _L_lock_2462 () from /lib64/libc.so.6
#2  0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6
#3  0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()

調用LogInfo()LogError()函數時,從我們的記錄器模塊調用getCurrentTimestamp(char*)函數。 此函數在內部調用gettimeofday()以在日志中打印當前時間。 所以我懷疑在signalHandler()內部調用的LogInfo()函數(調用gettimeofday() )可能會導致死鎖問題。 但我對此沒有信心,因為這個問題沒有經常復制,我沒有得到任何信息,說明gettimeofday()不是異步信號安全。

gettimeofday()是線程安全的 ,我認為它不是異步信號安全,因為它沒有在異步信號安全功能下列出

1)我可以認為gettimeofday()不是異步信號安全,它是死鎖的根本原因嗎?

2)是否存在使用pthread_kill()可能導致死鎖的已知問題?

編輯:

下面是getCurrentTimeStamp()函數的定義

char* Logger::getCurrentTimestamp ( char *tm_buff ) {
    char curr_time[ 32 ] = { 0 };
    time_t current = time( NULL );
    struct timeval detail_time;

    if ( tm_buff ) {
        strftime( curr_time, sizeof(curr_time), "%Y-%m-%d-%H:%M:%S", localtime( &current ) );
        gettimeofday( &detail_time, NULL );
        sprintf( tm_buff, "%s:%03d", curr_time, (int) (detail_time.tv_usec / 1000) );
        return (tm_buff);
    }
    return (NULL);
}

堆棧被信號中斷的線程的跟蹤。

#0  0x00007fc1b2dc565c in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007fc1b2d70f0c in _L_lock_2462 () from /lib64/libc.so.6
#2  0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6
#3  0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()
#4  0x000000000046e80f in Log::write(Logger*, LogType, std::string const&, char const*) ()
#5  0x000000000046df74 in Log::i(Logger*, char const*, std::string const&, std::string const&, char const*, ...) ()
#6  0x0000000000456b67 in ?? ()
#7  <signal handler called>
#8  0x00007fc1b2da8dc5 in _xstat () from /lib64/libc.so.6
#9  0x00007fc1b2d71056 in __tzfile_read () from /lib64/libc.so.6
#10 0x00007fc1b2d703b4 in tzset_internal () from /lib64/libc.so.6
#11 0x00007fc1b2d70d63 in __tz_convert () from /lib64/libc.so.6
#12 0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()
#13 0x000000000047030e in Logger::writeLog(int, std::string&, std::string&) ()
#14 0x0000000000470633 in Logger::Write(Logger*, int, std::string, std::string const&) ()
#15 0x000000000046eb02 in Log::write(Logger*, LogType, std::string const&, char const*) ()
#16 0x000000000046df74 in Log::i(Logger*, char const*, std::string const&, std::string const&, char const*, ...) ()

正如你已經發現的那樣, gettimeofday本身並不是信號安全的。

但是, “POSIX.1-2008將gettimeofday()標記為過時,建議使用clock_gettime(2)。” source )和clock_gettime信號安全的 (也是線程安全的),所以這可能是你的另一種選擇。

gettimeofday沒有被定義為async-signal-safe,但是如果你為第二個(時區)參數傳遞0,它就不需要做任何timeclock_gettime (兩者都是正式異步信號安全的)don'也可以這樣做,所以在這種情況下它應該是異步信號安全的QoI。

你的死鎖是在內部libc函數__tz_convert

#2  0x00007fc1b2d70d47 in __tz_convert () from /lib64/libc.so.6
#3  0x00000000004708f7 in Logger::getCurrentTimestamp(char*) ()

它似乎是直接從Logger::getCurrentTimestamp調用的,但那是因為它是從文檔化的API函數中“尾調用”的。 GNU libc中只有四個函數調用__tz_convert (我使用了grepped源代碼): localtimelocaltime_rgmtimegmtime_r 因此,您的問題不是您正在調用gettimeofday ,而是您正在調用其中一個函數。

localtimegmtime顯然不是異步信號安全的,因為它們寫入全局變量。 localtime_rgmtime_r也不是異步信號安全的,因為他們必須查看時區信息的全局數據庫(是的,即使gmtime_r這樣做 - 也許可以改變它而不需要這樣做,但它仍然不會這是你可以依賴跨平台的事情。

我認為沒有一個好的解決方法。 來自異步信號處理程序的格式化輸出將會遇到各種其他問題; 我的建議是重構您的代碼,以便您永遠不需要從異步信號處理程序調用日志記錄功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM