我的理解是,一般来说,如果从信号处理程序调用非异步信号安全函数,则行为是未定义的,但我听说linux允许您安全地调用任何系统调用。这是真的?此外,SIGSEGV处理程序的唯一可移植行为是中止或退出,但我知道如果你返回,linux实际上会恢复执行,是吗?
答案 0 :(得分:19)
请参阅signal(7)以获取可以执行的异步信号安全功能列表 从信号处理程序内部安全地调用。
section 7 signals
manual列出了以下函数和/或系统调用以及非常清晰的描述:
异步信号安全功能
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 :(得分:7)
是,否
是:
您可以在信号处理程序中调用任何真实/原始系统调用。内核有责任确保它是安全的(在内核的视图中)。
1)内核不知道用户空间的上下文,或者说内核在传递信号后将状态保存到用户空间后故意忘记它。 (注意:执行恢复是由用户通过系统调用在保存状态的帮助下完成的,而不是内核,内核已经忘记了)
2)某些线程lib是通过单一实现的,因此线程已经在“信号处理程序”中,但这些线程可以调用任何系统调用。
NO:
但是用户空间功能有其自身的目的和副作用。有些不是re-entrance safe,无法从信号处理程序调用这些函数。 man 7 signal
会帮助您找出哪些是重新入场安全的。
举例来说,你可以在任何地方调用sys_futex()
,包括信号处理程序,但是如果使用sys_futex()
来实现互斥锁,那么sys_futex()
内部信号处理程序可能会在信号中断时被阻止互斥体的关键部分。
此外,SIGSEGV处理程序的唯一可移植行为是中止或 退出,但我知道如果你真的恢复执行linux 回来,是吗?
是的,如果你找不到原因。有些用户可能会将SIGSEGV用于他们自己的地图 - 当需要时(例如,在JIT中,您可以将SIGSEGV信号处理程序中的代码翻译并将已翻译的代码mmap到内存然后返回),他们可以调用mmap()或mprotect ()......等。
答案 2 :(得分:5)
我相信任何真正的系统调用都可以从信号处理程序中调用。真正的系统调用具有<asm/unistd.h>
(或<asm/unistd_64.h>
)中的数字。
手册页第2节中的一些posix函数是通过“多路复用”系统调用来实现的,所以它们不是我所说的“真正的系统调用”
从应用程序的角度来看,系统调用是一种原子操作;它几乎就像一台机器指令(来自应用程序内部)。请参阅this answer。
如果您的问题是: SIGSEGV
处理程序是否可以通过mprotect
或mmap
?更改错误地址映射,那么我相信答案是是的(至少在x86-64&amp; x86-32架构上),在你引用的问题中为said here,但我没试过。我已经读过这样做非常低效(SIGSEGV
处理速度不是很快,mprotect
或mmap
也有点慢。特别是,模仿这种方式Hurd/Mach external pagers可能效率低下。