Linux允许从信号处理程序进行任何系统调用吗?

我的理解是,一般来说,如果您从信号处理程序中调用非asynchronous信号安全函数,行为是不确定的,但是我听说linux允许您安全地调用任何系统调用。 这是真的? 另外,SIGSEGV处理程序的唯一可移植行为是中止或退出,但是我明白,如果返回true,那么linux实际上会恢复执行。

Solutions Collecting From Web of "Linux允许从信号处理程序进行任何系统调用吗?"

我相信任何真正的系统调用都可以从信号处理程序调用。 一个真正的系统调用在<asm/unistd.h> (或<asm/unistd_64.h> )中有一个数字。

手册第2部分的一些posix函数是通过一个“多路复用”系统调用来实现的,所以在我的意义上它们不是“真正的系统调用”

系统调用是从应用程序的角度来看的一个原子操作; 它几乎就像一个机器指令(来自应用程序内部)。 看到这个答案 。

如果你的问题是: SIGSEGV处理程序可以通过mprotectmmap更改错误的地址映射吗? 那么我相信答案是肯定的 (至少在x86-64和x86-32架构上),正如你引用的一个问题所说的那样,但是我没有尝试。 我读过这样做效率非常低( SIGSEGV处理不是很快, mprotectmmap也有点慢)。 特别是,模仿赫德/马赫的外部传呼机可能效率低下。

根据第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只允许一些系统调用,但不是全部。 所以你的问题的答案是简单的 – 不。

是的,没有

是:

您可以调用信号处理程序中的任何真实/原始系统调用。 内核有责任确保它是安全的(在内核中)。

1)内核不知道用户空间的上下文,或者在传递信号时将内核状态保存到用户空间后,内核会故意忘记它。 (注意:执行恢复是由用户通过系统调用在保存状态的帮助下完成的,而不是内核,内核已经忘记了)

2)一些线程库是通过单线程实现的,所以线程已经在“信号处理程序”中,但是这些线程可以调用任何系统调用。

没有:

但用户空间功能有其自身的目的和副作用。 有些不能重新进入安全 ,这些功能不能从信号处理程序调用。 man 7 signal将帮助你找出哪些重新进入安全。

举个例子,你可以在任何地方调用sys_futex() ,包括信号处理程序,但是如果你使用sys_futex()来实现一个互斥体,信号处理程序中的sys_futex()信号处理程序可能被阻塞,直到信号中断互斥体的关键部分。

另外,SIGSEGV处理程序的唯一可移植行为是中止或退出,但是我明白,如果返回true,那么linux实际上会恢复执行。

是的,如果你找不到原因。 有些用户可能会使用SIGSEGV来实现自己的map-when-requested目的(例如,在JIT中,您可以在SIGSEGV信号处理程序中翻译代码,并将翻译的代码mmap到内存然后返回),则可以调用mmap()或mprotect ()…等。