面试题答案
一键面试1. 编写可靠的信号处理函数
在Linux C编程中,处理SIGINT
信号可按以下步骤进行:
-
定义信号处理函数:
#include <stdio.h> #include <signal.h> #include <unistd.h> volatile sig_atomic_t sigint_received = 0; void sigint_handler(int signum) { sigint_received = 1; }
这里定义了一个全局变量
sigint_received
,用于标记SIGINT
信号是否被接收。volatile sig_atomic_t
类型用于确保对该变量的访问是原子操作,避免竞态条件。 -
注册信号处理函数:
int main() { struct sigaction sa; sa.sa_handler = sigint_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) == -1) { perror("sigaction"); return 1; } while (!sigint_received) { // 主程序执行正常任务 printf("Working...\n"); sleep(1); } printf("Received SIGINT, cleaning up and exiting...\n"); // 执行清理操作 return 0; }
使用
sigaction
函数注册信号处理函数,sigemptyset
用于清空信号掩码,sa_flags
设置为0表示默认行为。
2. 防止竞态条件
- 使用
volatile sig_atomic_t
类型:如上述代码中对sigint_received
变量的定义。volatile
关键字告诉编译器该变量的值可能会在程序控制之外被改变,防止编译器对其进行过度优化。sig_atomic_t
类型保证对该变量的读写操作是原子的,即在多线程或信号处理环境下不会出现数据竞争。
3. 信号处理函数中不能调用的函数类型及原因
-
不可重入函数:
- 原因:不可重入函数在被信号处理函数调用时,可能会导致数据不一致。例如,函数内部使用了静态变量,当信号处理函数中断主程序执行并调用该函数时,静态变量的状态可能会被破坏,因为主程序和信号处理函数共享这些静态变量。像
printf
函数就是不可重入的,因为它内部使用了静态缓冲区。 - 示例:
asctime
函数,它返回一个指向静态缓冲区的指针。如果在信号处理函数和主程序中同时调用asctime
,可能会导致缓冲区数据冲突。
- 原因:不可重入函数在被信号处理函数调用时,可能会导致数据不一致。例如,函数内部使用了静态变量,当信号处理函数中断主程序执行并调用该函数时,静态变量的状态可能会被破坏,因为主程序和信号处理函数共享这些静态变量。像
-
标准I/O函数:
- 原因:标准I/O函数通常是基于缓冲区的,信号处理函数调用这些函数可能会破坏缓冲区的状态。例如,
fprintf
函数在向缓冲区写入数据时,如果被信号处理函数中断并再次调用fprintf
,可能会导致缓冲区混乱,数据输出错误。 - 示例:
fprintf
、fgets
等函数都不适合在信号处理函数中调用。
- 原因:标准I/O函数通常是基于缓冲区的,信号处理函数调用这些函数可能会破坏缓冲区的状态。例如,
-
malloc和free函数:
- 原因:
malloc
和free
函数维护着堆内存的状态,信号处理函数调用它们可能会导致堆内存管理结构的不一致。例如,malloc
在分配内存时,可能正在修改堆内存的元数据,如果此时信号处理函数中断并调用malloc
或free
,可能会破坏堆内存的一致性,导致程序崩溃。 - 示例:在信号处理函数中调用
malloc
来分配内存,可能会引发未定义行为。
- 原因: