如果我mprotect
是一个带有 PROT_NONE 的段,并且如果 aSIGSEGV
是由于由sigaction
with处理的写入而发生的sa_sigaction
,我们将能够使用siginfo_t
's找到发生故障的地址si_addr
。但是有没有办法找到试图写入的数据和数据的长度?
我正在尝试这样做,因为我为我的项目尝试了一种写时复制机制。
如果我mprotect
是一个带有 PROT_NONE 的段,并且如果 aSIGSEGV
是由于由sigaction
with处理的写入而发生的sa_sigaction
,我们将能够使用siginfo_t
's找到发生故障的地址si_addr
。但是有没有办法找到试图写入的数据和数据的长度?
我正在尝试这样做,因为我为我的项目尝试了一种写时复制机制。
你找不到进程试图写入的数据,询问它的大小也没有意义。如果您可以获得数据,则意味着内核已经将其复制到某个地方。
你会SIGSEGV
在整个页面上得到一个。也就是说,无论进程正在写入什么数据,每页都会出现一个错误——第一次尝试写入一个字节时。所以你需要做的就是:
内核不知道,所以它不能告诉你。但是您可以尝试找出是否愿意。您在堆栈上有错误的代码地址,因此您可以在那里反汇编代码以尝试找出它。没有其他方法可以知道(如果您不明白为什么,请考虑一下)。该指令出错是因为它触及了受保护的页面,除非您分析汇编代码,否则这就是已知的一切。
如果您无法通过仅知道出错的页面来判断您正在处理的对象,我强烈建议您重新考虑更改您的设计以便您这样做。(posix_memalign
,也许?)
更新:不要忘记你需要有一个在每次上下文切换时调用的钩子。您可能需要在每个挂钩上复制页面。例如:
上下文 A 正在使用 CoW 语义访问页面 Q。上下文 A 获得对页面的只读访问权限。
上下文 B 正在使用 CoW 语义访问页面 Q。上下文 B 获得对该页面的只读访问权限。
上下文 A 去修改页面,我们为上下文 B 制作一个副本。上下文 A 现在对页面具有写访问权并对其进行修改。
我们从上下文 A 切换到上下文 B。此时,您必须切换为上下文 B 制作的页面副本。
请注意,解决此问题的另一种方法是让上下文对映射和锁定页面进行特定调用。如果您允许上下文在上下文切换中保存映射,那将无法正常工作——至少,如果没有大量额外的工作,这将是行不通的。
当您使用 mprotect() 时,您知道要保护的内存段的起始地址和长度。您需要将该信息存储在某处,以便以后使用。
一旦你保护了你的内存段,如果你以违反保护的方式访问它,你会得到一个 SIGSEGV 信号。在信号处理程序中,您将获得一个指向 siginfo_t 的指针。这将为您提供所需的信息。si_addr 为您提供导致非法访问的指令的地址,而 si_ptr 为您提供非法访问的地址。然后,您将 si_ptr 与您保护的内存段进行比较,直到找到它所属的内存段,这就是您需要为“写时复制”复制的内存段......一旦完成,您就需要调用setcontext() 或 siglongjmp() 之类的东西可以从已知位置继续运行。
我希望这有帮助。