linux信号处理程序中的malloc导致死锁

时间:2016-10-14 18:22:34

标签: c linux signals rhel

首先很抱歉在信号处理程序里面调用malloc :)。我也明白我们不应该在信号处理程序中做任何耗费任务/这种讨厌的事情。

但我很想知道它崩溃的原因?

 #0  0x00006e3ff2b60dce in _lll_lock_wait_private () from /lib64/libc.so.6
 #1  0x00006e3ff2aec138 in _L_lock_9164 () from /lib64/libc.so.6
 #2  0x00006e3ff2ae9a32 in malloc () from /lib64/libc.so.6
 #3  0x00006e3ff1f691ad in ?? () from ..

我在https://access.redhat.com/solutions/48701报告了类似的核心。

操作系统:RHEL

4 个答案:

答案 0 :(得分:6)

malloc()不是可以从信号处理程序安全调用的函数。它不是异步信号安全功能。 所以,你永远不应该从信号处理程序调用malloc()。您只能从信号处理程序中调用一组有限的功能。 有关可以从信号处理程序安全调用的函数列表,请参阅man signal-safety

查看您的GDB输出,看来当malloc()持有锁时,您再次调用malloc()会导致死锁。

答案 1 :(得分:4)

只能从信号处理程序中安全地调用异步信号安全函数。

the POSIX standard

  

以上[下面复制]表格中的任何功能都可能不安全   信号。实现可以使其他接口异步信号安全。   在存在信号的情况下,由此卷定义的所有函数   POSIX.1-2008在调用或中断时应按行为定义   通过信号捕捉功能,除了当一个信号   中断不安全的功能或等效功能(如处理   相当于从初始调用返回后执行的exit()   到main())和信号捕获函数调用一个不安全的函数,   行为未定义。其他例外情况在   各个功能的描述,例如longjmp()

如果从信号处理程序中调用“不安全的函数”,则“行为未定义”。

Linux signal.7 man page州:

  

异步信号安全功能

     

自处理以来,信号处理函数必须非常小心          其他地方可能会在执行中的某个任意点中断          该计划。 POSIX具有“安全功能”的概念。如果一个          信号中断执行不安全的函数和处理程序          要么调用不安全的函数,要么通过调用来终止处理程序          longjmp()或siglongjmp(),程序随后调用          不安全的函数,那么程序的行为是不确定的。

Linux手册页提供了Linux上的async-signal-safe函数列表。它们可能与POSIX规范中列出的不同 - 我没有对它们进行比较,标准和实现确实会随着时间的推移而发生变化。 上面第一句中POSIX“上表”中的“安全功能”仅包含以下功能:

_Exit()
_exit()
abort()
accept()
access()
aio_error()
aio_return()
aio_suspend()
alarm()
bind()
cfgetispeed()
cfgetospeed()
cfsetispeed()
cfsetospeed()
chdir()
chmod()
chown()
clock_gettime()
close()
connect()
creat()
dup()
dup2()
execl()
execle()
execv()
execve()
faccessat()
fchdir()
fchmod()
fchmodat()
fchown()
fchownat()
fcntl()
fdatasync()
fexecve()
ffs()
fork()
fstat()
fstatat()
fsync()
ftruncate()
futimens()
getegid()
geteuid()
getgid()
getgroups()
getpeername()
getpgrp()
getpid()
getppid()
getsockname()
getsockopt()
getuid()
htonl()
htons()
kill()
link()
linkat()
listen()
longjmp()
lseek()
lstat()
memccpy()
memchr()
memcmp()
memcpy()
memmove()
memset()
mkdir()
mkdirat()
mkfifo()
mkfifoat()
mknod()
mknodat()
ntohl()
ntohs()
open()
openat()
pause()
pipe()
poll()
posix_trace_event()
pselect()
pthread_kill()
pthread_self()
pthread_sigmask()
raise()
read()
readlink()
readlinkat()
recv()
recvfrom()
recvmsg()
rename()
renameat()
rmdir()
select()
sem_post()
send()
sendmsg()
sendto()
setgid()
setpgid()
setsid()
setsockopt()
setuid()
shutdown()
sigaction()
sigaddset()
sigdelset()
sigemptyset()
sigfillset()
sigismember()
siglongjmp()
signal()
sigpause()
sigpending()
sigprocmask()
sigqueue()
sigset()
sigsuspend()
sleep()
sockatmark()
socket()
socketpair()
stat()
stpcpy()
stpncpy()
strcat()
strchr()
strcmp()
strcpy()
strcspn()
strlen()
strncat()
strncmp()
strncpy()
strnlen()
strpbrk()
strrchr()
strspn()
strstr()
strtok_r()
symlink()
symlinkat()
tcdrain()
tcflow()
tcflush()
tcgetattr()
tcgetpgrp()
tcsendbreak()
tcsetattr()
tcsetpgrp()
time()
timer_getoverrun()
timer_gettime()
timer_settime()
times()
umask()
uname()
unlink()
unlinkat()
utime()
utimensat()
utimes()
wait()
waitpid()
wcpcpy()
wcpncpy()
wcscat()
wcschr()
wcscmp()
wcscpy()
wcscspn()
wcslen()
wcsncat()
wcsncmp()
wcsncpy()
wcsnlen()
wcspbrk()
wcsrchr()
wcsspn()
wcsstr()
wcstok()
wmemchr()
wmemcmp()
wmemcpy()
wmemmove()
wmemset()
write()

答案 2 :(得分:0)

malloc的实现可能会抓住内部glibc锁定。我们知道信号处理程序是异步调用的。如果正常执行期间的线程有malloc&d;并且被中断以处理信号,那么如果信号处理函数使用malloc则会出现问题。信号处理程序malloc会尝试获取锁,但它不可用,因为同一个线程在正常执行期间获得了它。你有僵局。因此,信号处理程序应该是精简的,不应该调用非AS安全功能。

答案 3 :(得分:0)

直接回答OP的问题。一些glibc包装器(例如malloc arenas和printf文件访问)使用低级锁定来实现并发。信号处理程序进入函数调用,抓取" lll _",中断,重新进入函数调用和死锁。

可能的解决方案: 1)上面已经讨论过第一个 2)不要使用glibc包装器 - 直接进入内核系统调用。例如。不要使用printf,请使用write。不要使用glibc malloc,使用系统调用(sbrk ...) - 可能不是一个好主意,除非你真的不得不...... 3)不要在处理程序中进行任何动态内存分配,在主任务中分配它并在处理程序中访问它

相关问题