内存出错信号处理例子 void signal_handler()

signal(SIGSEGV,set_on_segment_fault); 

void set_on_segment_fault(int sig)

{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = handler;
sigaction(SIGSEGV, &act, NULL);

}

static void handler(int signum, siginfo_t *info, void  *arg)

{

#ifdef __HI_CODEC__
// ucontext_t* context= (ucontext_t *)arg;
void* buffer[8] = {0, };
size_t nptr;
int i;
char **strings= NULL;
/*
  unsigned long sp,top,bottom;
  int i=1;
  */
fprintf(stderr, "==============================================================\n");
fprintf(stderr, "(1)SIGSEGV : %d\n", info->si_signo);
fprintf(stderr, "ADDR : %p\n", info->si_addr);
fprintf(stderr, "VALUE : %x\n", *(int*)(info->si_addr));
//fprintf(stderr, "%x\n", (int)'H');


// fprintf(stderr,"context : %lx\n",context->uc_mcontext.regs->nip);
// fprintf(stderr,"sp      : %lx\n",context->uc_mcontext.regs->gpr[1]);
#else //__HI_CODEC__
ucontext_t* context= (ucontext_t *)arg;
void* buffer[8] = {0, };
size_t nptr;
int i;
char **strings= NULL;
/*
unsigned long sp,top,bottom;
int i=1;
*/
fprintf(stderr, "==============================================================\n");
fprintf(stderr, "(1)SIGSEGV : %d\n", info->si_signo);
//fprintf(stderr, "ADDR : %p\n", info->si_addr);
//fprintf(stderr, "VALUE : %x\n", *(int*)(info->si_addr));
//fprintf(stderr, "%x\n", (int)'H');


fprintf(stderr,"context : %lx\n",context->uc_mcontext.regs->nip);
fprintf(stderr,"sp      : %lx\n",context->uc_mcontext.regs->gpr[1]);


#endif //__HI_CODEC__
/*
  sp = context->uc_mcontext.regs->gpr[1];
  bottom = sp & ~(THREAD_SIZE-1UL);
  top = bottom + THREAD_SIZE;
  while (sp < top && sp >= bottom) {
  printf("%.16lx ", *(unsigned long *)(sp + 16));
  sp = *(unsigned long *)sp;
  if (!(i % 4))
  printf("\n");
  if (i++ == 32)
  break;
  }
  */
fprintf(stderr, "==============================================================\n");
nptr = backtrace(buffer, 8);
fprintf(stderr, "(2)BACKTRACE num : %d\n" , nptr);
strings = backtrace_symbols(buffer, nptr);
if(strings == NULL)
{
fprintf(stderr, "backtrace symbols err\n");
fprintf(stderr, "==============================================================\n");
// for console.log
fflush(stderr);
sleep(3);
exit(0);
}
for (i=0 ; i<nptr; i++)
{
fprintf(stderr, "addr : %s\n", strings[i]);
}
free(strings);
fprintf(stderr, "==============================================================\n");
// for console.log
fflush(stderr);
sleep(3);
exit(0);
}

