115-select 与信号

1. 引言

在 select 基础中已经提到过,select 函数可能会返回错误,比如使用了错误的描述符,或者被信号打断。第一种情况使用了错误的描述符,这一般是人犯的错,而第二种被信号打断,谈不上是一种真正的错误,对这种情况,程序要能考虑到。

有人说,要防止 select 被信号打断,可不可以在注册信号的时候,将其设置成自动重启的?不行,对于所有的 IO 利用类函数都不支持自动重启,也就是说一旦有信号到来,会被立即中断。如果你忘记了什么是自动重启,请参考《中断系统调用与自动重启动》。

当 select 被信号打断后,返回值 < 0,同时 errno 会被置成 EINTR. 接下来,通过实验来进行验证。

2. 实验

程序 select_sig.c 捕捉了 SIGALRM 信号,如果程序在运行的时候收到了 SIGALRM 信号,select 函数会出错返回。

2.1 代码

// select_sig.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PERR(msg) do { perror(msg); exit(1); } while(0);

void handler(int sig) {
  if (sig == SIGALRM)
    puts("Hello SIGALRM");
}

int process(char* prompt, int fd) {
  int n;
  char buf[64];
  char line[64];
  n = read(fd, buf, 64);
  if (n < 0) {
    // error
    PERR("read");
  }
  else if (n == 0) {
    // peer close
    sprintf(line, "%s closed\n", prompt);
    puts(line);
    return 0;
  }
  else if (n > 0) {
    buf[n] = 0;
    sprintf(line, "%s say: %s", prompt, buf);
    puts(line);
  }
  return n;
}

int main () {
  int n, res, fd0, maxfd;
  char buf[64];
  struct sigaction sa; 
  fd_set st; 

  // 打印 pid
  printf("pid = %d\n", getpid());

  // 安装信号处理函数
  sa.sa_handler = handler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGALRM, &sa, NULL);

  // 为了简化程序,这里只管理一个描述符
  FD_ZERO(&st);
  fd0 = STDIN_FILENO;
  FD_SET(fd0, &st);

  maxfd = fd0 + 1;

  while(1) {
    fd_set tmpset = st;
    res = select(maxfd, &tmpset, NULL, NULL, NULL);

    if (res < 0) {
      // 如果被信号打断的,不让程序退出,直接 continue
      if (errno == EINTR) {
        perror("select");
        continue;
      }
      // 其它情况的错误,直接让程序退出
      PERR("select");
    }
    else if (res == 0) {
      // timeout
      continue;
    }

    if (FD_ISSET(fd0, &tmpset)) {
      n = process("fd0", fd0);
      if (n == 0) FD_CLR(fd0, &st);
    }
  }
}

2.2 编译和运行

  • 编译
$ gcc select_sig.c -o select_sig
  • 运行

启动 select_sig 后,在另一个终端给它发 SIGALRM 信号。


115-select 与信号_第1张图片
图1 程序收到信号后,select_sig 被中断

3. 总结

  • 知道使用 select 函数的时候,要处理被信号中断的情况。

你可能感兴趣的:(linux,编程学习笔记,Linux,环境编程修炼指南-外功心法)