在我的程序中,我需要知道程序正在定期访问哪些页面,所以在每0.5或1秒后我检查访问了哪些页面并从这些页面值计算校验和。
我使用mprotect
函数来标记需要查看的内存区域,并为每个线程安装SIGSEGV
信号处理程序。在每个周期开始时,我将保护设置为PROT_READ
,然后当发生页面错误时,我会在注意到其地址后对页面进行读写访问。
但是,我注意到这个方法使我的程序执行速度很慢。此外,由于我正在为每个线程执行此操作,因此会进一步降低性能。有没有办法让这个程序更快。特别是,是否可以在进程级别进行此操作,例如,如果线程A引发页面错误,它会写入该页面,并且当线程B访问它时,它已经具有写入权限。
答案 0 :(得分:2)
对于更快的方法,可以构造特殊的编译器传递。它将通过更改影子存储器中的标志来检测内存访问。例如。对于每个读取或写入操作,编译器传递将向特定于线程的区域(影子存储器)添加特殊写入操作。
有一个http://code.google.com/p/address-sanitizer/项目,作为LLVM编译器的附加传递。附加内存(影子内存)比使用内存少8倍。 AddressSanitizer使用此传递来检测对未初始化内存的访问:http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm减速仅为1.5x-3x。 对于页面粒度标记,影子内存将非常小(对于4096字节的常用内存,为1-4个字节的阴影)。
如果您不想在编译器上执行此操作或无法执行此操作(例如,闭源应用程序),则可以使用内核中现有的COW技术:http://en.wikipedia.org/wiki/Copy-on-write内核为每个fork执行COW通过内存访问标志。您可以在时间t1分叉进程,停止子进程,等待1或2秒,然后比较已停止的子进程的映射(它没有写入,因此映射与t1时的映射相同)和进程(已更改的页面)被重新映射)。这种变体更快,但它只提供有关写入的信息,而不是每个线程操作的信息。
此外,您可以破解内核中的COW页面错误处理程序。这个hack会更难,但它会有关于哪个线程写入的信息。