<think>好的,我现在要仔细分析用户提供的这段C代码,并解释它的功能以及可能存在的问题。首先,我需要逐行理解代码的结构和各个部分的作用。 首先,代码包含了几个头文件:stdio.h用于输入输出函数,signal.h用于信号处理,malloc.h和memory.h可能用于内存分配和操作函数。不过注意到,在标准C库中,malloc.h其实并不是标准的,通常应该使用stdlib.h来代替,这可能是一个需要注意的地方。不过,这可能取决于具体的编译器环境,用户代码中确实包含了malloc.h,所以这里可能存在一些小问题,但可能不影响代码的运行。 接下来,定义了一个静态的字符指针buf,初始化为NULL。然后在test_sigterm函数中,先打印"before",然后释放buf指向的内存,再打印"after"。这个函数被注册为SIGTERM信号的处理函数。SIGTERM是一个用于终止进程的信号,通常由系统或其他进程发送。 在main函数中,首先给buf分配了100字节的内存。然后使用signal函数将SIGTERM信号的处理函数设置为test_sigterm。之后进入一个无限循环,循环体内不断分配100字节的内存,并立即释放。最后返回0,但实际上由于无限循环的存在,程序不会执行到return语句。 现在,我需要考虑这段代码可能存在的问题。首先,在信号处理函数中使用free函数是否安全?根据C标准,信号处理函数中只能调用异步信号安全的函数。例如,printf和free是否属于异步信号安全函数?查阅资料后,发现printf并不是异步信号安全的,因为它在执行时可能修改全局状态,比如标准输出的缓冲区。同样,free函数如果在信号处理中使用,也可能导致问题,尤其是在多线程环境或当主程序正在操作堆时。因此,在信号处理函数中调用free和printf是不安全的,可能导致未定义行为,比如死锁或内存损坏。 其次,主程序中的无限循环不断地分配和释放内存。每次循环都会调用malloc(100),然后立即free掉。这在理论上不会导致内存泄漏,因为每次分配的内存都被立即释放了。但是,频繁的malloc和free操作可能导致内存碎片,尤其是在长时间运行的情况下。不过,这里的问题可能更侧重于信号处理部分。 当进程接收到SIGTERM信号时,会调用test_sigterm函数。此时,主程序可能正在执行malloc或free操作。如果在信号处理函数中调用free(buf),而主程序此时可能也正在操作堆(比如在循环中执行free),这可能导致堆的结构被破坏,引发不可预知的后果,比如程序崩溃或死锁。 此外,test_sigterm函数中释放buf后,并没有将buf指针置为NULL,这可能导致重复释放的问题。虽然在这个例子中,main函数中只分配了一次buf,但如果在信号处理函数被多次调用的情况下(虽然SIGTERM通常会导致进程终止,但可能被捕获后不退出),多次free同一个指针会导致未定义行为。 另外,当信号处理函数执行完毕后,程序会回到主循环继续执行吗?这取决于信号的处理方式。默认情况下,信号处理函数执行后,被中断的主程序会继续执行。所以,当SIGTERM被捕获并处理后,程序会继续运行,可能继续循环。但通常处理SIGTERM后,程序应该退出,否则可能导致无法正常终止。 总结可能的问题点: 1. 在信号处理函数中使用非异步信号安全的函数(printf和free)。 2. 主循环频繁分配和释放内存,可能影响性能或导致碎片,但这不是主要错误。 3. 信号处理函数中释放buf后未置NULL,可能潜在导致重复释放(如果信号被多次处理)。 4. 捕获SIGTERM后未让程序正常退出,导致程序无法终止,除非有其他终止方式。 此外,代码中的malloc.h可能应该替换为stdlib.h,因为标准C中malloc等函数的声明在stdlib.h中,而malloc.h可能在某些系统中不可用或已过时。 可能的运行情况:当程序运行时,主循环不断分配和释放内存。当收到SIGTERM信号时,调用test_sigterm函数,打印"before",释放buf指向的内存,然后打印"after"。之后程序继续运行,但buf已经被释放,再次访问或释放会导致错误。不过在此代码中,main函数中只有一次buf的分配,之后没有再次使用buf,所以在信号处理函数中释放一次后,主函数中的buf指针仍然指向已释放的内存,成为悬空指针。如果后续主函数中有代码试图使用或再次释放buf,就会导致问题。但在此代码中,主函数在循环中没有使用buf,所以可能不会立即出错,但这是一个不良实践。 另外,主循环中的free(malloc(100))每次分配100字节,并立即释放,所以理论上不会有内存泄漏,但频繁的分配释放可能影响性能。 此外,当处理SIGTERM后,程序应该退出,否则可能会继续运行。例如,用户可能发送SIGTERM希望终止程序,但程序捕获信号后没有退出,继续运行,这不符合预期。通常,处理SIGTERM时应进行清理然后退出。例如,在test_sigterm中设置退出标志,或直接调用exit()。但要注意,在信号处理函数中调用exit()是否是安全的,但通常来说,exit()是允许的,因为它会终止进程。 综上所述,这段代码的主要问题在于信号处理函数中使用了不安全的函数,可能导致未定义行为,以及在处理SIGTERM后未正确终止程序。</think>以下是对这段C代码的逐步分析: $$代码结构解析$$ ```c #include <stdio.h> #include <signal.h> #include <malloc.h> // 非标准头文件(建议改用<stdlib.h>) #include <memory.h> // 非标准头文件(可能误用<string.h>) static char *buf = NULL; // 全局静态指针 void test_sigterm(){ // SIGTERM信号处理函数 printf("before\n"); // 问题点1:非异步信号安全函数 free(buf); // 问题点2:在信号处理中操作堆内存 printf("after\n"); // 继续使用非安全函数 } int main(){ buf = malloc(100); // 分配堆内存 signal(SIGTERM, test_sigterm); // 注册信号处理器 while(1){ // 无限循环 free(malloc(100));// 持续分配+立即释放(无泄漏但低效) } return 0; } ``` $$核心问题解析$$ 1. **信号处理安全隐患** - `printf()`和`free()`都不是[异步信号安全函数](https://man7.org/linux/man-pages/man7/signal-safety.7.html) - 当主程序正在执行`malloc/free`时收到信号,可能导致**堆内存管理数据结构损坏** 2. **悬空指针风险** ```c free(buf); // 释放后未置空 ``` - 若多次收到`SIGTERM`信号(虽然非常规操作),会导致重复释放 3. **逻辑缺陷** - 捕获`SIGTERM`后没有终止程序,违背该信号的常规用途(要求进程终止) $$执行流程示意图$$ ``` 启动程序 ├─ 分配buf内存 ├─ 注册信号处理器 └─ 进入疯狂循环:分配100字节 → 立即释放 当收到SIGTERM信号时: 暂停主循环 → 执行test_sigterm() 打印"before" 释放buf内存(此时全局指针仍指向已释放区域) 打印"after" 恢复主循环继续执行 ``` $$改进建议$$ 1. 信号处理函数中只设置退出标志 ```c volatile sig_atomic_t flag = 0; // 原子类型保证可见性 void handler(int sig) { flag = 1; // 仅设置标志 } ``` 2. 主循环检测退出标志 ```c while(!flag) { free(malloc(100)); } free(buf); // 在主线程安全释放 ``` 3. 头文件修正 ```c #include <stdlib.h> // 替换<malloc.h> #include <string.h> // 替换<memory.h> ``` 该代码展示了信号处理内存操作的典型陷阱,实际开发中应遵循: - 信号处理器保持极简(仅设置标志) - 避免在信号处理器中操作非局部变量 - 使用POSIX标准的`sigaction()`代替`signal()`以获得更可靠行为
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值