我正在构建一个程序,它使用mprotect()来限制一块内存访问。当请求内存时,抛出SIGSEGV,我使用signal()调用来监听。
一旦检测到SIGSEGV,我需要以某种方式访问指向所请求的内存(引发错误)的指针以及所请求的段的大小。这可能吗?
void fifoSigHandler(){
// Needs to only remove protection from requested block of virtual memory
mprotect(fifoVm,(size_t)fifoVm_size,PROT_WRITE);
printf("Caught Seg Fault");
}
void fifo_init(void* vm, int vm_size, int n_frames, int page_size)
{
fifoVm = vm;
fifoVm_size = vm_size;
fifoFrames = n_frames;
fifoPageSize = page_size;
mprotect(fifoVm,(size_t)fifoVm_size,PROT_NONE);
signal(SIGSEGV, fifoSigHandler);
}
另外,有没有办法确定当前分配了一块内存(PROT_NONE,PROT_READ等等)的mprotect()级别?
答案 0 :(得分:6)
您必须使用sigaction
与SA_SIGINFO
代替signal
来建立您的处理程序,然后您将在siginfo_t
中收到有用信息的回复,包括{ {1}}。
si_addr
(2)中所述, si_addr
将包含地址。至于长度,除非你愿意解析说明,否则你运气不好。你能做的最好就是对sigaction
中报告的页面采取行动,然后如果这还不够,你很快就会得到另一个信号。至少,这就是我们在ObjectStore中做事的方式。
答案 1 :(得分:1)
您正在寻找libsigsegv
http://libsigsegv.sourceforge.net/
但请注意,调用mprotect
仅在Linux中是信号安全的,其他POSIX系统可能不支持此功能。
我担心在Linux中获取内存保护位的唯一方法是读入/proc/$pid/meminfo
旁注(仅限Linux):如果您担心内存消耗并打算逐个启用较大映射的页面,那么我建议使用mmap
和{{1}创建映射在这种情况下,您将获得映射到零填充的写时复制页面,这将在第一次写入时分配物理RAM。 MAP_NORESERVE
指示内核不要使用交换空间来备份内存,从而允许您分配最多64TB的虚拟地址空间。唯一的缺点是,如果你的内存耗尽,可能会发生可怕的事情(oom-killer)。
答案 2 :(得分:1)
第1步:初始化sigaction
:
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
sigemptyset(&act.sa_mask);
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
第2步:设置此sigaction
句柄SIGSEGV
:
sigaction(SIGSEGV, &act, NULL);
(可选)第3步:让它处理其他内存信号:
sigaction(SIGBUS, &act, NULL);
sigaction(SIGTRAP, &act, NULL);
根据需要添加错误处理
第4步:定义处理函数:
void handler(int signal, siginfo_t* siginfo, void* uap) {
printf("Attempt to access memory at address %p\n",
siginfo->si_addr);
#ifdef LINUX_64BIT
printf("Instruction pointer: %p\n",
(((ucontext_t*)uap)->uc_mcontext.gregs[16]));
#elif LINUX_32BIT
printf("Instruction pointer: %p\n",
(((ucontext_t*)uap)->uc_mcontext.gregs[14]));
#endif
}
您可以参考ucontext_t
和siginfo_t
的手册页,以获取处理程序可以提取的更多有趣数据。