2016-10-14 3 views
3

Прежде всего извините за вызов malloc внутри обработчика сигнала :). Я тоже понимаю, что мы не должны делать сколько-нибудь трудоемкую задачу/этот вид неприятных вещей внутри обработчика сигнала.malloc in linux обработчик сигнала вызывает тупик

Но мне любопытно узнать причину, почему она разбилась?

#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

+2

Я не верю, что 'malloc()' считается повторным, и поэтому небезопасно вызывать обработчик сигнала. См. 'Man 7 signal' в разделе« Безопасные функции сигнала Async », чтобы узнать, что разрешено. Вообще говоря, если это не в этом списке, вероятно, небезопасно звонить из обработчика сигнала. На самом деле, это даже рекомендация в документе, который вы связали. – twalberg

+0

связанных: https://stackoverflow.com/questions/3366307/why-is-malloc-not-async-signal-safe –

ответ

5

malloc() не является функцией, которая может быть безопасно вызвана из обработчика сигнала. Это не функция защиты от асинхронного сигнала. Итак, вы никогда не должны вызывать malloc() из обработчика сигнала. Вы можете разрешать только ограниченный набор функций из обработчика сигнала. См. man signal для списка функций, которые вы можете безопасно вызывать из обработчика сигнала.

Глядя на ваш выход GDB, оказывается, что пока malloc() удерживает блокировку, вы снова вызываете malloc(), что приводит к тупику.

3

С помощью обработчика сигналов можно безопасно вызывать только функции безопасности с использованием асинхронного сигнала.

Пер the POSIX standard:

Любая функция не в приведенном выше [реплицированного ниже] таблицы может быть небезопасно относительно сигналов. Реализации могут сделать другие интерфейсы безопасными для асинхронных сигналов. При наличии сигналов все функции, определяемые этим томом POSIX.1-2008, должны вести себя так, как определено при вызове или прерывании с помощью функции захвата сигнала, за исключением того, что когда сигнал прерывает небезопасную функцию или эквивалентный (например, обработка , эквивалентная exit(), выполненная после возврата от начального вызова к main()) и , функция захвата сигнала вызывает небезопасную функцию, поведение не определено. Дополнительные исключения указаны в описании отдельных функций, таких как longjmp().

Если вы вызываете «небезопасную функцию» из обработчика сигнала, «поведение не определено».

В Linux signal.7 man page состояния:

Асинхронный-сигнал безопасные функции

Функция обработчик сигнала должен быть очень осторожным, так как обработка в другом месте может быть прервано в некоторой произвольной точке выполнения программы. POSIX имеет понятие «безопасная функция». Если сигнал прерывает выполнение небезопасной функции, а обработчик либо вызывает небезопасную функцию, либо обработчик завершает вызов по вызову longjmp() или siglongjmp(), и программа впоследствии вызывает небезопасную функцию , тогда поведение программа не определена.

Личная страница Linux предоставляет список функций, поддерживающих синхронизацию по сигналу async, в Linux. Они могут отличаться от перечисленных в спецификации 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() 
0

Реализация malloc может быть захватывая внутренний замок GLibC. Мы знаем, что обработчики сигналов называются асинхронно. Если поток при нормальном выполнении имел malloc 'd и был прерван для обработки сигнала, у нас возникает проблема, если функция обработчика сигналов использует malloc. Обработчик сигналов malloc попытается получить замок, но он недоступен, потому что тот же поток получил его во время его нормального выполнения. И вы зашли в тупик. Именно по этой причине обработчики сигналов должны быть тонкими, и не следует вызывать функцию без AS-safe.

0

Чтобы прямо ответить на вопрос о OP. Некоторые оболочки glibc (например, malloc arenas и доступ к файлам printf) используют блокировку низкого уровня для параллелизма. Обработчик сигнала вводит вызов функции, захватывает «lll_», прерывается, повторно вводит вызов функции и блокировки.

Возможные решения: 1) первый уже обсуждался выше 2) не используйте обертки glibc - идите прямо к системному вызову ядра. Например. не используйте printf, используйте write. Не используйте glibc malloc, используйте syscall (sbrk ...) - возможно, это не очень хорошая идея, если только вы НЕ ДОЛЖНЫ ... 3) НЕ ЛЮБИТЕ динамическое выделение памяти в обработчике, выделите его в основной задаче и получить доступ к нему в обработчике

Смежные вопросы