当前位置: 首页 > 编程笔记 >

Linux被中断的系统如何调用详解

越嘉茂
2023-03-14
本文向大家介绍Linux被中断的系统如何调用详解,包括了Linux被中断的系统如何调用详解的使用技巧和注意事项,需要的朋友参考一下

前言

慢系统调用,指的是可能永远无法返回,从而使进程永远阻塞的系统调用,比如无客户连接时的accept、无输入时的read都属于慢速系统调用。

在Linux中,当阻塞于某个慢系统调用的进程捕获一个信号,则该系统调用就会被中断,转而执行信号处理函数,这就是被中断的系统调用。

然而,当信号处理函数返回时,有可能发生以下的情况:

  • 如果信号处理函数是用signal注册的,系统调用会自动重启,函数不会返回
  • 如果信号处理函数是用sigaction注册的
    • 默认情况下,系统调用不会自动重启,函数将返回失败,同时errno被置为EINTR
    • 只有中断信号的SA_RESTART标志有效时,系统调用才会自动重启

下面我们编写代码,分别验证上述几种情形,其中系统调用选择read,中断信号选择SIGALRM,中断信号由alarm产生。

使用signal

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

void handler(int s)
{
  printf("read is interrupt by signal handler\n");
  return;
}

int main()
{
  char buf[10];
  int nread = 0;

  signal(SIGALRM, handler);
  alarm(2);

  printf("read start\n");
  nread = read(STDIN_FILENO, buf, sizeof(buf));
  printf("read return\n");

  if ((nread < 0) && (errno == EINTR))
  {
    printf("read return failed, errno is EINTR\n");
  }

  return 0;
}

使用sigaction + 默认情况

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

void handler(int s)
{
  printf("read is interrupt by signal handler\n");
  return;
}

int main()
{
  char buf[10];
  int nread = 0;
  struct sigaction act;

  sigemptyset(&act.sa_mask);
  act.sa_handler = handler;
  act.sa_flags = 0; //不给SIGALRM信号设置SA_RESTART标志,使用sigaction的默认处理方式
  //act.sa_flag |= SA_INTERRUPT; //SA_INTERRUPT是sigaction的默认处理方式,即不自动重启被中断的系统调用
  //实际上,不管act.sa_flags值为多少,只要不设置SA_RESTART,sigaction都是按SA_INTERRUPT处理的

  sigaction(SIGALRM, &act, NULL);
  alarm(2);

  printf("read start\n");
  nread = read(STDIN_FILENO, buf, sizeof(buf));
  printf("read return\n");

  if ((nread < 0) && (errno == EINTR))
  {
    printf("read return failed, errno is EINTR\n");
  }

  return 0;
}


使用sigaction + 指定SA_RESTART标志

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

void handler(int s)
{
  printf("read is interrupt by signal handler\n");
  return;
}

int main()
{
  char buf[10];
  int nread = 0;
  struct sigaction act;

  sigemptyset(&act.sa_mask);
  act.sa_handler = handler;
  act.sa_flags = 0;
  act.sa_flags |= SA_RESTART; //给SIGALRM信号设置SA_RESTART标志

  sigaction(SIGALRM, &act, NULL);
  alarm(2);

  printf("read start\n");
  nread = read(STDIN_FILENO, buf, sizeof(buf));
  printf("read return\n");

  if ((nread < 0) && (errno == EINTR))
  {
    printf("read return failed, errno is EINTR\n");
  }

  return 0;
}


由于对被中断系统调用处理方式的差异性,因此对应用程序来说,与被中断的系统调用相关的问题是:

  • 应用程序无法保证总是知道信号处理函数的注册方式,以及是否设置了SA_RESTART标志
  • 可移植的代码必须显式处理关键函数的出错返回,当函数出错且errno等于EINTR时,可以根据实际需求进行相应处理,比如重启该函数
int nread = read(fd, buf, 1024);

if (nread < 0)
{
  if (errno == EINTR)
  {
    //read被中断,其实不应该算作失败,可以根据实际需求进行处理,比如重写调用read,也可以忽略它
  }
  else
  {
    //read真正的读错误
  }
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对小牛知识库的支持。

 类似资料:
  • 问题内容: 我的用户空间应用程序有时会在收到EINTR信号后以某种方式阻塞。 我用strace记录的内容: 我可以捕获EINTR信号吗,如何重复有关的调用,例如写入,读取或选择?即使使用与系统调用一起使用的第三方库,我如何确定EINTR发生在哪里? 为什么收到EINTR后我的应用程序被完全阻止(请参阅strace转储:我发送了SIGUSR1,通常应该处理)?为什么futex()将ERESTARTS

  • 系统调用 我们要想启动一个进程,需要操作系统的调用(system call)。实际上操作系统和普通进程是运行在不同空间上的,操作系统进程运行在内核态(todo: kernel space),开发者运行的进程运行在用户态(todo: user space),这样有效规避了用户程序破坏系统的可能。 如果用户态进程想执行内核态的操作,只能通过系统调用了。Linux提供了超多系统调用函数,我们关注与进程相

  • 本文向大家介绍如何解决windows系统和linux系统中端口被占用的问题,包括了如何解决windows系统和linux系统中端口被占用的问题的使用技巧和注意事项,需要的朋友参考一下 一、在windows操作系统中,查询端口占用和清除端口占用的程序 提升权限后用:netstat -b 或用 1、查询端口占用的进程ID          点击"开始"-->"运行",输入"cmd"后点击确定按钮,进入

  • 问题内容: 上X86-64英特尔系统,支持和什么是从64位用户代码“最快”的系统调用在香草内核? 特别是,它必须是一个执行/ user <->内核转换1的系统调用,但执行的工作量最少。它甚至不需要执行syscall本身:某种从不分派给内核侧特定调用的早期错误是可以的,只要它不会因此而走慢。 这样的调用可用于估计原始和开销,而与调用完成的任何工作无关。 1特别是,这不包括看似系统调用但在VDSO中实

  • 问题内容: 我正在用python编写程序。我希望阅读stdin并处理sigchld。我想处理输入中的任何一个,但不要旋转(以推测方式采样输入)。 在我进行的每个调用中,我都无法捕获被信号打断的系统调用。 我会以错误的方式处理吗?我可以不用try / except使它正常工作吗? 我主要担心的不是到目前为止的try / except。但是在程序中其他代码行中,我将需要数百人。在我看来,这似乎不是模块

  • 问题内容: 这是成功的send()是“原子的”吗?,因为我认为它实际上实际上涉及系统调用,而不仅仅是在套接字上发送。 哪些系统调用可以被中断,何时中断,中断在哪里处理?我已经了解了SA_RESTART,但并不完全了解发生了什么。 如果我在没有SA_RESTART的情况下进行系统调用,该调用是否可以被与我的应用程序无关但需要操作系统中止我的调用并执行其他操作的任何类型的中断(例如,用户输入)中断?还