如何理解SA_RESTART”被信号中断的系统调用自动重启“?

"被信号中断的系统调用自动重启"是 Linux 信号处理中的一个重要机制,由 SA_RESTART 标志控制。以下是深入解释:

核心概念

当进程执行阻塞型系统调用 (如 read(), write(), accept(), wait() 等)时:

  1. 如果系统调用执行期间收到信号
  2. 且该信号设置了信号处理函数(非忽略/默认终止)
  3. 系统调用会被中断,立即返回用户空间执行信号处理函数

此时系统调用有两种后续处理方式:

  • 不自动重启 :系统调用返回 -1,并设置 errno = EINTR(被中断)
  • 自动重启 (设置 SA_RESTART):内核自动重新执行被中断的系统调用

工作流程对比

有信号 已设置 未设置 阻塞系统调用开始 信号到达 中断系统调用 执行信号处理函数 SA_RESTART 标志 内核自动重启系统调用 返回用户空间 errno=EINTR 系统调用完成 应用程序处理 EINTR

实际代码示例

未设置 SA_RESTART 的情况:

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void handler(int sig) {
    printf("Received signal %d\n", sig);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0; // 不设置 SA_RESTART
    
    sigaction(SIGINT, &sa, NULL);
    
    char buf[256];
    ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); // 阻塞读
    
    if (n == -1 && errno == EINTR) {
        printf("Read interrupted!\n");
    }
    return 0;
}

当按下 Ctrl+C(发送 SIGINT)时:

  1. read() 被中断
  2. 执行信号处理函数
  3. read() 返回 -1,errno = EINTR

设置 SA_RESTART 的情况:

c 复制代码
sa.sa_flags = SA_RESTART; // 关键设置

同样按下 Ctrl+C 后:

  1. read() 被中断
  2. 执行信号处理函数
  3. 内核自动重启 read() 调用
  4. 程序继续阻塞等待输入

技术细节

  1. 哪些系统调用支持重启:

    • 文件I/O:read, write, open 等
    • 进程控制:wait, waitpid
    • 网络操作:accept, connect, recv 等
    • 不支持的:poll, select, epoll_wait(需手动处理 EINTR)
  2. 重启的实现位置 :

    在 Linux 内核源码 kernel/signal.c 的 get_signal() 函数中:

    c 复制代码
    if (sig_handler_ignored(...) || 
        (sa->sa_flags & SA_RESTART)) {
        restart_syscall(); // 重启逻辑
    }
  3. 编程最佳实践:

    • 对需要可靠性的服务程序,建议设置 SA_RESTART

    • 对需要快速响应信号的程序(如监控),避免使用 SA_RESTART

    • 即使使用 SA_RESTART,也应处理关键操作的 EINTR:

      c 复制代码
      do {
          ret = accept(sockfd, ...);
      } while (ret == -1 && errno == EINTR); // 安全重启

典型应用场景

  1. 网络服务器:

    c 复制代码
    sa.sa_flags = SA_RESTART; // 确保accept不会因信号失败
    sigaction(SIGCHLD, &sa, NULL); // 处理子进程退出

    当子进程退出发送 SIGCHLD 时,accept() 自动重启

  2. 交互式程序:

    c 复制代码
    sa.sa_flags = 0; // 不设置SA_RESTART
    sigaction(SIGALRM, &sa, NULL); // 超时信号

    当 read() 被 SIGALRM 中断时,可执行超时处理逻辑

这种机制平衡了系统调用的可靠性和信号的响应能力,是 Linux 信号处理体系的核心设计之一。

相关推荐
GeW2 小时前
新质生产力路线图落地,Linux运维与数据库架构师的机会清单
linux
Brilliantwxx2 小时前
【Linux】 文件访问与路径解析:目录、文件名、dentry、路径缓冲与 dcache 全解析
linux·运维·服务器·开发语言·spring
father_12272 小时前
Greenplum 集群故障恢复与主备同步机制实战
linux·网络·greenplum
ynchyong3 小时前
linux中的 sed` 与 `seq`
linux·运维·sed·seq·文本工具
七牛云行业应用4 小时前
DeepSeek Harness(dsh)安装完整指南:Windows、macOS、Linux 三平台 + 桌面端与 Web 端全覆盖
linux·windows·macos·ai·ai编程
十年磨一剑~5 小时前
APT 仓库包查看方法文档
linux·ubuntu
DYWorker0015 小时前
第 1-1 课:启动编排:内核如何把自己装配起来
linux
wacsii5 小时前
【Linux基础开发工具(五)】Git版本控制器从入门到实战:核心操作三板斧与新手避坑指南
linux·运维·服务器
水饺编程5 小时前
第2章:Linux 基础,解压缩【.tar.gz】压缩包
linux·c语言·汇编
luoqice6 小时前
telnet登录linux实时查看xx.log日志
linux