Я понимаю, что в целом поведение undefined, если вы вызываете функцию неасинхронного сигнала с помощью обработчика сигнала, но я слышал, что linux позволяет безопасно звонить на любой системный вызов. Это правда? Кроме того, единственным переносимым поведением для обработчика SIGSEGV является прервать или выйти, но я понимаю, что linux действительно возобновит выполнение, если вы вернетесь, true?
Линукс разрешает любой системный вызов из обработчиков сигналов?
Ответ 1
Я бы поверил, что любой реальный системный вызов можно вызвать из обработчика сигнала. Истинный syscall имеет число в <asm/unistd.h>
(или <asm/unistd_64.h>
).
некоторые функции posix из раздела 2 справочных страниц реализованы через "мультиплексирование" syscall, поэтому они не являются "истинными syscalls" в моем смысле
Системный вызов - это атомная операция с точки зрения приложения; это почти как отдельная машинная инструкция (изнутри приложения). См. этот ответ.
Если ваш вопрос: может ли обработчик SIGSEGV
изменить неправильное сопоставление адресов через mprotect
или mmap
? то я считаю, что ответ да (по крайней мере, на архитектуре x86-64 и x86-32), поскольку сказал здесь в вопросе, который вы цитировали, но я не пробовал. Я читал, что это довольно неэффективно (обработка SIGSEGV
не очень быстро, а mprotect
или mmap
также немного медленнее). В частности, имитируя этот способ, внешние пэды Hurd/Mach могут быть неэффективными.
Ответ 2
Согласно разделу 2 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 разрешает только некоторые системные вызовы, но не все из них. Так что ответ на ваш вопрос просто - нет.
Ответ 3
Да и НЕТ
Да:
Вы можете вызвать любой реальный/необработанный syscall внутри обработчика сигнала. Ядро несет ответственность за обеспечение безопасности (с точки зрения ядра).
1) Ядро не знает контекста пользовательского пространства или говорит, что ядро умышленно забывает его после того, как оно сохраняет состояние в пользовательском пространстве при подаче сигнала. (ПРИМЕЧАНИЕ: возобновление выполнения выполняется пользователем через syscall с помощью сохраненных состояний, а не на самом ядре, ядро уже забыто)
2) некоторые потоки lib реализуются через синглы, поэтому потоки уже находятся в "обработчике сигналов", но эти потоки могут вызывать любой syscall.
NO:
Но функции пользовательского пространства имеют свои собственные цели и побочные эффекты. Некоторые из них не являются re-entry safe, эти функции нельзя вызывать из обработчика сигналов. man 7 signal
поможет вам узнать, какие из них безопасны для повторного входа.
Например, вы можете вызывать sys_futex()
где угодно, включая обработчик сигналов, но если вы используете sys_futex()
для реализации мьютекса, внутренний обработчик сигнала sys_futex()
может заблокироваться навсегда, когда сигнал прерывает критический раздел мьютекса.
Кроме того, единственным переносимым поведением для обработчика SIGSEGV является прерывание или exit, но я понимаю, что linux фактически возобновит выполнение, если вы return, true?
Да, если вы не можете узнать причину. Некоторые пользователи могут использовать SIGSEGV для своей собственной цели по карте (например, в JIT, вы можете перевести код в обработчике сигнала SIGSEGV и переместить код в память, а затем вернуться), они могут вызывать mmap() или mprotect()...и т.д.