寻找有关僵局情况的指导 [英] Looking for guidance on a deadlock scenario
问题描述
我有一个程序,该程序可以产生许多孩子并可以长时间运行。该程序包含一个SIGCHLD处理程序以获取已终止的进程。有时,该程序冻结。我相信pstack表示出现死锁情况。
I have a program that spawns lots of children and runs for long periods of time. The program contains a SIGCHLD handler to reap defunct processes. Occasionally, this program freezes. I believe that pstack is indicating a deadlock scenario. Is that the proper interpretation of this output?
10533: ./asyncsignalhandler
ff3954e4 lwp_park (0, 0, 0)
ff391bbc slow_lock (ff341688, ff350000, 0, 0, 0, 0) + 58
ff2c45c8 localtime_r (ffbfe7a0, 0, 0, 0, 0, 0) + 24
ff2ba39c __posix_ctime_r (ffbfe7a0, ffbfe80e, ffbfe7a0, 0, 0, 0) + c
00010bd8 gettimestamp (ffbfe80e, ffbfe828, 40, 0, 0, 0) + 18
00010c50 sig_chld (12, 0, ffbfe9f0, 0, 0, 0) + 30
ff3956fc __sighndlr (12, 0, ffbfe9f0, 10c20, 0, 0) + c
ff38f354 call_user_handler (12, 0, ffbfe9f0, 0, 0, 0) + 234
ff38f504 sigacthandler (12, 0, ffbfe9f0, 0, 0, 0) + 64
--- called from signal handler with signal 18 (SIGCLD) ---
ff391c14 pthread_mutex_lock (20fc8, 0, 0, 0, 0, 0) + 48
ff2bcdec getenv (ff32a9ac, 770d0, 0, 0, 0, 0) + 1c
ff2c6f40 getsystemTZ (0, 79268, 0, 0, 0, 0) + 14
ff2c4da8 ltzset_u (4ede65ba, 0, 0, 0, 0, 0) + 14
ff2c45d0 localtime_r (ffbff378, 0, 0, 0, 0, 0) + 2c
ff2ba39c __posix_ctime_r (ffbff378, ffbff402, ffbff378, ff33e000, 0, 0) + c
00010bd8 gettimestamp (ffbff402, ffbff402, 2925, 29a7, 79c38, 10b54) + 18
00010ae0 main (1, ffbff4ac, ffbff4b4, 20c00, 0, 0) + 190
00010928 _start (0, 0, 0, 0, 0, 0) + 108
我不太喜欢C编码器,也不熟悉这种语言的细微差别。我专门在程序中使用可重入版本的ctime(_r)。为什么它仍然处于僵局?
I don't really fancy myself a C coder and am not familiar with the nuances of the language. I'm specifically using the re-entrant version of ctime(_r) in the program. Why is this still deadlocking?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// import pid_t type
#include <sys/types.h>
// import _exit function
#include <unistd.h>
// import WNOHANG definition
#include <sys/wait.h>
// import errno variable
#include <errno.h>
// header for signal functions
#include <signal.h>
// function prototypes
void sig_chld(int);
char * gettimestamp(char *);
// begin
int main(int argc, char **argv)
{
time_t sleepstart;
time_t sleepcheck;
pid_t childpid;
int i;
unsigned int sleeptime;
char sleepcommand[20];
char ctime_buf[26];
struct sigaction act;
/* set stdout to line buffered for logging purposes */
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
/* Assign sig_chld as our SIGCHLD handler */
act.sa_handler = sig_chld;
/* We don't want to block any other signals */
sigemptyset(&act.sa_mask);
/*
* We're only interested in children that have terminated, not ones
* which have been stopped (eg user pressing control-Z at terminal)
*/
act.sa_flags = SA_NOCLDSTOP;
/* Make these values effective. */
if (sigaction(SIGCHLD, &act, NULL) < 0)
{
printf("sigaction failed\n");
return 1;
}
while (1) {
for (i = 0; i < 20; i++) {
/* fork/exec child program */
childpid = fork();
if (childpid == 0) // child
{
//sleeptime = 30 + i;
sprintf(sleepcommand, "sleep %d", i);
printf("\t[%s][%d] Executing /bin/sh -c %s\n", gettimestamp(ctime_buf), getpid(), sleepcommand);
execl("/bin/sh", "/bin/sh", "-c", sleepcommand, NULL);
// only executed if exec fails
printf("[%s][%d] Error executing program, errno: %d\n", gettimestamp(ctime_buf), getpid(), errno);
_exit(1);
}
else if (childpid < 0) // error
{
printf("[%s][%d] Error forking, errno: %d\n", gettimestamp(ctime_buf), getpid(), errno);
}
else // parent
{
printf("[%s][%d] Spawned child, pid: %d\n", gettimestamp(ctime_buf), getpid(), childpid);
}
}
// sleep is interrupted by SIGCHLD, so we can't simply sleep(5)
printf("[%s][%d] Sleeping for 5 seconds\n", gettimestamp(ctime_buf), getpid());
time(&sleepstart);
while (1) {
time(&sleepcheck);
if (difftime(sleepcheck, sleepstart) < 5) {
sleep(1);
} else {
break;
}
}
}
return(0);
}
char * gettimestamp(char *ctime_buf)
{
time_t now;
time(&now);
// format the timestamp and chomp the newline
ctime_r(&now, ctime_buf);
ctime_buf[strlen(ctime_buf) - 1] = '\0';
return ctime_buf;
}
/*
* The signal handler function -- only gets called when a SIGCHLD
* is received, ie when a child terminates.
*/
void sig_chld(int signo)
{
pid_t childpid;
int childexitstatus;
char ctime_buf[26];
while (1) {
childpid = waitpid(-1, &childexitstatus, WNOHANG);
if (childpid > 0)
printf("[%s][%d] Reaped child, pid: %d, exitstatus: %d\n", gettimestamp(ctime_buf), getpid(), childpid, WEXITSTATUS(childexitstatus));
else
return;
}
}
我正在Solaris 9环境中运行。该程序是使用以下语法在Sun WorkShop 6 update 2 C 5.3补丁111679-15 2009/09/10中编译的:
I'm running in a Solaris 9 environment. The program was compiled with Sun WorkShop 6 update 2 C 5.3 Patch 111679-15 2009/09/10 using the following syntax:
cc -o asyncsignalhandler asyncsignalhandler.c -mt -D_POSIX_PTHREAD_SEMANTICS
程序是否存在缺陷?
推荐答案
您是否在调用信号处理程序中的更好方法(带有时间戳)?信号安全(请参见Unix规范的第2.4.3节 )从信号处理程序中-在这种情况下, ctime_r()
和 printf()
(死锁似乎是发生的原因是您显示的堆栈跟踪中的 ctime_r()
使用了一个锁)。这些函数可能带有锁,并且由于可以随时调用信号处理程序,因此该锁可能已经被持有,从而导致死锁。
You are calling functions that are not async-signal-safe (see section 2.4.3 of the unix spec) from within a signal handler - in this case, ctime_r()
and printf()
(the deadlock appears to be happening due to a lock used by ctime_r()
in the stacktrace you show). These functions may take locks, and since a signal handler may be invoked at any moment, the lock may already be held, resulting in a deadlock.
通常,在信号中处理程序,您要做的就是记下主线程以供以后检查。例如,您可以将 write()
(这是异步信号安全函数)添加到 pipe()
-创建的文件描述符,并让您的主循环(或另一个线程)进行选择循环,以等待某些数据显示在该管道上。
Generally, in a signal handler, all you should so is make a note for the main thread to examine later. For example, you could write()
(which is an async-signal-safe function) to a pipe()
-created file descriptor, and have your main loop (or another thread) doing a select loop to wait for some data to show up on that pipe.
还请注意 thread-safe 与 async-signal-safe 不同。 ctime_r
是线程安全的-它需要锁定以确保线程不会彼此踩踏,并且使用传入的缓冲区而不是静态缓冲区。但这不是异步信号安全的,因为它不能容忍在执行过程中的任何任意点被再次进入。
Note also that thread-safe is not the same as async-signal-safe. ctime_r
is thread safe - it takes locks to ensure threads don't step on each other, and it uses a passed-in buffer rather than a static buffer. But it's not async-signal-safe, because it can't tolerate being called reentrantly at any arbitrary point in its execution.
这篇关于寻找有关僵局情况的指导的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!