我们来深入学习 rt_sigreturn 系统调用
1. 函数介绍
rt_sigreturn 的作用就是:在信号处理函数执行完毕后,由内核调用它来恢复程序被中断前的执行状态,并使程序从中断点继续执行。
你可以把它看作是信号处理机制的“返回票”:内核用“去程票”(保存状态并跳转到处理函数),信号处理函数执行完后,内核用“回程票”(rt_sigreturn)把你送回原来的地方。
对于 Linux 编程小白:你通常不需要知道 rt_sigreturn 的存在,也不需要直接与它交互。它在幕后默默地工作,保证了信号处理完成后程序能正确恢复执行。了解它有助于你更深入地理解信号机制是如何工作的。
2. 函数原型
// 这是内核系统调用,用户空间程序不会直接调用它。
// 它的原型在内核源码中类似这样 (概念性):
asmlinkage long sys_rt_sigreturn(void);
3. 功能
从信号处理函数返回,恢复进程在信号处理前被中断的处理器状态(包括寄存器、堆栈指针等),并恢复信号屏蔽字,使进程从中断点继续执行。
4. 参数
rt_sigreturn 系统调用不接受任何用户空间传递的参数。
5. 返回值
rt_sigreturn 永远不会正常返回到调用者。
6. 相似函数或关联函数
- 信号处理函数: 你用
sigaction设置的函数。rt_sigreturn是在它return后被间接调用的。 sigaction: 用于设置信号处理函数,间接影响rt_sigreturn的行为(例如,旧的信号掩码会被恢复)。sigaltstack: 可以设置信号处理函数运行的备用堆栈。rt_sigreturn需要知道是否使用了备用堆栈以便正确恢复。ucontext_t: 在使用SA_SIGINFO标志时,信号处理函数会收到一个指向ucontext_t的指针,其中包含了调用时的上下文信息。rt_sigreturn会使用这些信息(或内核内部保存的类似信息)来恢复状态。setjmp/longjmp: 提供了另一种用户态的“跳转并恢复状态”机制,但原理和用途与rt_sigreturn不同。
7. 示例代码
由于 rt_sigreturn 是内核自动调用的,我们无法写出直接调用它的 C 代码。但是,我们可以通过一个信号处理的例子来观察 rt_sigreturn 的效果。
下面的代码展示了信号处理函数执行完毕后,程序如何恢复并继续执行,这背后就是 rt_sigreturn 在起作用。
#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h> // 包含 setitimer
// 全局变量,用于在主程序和信号处理函数间通信
volatile sig_atomic_t alarm_received = 0;
// 信号处理函数
void alarm_handler(int sig) {
// 注意:在信号处理函数中应只使用异步信号安全的函数
// printf 通常不安全,但为了演示我们简化使用
printf(" >>> Alarm signal (%d) received! <<<\n", sig);
alarm_received = 1; // 设置标志
// 模拟在信号处理函数中做一些工作
for (int i = 0; i < 3; ++i) {
printf(" >>> Working in signal handler... %d <<<\n", i+1);
sleep(1); // 暂停1秒
}
printf(" >>> Signal handler finished. <<<\n");
// 当这个函数执行 return 时,
// 运行时库会安排调用 rt_sigreturn 系统调用
// 来恢复主程序的执行状态
}
int main() {
struct sigaction sa;
struct itimerval timer;
printf("Main program starting...\n");
printf("PID: %d\n", getpid());
// 1. 设置 SIGALRM 的处理函数
memset(&sa, 0, sizeof(sa));
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
// 不设置 SA_RESTART,这样被中断的系统调用会返回 EINTR
sa.sa_flags = 0;
if (sigaction(SIGALRM, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
// 2. 设置定时器,在 3 秒后产生 SIGALRM 信号
memset(&timer, 0, sizeof(timer));
timer.it_value.tv_sec = 3; // 3秒后启动
timer.it_value.tv_usec = 0;
timer.it_interval.tv_sec = 0; // 不重复
timer.it_interval.tv_usec = 0;
printf("Setting alarm for 3 seconds...\n");
if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
perror("setitimer");
exit(EXIT_FAILURE);
}
printf("Entering main loop. Will be interrupted by alarm in 3 seconds.\n");
int counter = 0;
while (counter < 10) {
printf("Main loop iteration %d\n", counter);
counter++;
// 调用一个可能被信号中断的系统调用
printf("Calling sleep(2)...\n");
int sleep_result = sleep(2);
// 如果 sleep 被信号中断,它会提前返回剩余的睡眠时间
if (sleep_result > 0) {
printf("Sleep was interrupted with %d seconds remaining.\n", sleep_result);
// 检查我们的标志是否被信号处理函数设置
if (alarm_received) {
printf("Confirmed: Alarm was received and handled.\n");
printf("Now continuing main loop execution.\n");
// 重置标志
alarm_received = 0;
}
} else {
printf("Sleep completed normally.\n");
}
printf("---\n");
}
printf("Main program finished.\n");
return 0;
}
代码执行流程解释:
编译和运行:
# 假设代码保存在 sigreturn_example.c 中
gcc -o sigreturn_example sigreturn_example.c
# 运行程序
./sigreturn_example
预期输出:
Main program starting...
PID: 12345
Setting alarm for 3 seconds...
Entering main loop. Will be interrupted by alarm in 3 seconds.
Main loop iteration 0
Calling sleep(2)...
>>> Alarm signal (14) received! <<<
>>> Working in signal handler... 1 <<<
>>> Working in signal handler... 2 <<<
>>> Working in signal handler... 3 <<<
>>> Signal handler finished. <<<
Sleep was interrupted with 1 seconds remaining.
Confirmed: Alarm was received and handled.
Now continuing main loop execution.
---
Main loop iteration 1
Calling sleep(2)...
Sleep completed normally.
---
... (后续循环) ...
这个例子清晰地展示了信号处理机制的工作流程,以及 rt_sigreturn 如何在幕后确保程序在信号处理后能正确恢复执行。
1298

被折叠的 条评论
为什么被折叠?



