c-linux是否允许从信号处理程序进行任何系统调用



我的理解是,通常情况下,如果您从信号处理程序调用非异步信号安全函数,则行为是未定义的,但我听说linux允许您安全地调用任何系统调用。这是真的吗?此外,SIGSEGV处理程序的唯一可移植行为是中止或退出,但我知道如果返回,linux实际上会恢复执行,对吗?

根据signal手册第2节:

有关可以从信号处理程序内部安全地调用。

signals手册第7节列出了以下功能和/或系统调用,并给出了非常清晰的描述:

异步信号安全功能

   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) 一些线程库是通过single实现的,因此线程已经在"信号处理程序"中,但这些线程可以调用任何syscall。

否:

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

例如,您可以在包括信号处理程序在内的任何位置调用sys_futex(),但如果使用sys_futex()来实现互斥,则当信号中断互斥的关键部分时,信号处理程序内部的sys_futex()可能会永远被阻塞。

此外,SIGSEGV处理程序的唯一可移植行为是中止或退出,但我知道如果您返回,真的吗?

是的,如果你找不出原因的话。一些用户可能会在需要时将SIGSEGV用于自己的映射(例如,在JIT中,您可以在SIGSEGV信号处理程序中翻译代码,并将翻译后的代码mmap到内存中,然后返回),他们可以调用mmap()或mprotect()。。。等等

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

手册页第2节中的一些posix函数是通过"多路复用"系统调用实现的,因此在我看来它们不是"真正的系统调用"

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

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

相关内容

最新更新