1. 首页
  2. 技术知识

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真正的读错误

  }

}
总结

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

原创文章,作者:starterknow,如若转载,请注明出处:https://www.starterknow.com/108437.html

联系我们