在用户空间中实现可取消的系统调用

我正在努力在Linux上实现pthread的取消,而没有任何“不愉快的行为”(有些人可能会说错误)在我的其他一些最近的问题中讨论。 到目前为止,用于pthread取消的Linux / glibc方法一直是将其视为不需要内核支持的方法,并且可以在库级别处理纯粹通过在进行系统调用之前启用asynchronous取消,并恢复先前的取消状态系统调用返回之后。 这至less有两个问题,其中一个非常严重:

  1. 取消可以在系统调用从内核空间返回之后,但在用户空间保存返回值之前执行。 如果系统调用分配了资源,这将导致资源泄漏,并且无法使用取消处理程序对其进行修补。
  2. 如果在线程在可取消的系统调用中被阻塞时处理信号,则整个信号处理程序在启用asynchronous取消的情况下运行。 这可能是非常危险的,因为信号处理程序可能会调用asynchronous信号安全但不是asynchronous取消安全的函数。

我解决这个问题的第一个想法是设置一个标志,线程是在取消点,而不是启用asynchronous取消,当这个标志被设置,取消信号处理程序检查保存的指令指针,看看它是否指向系统调用指令(arch-specific)。 如果是这样,这表示系统调用没有完成,并将在信号处理程序返回时重新启动,因此我们可以取消。 如果没有,我假定系统调用已经返回,并推迟取消。 但是,还有一个竞争条件 – 可能线程还没有到达系统调用指令,在这种情况下,系统调用可能会阻止并且不会响应取消。 另一个小问题是,如果在input信号处理程序时设置了取消点标志,则从信号处理程序执行的不可取消的系统调用错误地变为可取消。

我正在寻找一种新的方法,并寻找反馈意见。 必须满足的条件:

  • 系统调用完成之前收到的任何取消请求必须在系统调用块之前的任何显着的时间间隔内执行,而不是由于信号处理程序的中断而暂停重启。
  • 系统调用完成后收到的任何取消请求必须推迟到下一个取消点。

我想到的想法需要为可取消的系统调用包装器进行专门的组装。 基本的想法是:

  1. 将即将到来的syscall指令的地址推入堆栈。
  2. 将堆栈指针存储在线程本地存储器中。
  3. 从线程本地存储testing取消标志; 如果设置,则跳转到取消例程。
  4. 进行系统调用。
  5. 清除保存在线程本地存储中的指针。

取消操作将涉及:

  1. 在目标线程的线程本地存储中设置取消标志。
  2. testing目标线程的线程本地存储中的指针; 如果不为空,则向目标线程发送取消信号。

取消信号处理程序将然后:

  1. 检查保存的堆栈指针(在信号上下文中)是否等于线程本地存储器中保存的指针。 如果没有,那么取消点就被一个信号处理程序中断了,现在没有什么可做的了。
  2. 检查程序计数器寄存器(保存在信号上下文中)是否小于或等于保存在堆栈指针处的地址。 如果是这样,这意味着系统调用还没有完成,我们执行取消。

我现在看到的唯一问题是在信号处理程序的第1步中:如果它决定不采取行动,那么在信号处理程序返回后,线程可能会在系统调用上被阻塞,忽略挂起的取消请求。 为此,我看到两个可能的解决scheme:

  1. 在这种情况下,安装一个计时器来传递信号到特定的线程,基本上每一毫秒都会重试,直到我们运气好。
  2. 再次提高取消信号,但不取消取消信号,从取消信号处理器返回。 当中断的信号处理程序返回时它会自动取消屏蔽,然后我们可以再试一次。 不过,这可能会干扰信号处理程序中取消点的行为。

任何想法哪种方法最好,或者是否还有其他更重要的缺陷?

Solutions Collecting From Web of "在用户空间中实现可取消的系统调用"

解决方案2感觉像少了一个黑客。 我不认为这会引起你提出的问题,因为在系统调用处理程序中调用的可取消的系统调用将检查TLS中的取消标志,如果取消信号处理程序已经运行并且用信号掩码进行了修改,那么必须已经设置该标志。

(如果每个阻塞系统调用都带有一个pselect() )的sigmask参数,那么对于实现者来说,好像更容易一些。