Позволяет ли linux выполнять какие-либо системные вызовы из обработчиков сигналов?
Насколько я понимаю, в общем, поведение не определено, если вы вызываете не асинхронную безопасную функцию сигнала из обработчика сигнала, но я слышал, что linux позволяет безопасно вызывать любой системный вызов. Это правда? Кроме того, единственное переносимое поведение для обработчика SIGSEGV - это прерывание или выход, но я понимаю, что linux фактически возобновит выполнение, если вы вернетесь, верно?
3 ответа
Я полагаю, что любой реальный системный вызов может быть вызван из обработчика сигнала. Настоящий системный вызов имеет номер в <asm/unistd.h>
(или же <asm/unistd_64.h>
).
некоторые функции posix из раздела 2 man-страниц реализованы с помощью системного вызова "мультиплексирования", так что в моем смысле они не являются "истинными системными вызовами"
Системный вызов - это атомарная операция с точки зрения приложения; это почти как отдельная машинная инструкция (изнутри приложения). Смотрите этот ответ.
Если ваш вопрос: может ли SIGSEGV
обработчик изменяет неверное сопоставление адресов через mprotect
или же mmap
? тогда я считаю, что ответ " да" (по крайней мере, для архитектур x86-64 и x86-32), как сказано здесь в вопросе, который вы цитировали, но я не пытался. Я читал, что делать это довольно неэффективно (SIGSEGV
обработка не очень быстрая, и mprotect
или же mmap
тоже немного медленно). В частности, имитировать таким образом внешние пейджеры Херда / Маха могут быть неэффективными.
Согласно разделу 2 signal
руководство:
См. Signal(7) для получения списка асинхронно-безопасных функций, которые можно безопасно вызывать из обработчика сигнала.
И раздел 7 signals
В руководстве перечислены следующие функции и / или системные вызовы вместе с довольно четким описанием:
Асинхронно-сигнальные безопасные функции
A signal handler function must be very careful, since processing elsewhere may
be interrupted at some arbitrary point in the execution of the program. POSIX
has the concept of "safe function". If a signal interrupts the execution of
an unsafe function, and handler calls an unsafe function, then the behavior of
the program is undefined.
POSIX.1-2004 (also known as POSIX.1-2001 Technical Corrigendum 2) requires an
implementation to guarantee that the following functions can be safely called
inside a signal handler:
_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()
execle()
execve()
fchmod()
fchown()
fcntl()
fdatasync()
fork()
fpathconf()
fstat()
fsync()
ftruncate()
getegid()
geteuid()
getgid()
getgroups()
getpeername()
getpgrp()
getpid()
getppid()
getsockname()
getsockopt()
getuid()
kill()
link()
listen()
lseek()
lstat()
mkdir()
mkfifo()
open()
pathconf()
pause()
pipe()
poll()
posix_trace_event()
pselect()
raise()
read()
readlink()
recv()
recvfrom()
recvmsg()
rename()
rmdir()
select()
sem_post()
send()
sendmsg()
sendto()
setgid()
setpgid()
setsid()
setsockopt()
setuid()
shutdown()
sigaction()
sigaddset()
sigdelset()
sigemptyset()
sigfillset()
sigismember()
signal()
sigpause()
sigpending()
sigprocmask()
sigqueue()
sigset()
sigsuspend()
sleep()
sockatmark()
socket()
socketpair()
stat()
symlink()
sysconf()
tcdrain()
tcflow()
tcflush()
tcgetattr()
tcgetpgrp()
tcsendbreak()
tcsetattr()
tcsetpgrp()
time()
timer_getoverrun()
timer_gettime()
timer_settime()
times()
umask()
uname()
unlink()
utime()
wait()
waitpid()
write()
POSIX.1-2008 removes fpathconf(), pathconf(), and sysconf() from the above
list, and adds the following functions:
execl()
execv()
faccessat()
fchmodat()
fchownat()
fexecve()
fstatat()
futimens()
linkat()
mkdirat()
mkfifoat()
mknod()
mknodat()
openat()
readlinkat()
renameat()
symlinkat()
unlinkat()
utimensat()
utimes()
Я считаю, что эта информация более надежна, чем то, что мы иногда слышим где-то. Таким образом, Linux допускает только некоторые системные вызовы, но не все из них. Так что ответ на ваш вопрос просто - нет.
И да и нет
Да:
Вы можете вызвать любой реальный / необработанный системный вызов внутри обработчика сигнала. Ядро несет ответственность за обеспечение его безопасности (по мнению ядра).
1) Ядро не знает контекст пользовательского пространства или говорит, что ядро намеренно забыло его после того, как оно сохранит состояние в пользовательском пространстве при доставке сигнала. (ПРИМЕЧАНИЕ: возобновление выполнения выполняется пользователем через системный вызов с помощью сохраненных состояний, а не ядром, ядро уже забыло)
2) некоторая библиотека потоков реализована через синглы, поэтому потоки уже находятся в "обработчике сигналов", но эти потоки могут вызывать любой системный вызов.
NO:
Но функции пространства пользователя имеют свое собственное назначение и побочный эффект. Некоторые не являются безопасными при повторном входе, эти функции нельзя вызвать из обработчика сигнала. man 7 signal
поможет вам узнать, какие из них безопасны при входе.
Возьмите пример, вы можете позвонить sys_futex()
в любом месте, включая обработчик сигнала, но если вы используете sys_futex()
реализовать мьютекс, sys_futex()
Внутренний обработчик сигнала может быть заблокирован навсегда, когда сигнал прерывает критическую секцию мьютекса.
Кроме того, единственное переносимое поведение для обработчика SIGSEGV - это прерывание или выход, но я понимаю, что linux фактически возобновит выполнение, если вы вернетесь, верно?
Да, если вы не можете выяснить причину. Некоторые пользователи могут использовать SIGSEGV для своих собственных целей сопоставления при запросе (например, в JIT вы можете преобразовать код в обработчике сигналов SIGSEGV и mmap преобразованный код в память, а затем вернуться), они могут вызвать mmap() или mprotect() ...так далее.