我计划创建一个日志记录/跟踪机制,它将字符串文字的地址(const char*
)写入环形缓冲区。这些字符串位于只读数据段中,由预处理器使用__function__
或__file__
创建。
问题:如果所有指针都有效,是否有可能在Segfault之后分析此环缓冲区内容? “有效”是指它们指向映射的内存区域,解除引用不会导致分段错误。
我正在使用Linux 2.6.3x和GCC 4.4.x。
致以最诚挚的问候,
查理
答案 0 :(得分:2)
我认为您正在寻找的方法是通过sigaction
处理SIGSEGV
信号。
void handler(int, siginfo_t *info, ucontext_t *uap)
{
/* Peek at parameters here... I'm not sure exactly what you want to do. */
}
/* Set up the signal handler... */
struct sigaction sa, old_sa;
memset(&sa, 0 sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sa, &old_sa))
{
/* TODO: handle error */
}
但请注意,在您自己的流程中捕获SIGSEGV
有点奇怪。该过程可能处于无法恢复的糟糕状态。你能够做出的回应可能是有限的,并且最有可能的是被杀的过程是一件好事。
如果你想要它更稳定一点,那么sigaltstack
调用可以让你指定一个备用堆栈缓冲区,这样如果你已经完全管理你的堆栈,你仍然可以处理{{1} }。要使用此功能,您需要在SIGSEGV
上方设置SA_ONSTACK
。
如果你想从另一个进程的安全性中回复sa.sa_flags
(从而将自己与表现不佳的segfaulting代码隔离开来并使其在检查时不会崩溃),你可以使用{{ 3}}。这个接口很复杂,有许多非便携部件,主要用于编写调试器。但是你可以用它来做很多事情,比如读写进程的内存和寄存器,并改变它的执行。
答案 1 :(得分:2)
检查取消引用内存区域是否会导致段错误的常用方法是使用read()
或write()
。例如,检查ptr
指向的前128个字节是否安全可读:
int fd[2];
if (pipe(fd) >= 0) {
if (write(fd[1], ptr, 128) > 0)
/* OK */
else
/* not OK */
close(fd[0]);
close(fd[1]);
}
(write()
将返回EFAULT
,而不是在区域不可读时发出信号。
如果您想一次测试超过PIPE_BUF
个字节,则需要从管道的读取端读取和丢弃。
答案 2 :(得分:1)
当然,如果您所依赖的堆栈或其他内存已损坏,那么可能会出现问题,但对于任何代码都是如此。
假设您依赖的堆栈或其他内存没有问题,并且假设您没有调用malloc()
之类的async-signal safe之类的函数,并假设您这样做不要尝试从信号处理程序返回,那么从信号处理程序中读取或写入缓冲区应该没有问题。
如果您要测试特定地址是否有效,可以使用mincore()
等系统调用并检查错误结果。
答案 3 :(得分:0)
一旦你收到了段错误,所有的赌注都会被取消。指针可能有效或者它们可能已损坏。你只是不知道。您可以将它们与有效值进行比较,或者指向环形缓冲区本身的指针可能已损坏。在这种情况下,你可能会得到垃圾。