【问题标题】:Does linux allow any system call to be made from signal handlers?linux 是否允许从信号处理程序进行任何系统调用?
【发布时间】:2012-07-25 09:05:12
【问题描述】:

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

【问题讨论】:

标签: c linux signals


【解决方案1】:

根据section 2 signal manual

请参阅 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 确实只允许一些系统调用,但不是全部。所以你的问题的答案很简单——不。

【讨论】:

  • 这似乎是 posix 而不是 linux 特定的。我知道 linux 是(应该是) posix 兼容的,所以我认为这至少意味着这些功能是安全的,我想了解是否还有其他功能(特别是 mprotect)。跨度>
  • @gct:这是 Linux 特定的手册页。这是您查看源代码后可以获得的最准确的信息。如果您想浏览源代码并进行更好的分析...去吧:)
  • @gct:顺便说一句,有一个更好的方法来处理没有这个限制的信号——你必须使用epollsignalfd。然后你可以在处理程序中做任何你想做的事情,见kernel.org/doc/man-pages/online/pages/man2/signalfd.2.html
  • @Vlad:epoll 违背了整个目的,因为在等待信号时你不能做任何其他事情(因为它是那些导致信号的“其他事情”,它永远不会发生)。 ..
  • 问题是这个列表是从 POSIX 标准逐字复制的,不是 Linux 特定的——即使它出现在 Linux 手册页中。据推测,Linux 有其他异步安全方法——尤其是像 gettid()(POSIX 的类似物 getpid)这样的方法,它们在 POSIX 中不存在——但不清楚从哪里获取这些信息。大多数手册页没有指定方法是否是异步安全的。
【解决方案2】:

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

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

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

如果您的问题是:SIGSEGV 处理程序可以通过mprotectmmap 更改错误的地址映射吗? 那么我相信答案是是的(至少在 x86-64 和 x86-32 架构上),正如您引用的问题中的said here 一样,但我没有尝试。我读到这样做效率很低(SIGSEGV 处理速度不是很快,mprotectmmap 也有点慢)。特别是,模仿这种方式Hurd/Mach external pagers 可能效率低下。

【讨论】:

  • 系统调用在某种程度上是原子的,但是它们可以将控制权返回给用户代码,无论是从哪里调用它们,或者返回到一个信号处理程序。正常的行为是信号处理程序做某事,然后返回,然后被中断的系统调用返回 EINTR。 (我认为 glibc 可以再次调用它。)如果你从信号处理程序内部进行系统调用,我猜操作系统会排队进一步的信号,直到你完成。或者,如果您调用不在保证安全列表中的系统调用,则可以选择做一些奇怪的事情。
【解决方案3】:

是和否

是的:

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

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

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

否:

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

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

此外,SIGSEGV 处理程序的唯一可移植行为是中止或 退出,但我知道如果你 linux 实际上会恢复执行 返回,是吗?

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

【讨论】:

  • “您可以在信号处理程序中调用任何真实/原始系统调用”您是否有任何资源(例如内核文档、cmets)来支持该声明?
猜你喜欢
  • 2013-05-24
  • 2021-07-15
  • 1970-01-01
  • 2014-05-17
  • 2020-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-26
相关资源
最近更新 更多