C++ 知道在执行期间访问了哪些内存页

C++ 知道在执行期间访问了哪些内存页,c++,c,memory-management,pthreads,C++,C,Memory Management,Pthreads,在我的程序中,我需要知道程序定期访问哪些页面,因此在每0.5秒或1秒后,我检查哪些页面被访问,并根据这些页面值计算校验和 我使用mprotect函数标记需要查看的内存区域,并为每个线程安装SIGSEGV信号处理程序。在每个周期开始时,我将保护设置为PROT_READ,然后当出现页面错误时,在记录页面地址后,我授予对页面的读写访问权 然而,我注意到这种方法使得我的程序的执行非常缓慢。另外,由于我对每个线程都这样做,因此会进一步降低性能。有没有办法使这个过程更快。特别是,是否有可能在进程级别执行此操

在我的程序中,我需要知道程序定期访问哪些页面,因此在每0.5秒或1秒后,我检查哪些页面被访问,并根据这些页面值计算校验和

我使用
mprotect
函数标记需要查看的内存区域,并为每个线程安装
SIGSEGV
信号处理程序。在每个周期开始时,我将保护设置为
PROT_READ
,然后当出现页面错误时,在记录页面地址后,我授予对页面的读写访问权


然而,我注意到这种方法使得我的程序的执行非常缓慢。另外,由于我对每个线程都这样做,因此会进一步降低性能。有没有办法使这个过程更快。特别是,是否有可能在进程级别执行此操作,因此,例如,如果线程A导致页面错误,它将写入该页面,并且当线程B访问该页面时,它已经具有写入权限。

对于更快的方法,可以构造特殊的编译器过程。它将通过更改阴影内存中的标志来检测内存访问。例如,对于每个读或写操作,编译器过程将向某些线程特定区域(阴影内存)添加特殊的写操作

有一个项目,作为LLVM编译器的附加传递。附加内存(阴影内存)比已用内存少8倍。AddressSanitizer使用此过程检测对未初始化内存的访问:速度仅为1.5x-3x。 对于页面粒度标记,阴影内存将非常小(1-4字节的阴影用于4096字节的常规内存)

如果您不想在编译器上执行此操作或无法执行此操作(例如,封闭源代码应用程序),则可以使用内核中现有的COW技术:内核通过内存访问标志为每个fork执行COW。您可以在时间t1分叉一个进程,停止子进程,等待1或2秒,然后比较停止的子进程(它没有写入,因此映射与t1时的映射相同)和进程(更改的页面将重新映射)的映射。此变体速度更快,但它只提供有关写入的信息,而不提供有关每个线程操作的信息


此外,您还可以在内核中破解COW页面错误处理程序。这种攻击会更难,但它会有关于哪个线程进行了写入的信息。

mprotect真的是特定于线程的吗?线程共享相同的地址spacemprotect不是,但SIGSEGV是。我刚才提到的是最后一句话。是的,yiu_H,SIGSEGV是线程特定的。我指的是那个,非常有用。谢谢osgx!您提到的fork选项很好,但它并不能解决我的问题,因为我的问题恰恰相反,我正试图看到两个复制进程的转移,所以我不能相信其中两个将以类似方式执行。@metallicprest,请邀请我回答其他问题。为他们安装的谷歌提醒太慢了(这个问题需要7分钟)。我对多线程进程的分叉有些怀疑,一些内存映射可以在这样的分叉处释放。但是想法是-内核已经对更改的页面进行了这样的计算。TLB未命中对我来说就像一个谜。这是因为我认为(我在这里不是普遍正确的,这里有PA-RISC、糟糕的SparcsV9、MIPS、糟糕的安腾和其他)硬件可能会在没有任何软件帮助的情况下重新填充TLB(页表的格式是固定的;页表根的地址存储在特殊寄存器中;页表由操作系统保持最新)。没有软件TLB未命中处理程序;这里唯一的处理程序是页面错误处理程序。引用一篇关于这种方法的文章:“几种现代RISC体系结构(例如MIPS R2000/3000、DEC Alpha和HP PA-RISC)处理软件中的TLB未命中。”这不适用于x86。