获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗

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

本文关键字:死锁 信号处理 信号 异步 取日期 安全 获取 如果 程序      更新时间:2023-10-16

以前我曾问过一个关于如何终止因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;
}
}

最近,我注意到一个死锁问题,少数线程(试图记录语句(被以下堆栈跟踪阻塞

#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(。">(源代码(和clock_gettime是信号安全的(也是线程安全的(,因此这可能是您的替代方案。

gettimeofday没有被定义为异步信号安全,但如果第二个(时区(参数传递0,它就不必做timeclock_gettime(这两个都是官方的异步信号安全(没有做的任何事,所以就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(我编译了源代码(:localtimelocaltime_rgmtimegmtime_r。因此,问题不在于您在调用gettimeofday,而在于您正在调用其中一个函数。

CCD_ 26和CCD_ 27显然不是异步信号安全的,因为它们写入全局变量。localtime_rgmtime_r也不是异步信号安全的,因为它们必须查看时区信息的全局数据库(是的,即使gmtime_r也这样做——可能会将其更改为不需要这样做,但这仍然不是一件可以跨平台依赖的事情(。

我认为没有好的变通办法。异步信号处理程序的格式化输出会遇到其他各种问题;我的建议是重新构造代码,这样就不需要从异步信号处理程序调用日志记录函数。