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

| 我正在努力在Linux上实现pthread取消,而没有在我最近的其他一些问题中讨论的任何“令人不快的行为”(有些人说是错误)。到目前为止,Linux / glibc取消pthread的方法一直将其视为不需要内核支持的东西,并且可以通过在进行syscall之前启用异步取消并恢复先前的取消来纯粹在库级别进行处理。 syscall返回后的状态。这至少有两个问题,其中一个非常严重: 从内核空间返回系统调用之后,但在用户空间保存返回值之前,可以执行取消操作。如果系统调用分配了资源,则会导致资源泄漏,并且无法使用取消处理程序对其进行修补。 如果在可取消的系统调用中阻塞线程的同时处理了信号,则整个信号处理程序将在启用异步取消的情况下运行。这可能非常危险,因为信号处理程序可能会调用异步信号安全的函数,而不是异步取消安全的函数。 解决该问题的第一个想法是设置一个标志,指出线程处于取消点,而不是启用异步取消,并且设置了此标志后,请取消信号处理程序检查保存的指令指针以查看其是否指向syscall指令(特定于Arch)。如果是这样,则表明系统调用尚未完成,并且将在信号处理程序返回时重新启动,因此可以取消。如果没有,我认为系统调用已经返回,并推迟取消。但是,还有一个竞争条件-线程可能根本没有到达syscall指令,在这种情况下,syscall可能会阻塞并且从不响应取消。另一个小问题是,如果在进入信号处理程序时设置了取消点标志,则从信号处理程序执行的不可取消的系统调用会错误地变为可取消。 我正在寻找一种新方法,并正在寻求反馈。必须满足的条件: 必须在任何较大的时间间隔内在syscall阻塞之前对在syscall完成之前收到的任何取消请求进行操作,但是在由于信号处理程序的中断而导致挂起重新启动期间,则不能执行该操作。 在完成syscall之后收到的任何取消请求都必须推迟到下一个取消点。 我想到的这个想法需要可取消的syscall包装器进行专门的组装。基本思想是: 将即将到来的syscall指令的地址压入堆栈。 将堆栈指针存储在线程本地存储中。 测试线程本地存储中的取消标志;跳转以取消例程(如果已设置)。 进行系统调用。 清除保存在线程本地存储中的指针。 取消操作将涉及: 在目标线程的线程本地存储中设置取消标志。 在目标线程的线程本地存储中测试指针;如果不为null,则向目标线程发送取消信号。 然后,取消信号处理程序将: 检查已保存的堆栈指针(在信号上下文中)是否与线程本地存储中的已保存指针相等。如果不是,则取消点被信号处理程序中断,并且现在无事可做。 检查程序计数器寄存器(在信号上下文中保存)是否小于或等于保存在堆栈指针处的地址。如果是这样,则意味着系统调用尚未完成,我们将执行取消。 到目前为止,我看到的唯一问题是信号处理程序的第1步:如果它决定不执行操作,则在信号处理程序返回后,线程可能在syscall上处于阻塞状态,而忽略了挂起的取消请求。为此,我看到两个潜在的解决方案: 在这种情况下,请安装计时器以将信号传递到特定线程,基本上每毫秒左右重试一次,直到我们感到幸运为止。 再次提高取消信号,但从取消信号处理程序返回而不会取消取消信号的屏蔽。当中断的信号处理程序返回时,它将自动取消屏蔽,然后我们可以重试。但是,这可能会干扰信号处理程序中取消点的行为。 是否有关于哪种方法最好的想法,或者我是否还缺少其他更基本的缺陷?     
已邀请:
解决方案2感觉不太像黑客。我认为这不会引起您建议的问题,因为在syscall处理程序中调用的可取消syscall会检查TLS中的取消标志,如果取消信号处理程序已运行并且始终带有信号掩码,则必须已设置TLS 。 (如果每个阻塞的系统调用都将
sigmask
参数设为
pselect()
,似乎对实现者来说要容易得多)。     

要回复问题请先登录注册