开发者社区> 问答> 正文

`pause()` 疑似阻塞,害得循环无法终止。

// bounce_async.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // pause()
#include <fcntl.h> // fcntl()
#include <signal.h> // signal()
#include <sys/time.h> // set_ticker()
#include <stdbool.h>
#include <string.h>
#include "curses.h" // initscr(), cbreak(), getch(), move(), addstr(), mvaddch(), refresh(), endwin()

#define MESSAGE "hello"
#define BLANK "     "

int row = 10;
int col = 0;
int dir = 1;
int delay = 200;
bool done = false;

extern void set_ticker(int n_msecs);
extern void on_input(int signum);
extern void enable_kbd_signal(void);
extern void on_alarm(int signum);

int main(void) {
  initscr();
  cbreak();
  noecho();
  refresh();

  signal(SIGIO, on_input);
  enable_kbd_signal();
  signal(SIGALRM, on_alarm);
  set_ticker(delay);

  mvaddstr(row, col, MESSAGE);

  while (done == false)
    pause();

  endwin();
  return EXIT_SUCCESS;
}

extern void set_ticker(int n_msecs) {
  long n_secs = n_msecs / 1000L;
  long n_usecs = (n_msecs % 1000L) * 1000L;

  struct itimerval new_timeset;

  new_timeset.it_value.tv_sec = n_secs;
  new_timeset.it_value.tv_usec = n_usecs;

  new_timeset.it_interval.tv_sec = n_secs;
  new_timeset.it_interval.tv_usec = n_usecs;

  setitimer(ITIMER_REAL, &new_timeset, NULL);
}

extern void on_input(int signum) {
  int c = getch();

  if (c == 'Q' || c == EOF)
    done = true;
    //exit(EXIT_SUCCESS);
  else if (c == ' ')
    dir = -dir;
}

extern void enable_kbd_signal(void) {
  fcntl(0, F_SETOWN, getpid());
  int fd_flags = fcntl(0, F_GETFL);
  fcntl(0, F_SETFL, fd_flags | O_ASYNC);
}

extern void on_alarm(int signum) {
  signal(SIGALRM, on_alarm);
  mvaddstr(row, col, BLANK);
  col += dir;
  mvaddstr(row, col, MESSAGE);
  refresh();

  if (dir == -1 && col <= 0)
    dir = 1;
  else if (dir == 1 && col + strlen(MESSAGE) >= COLS)
    dir = -1;
}

编译 clang bounce_async.c -l curses 并执行,发现按 Q 无法退出程序,怀疑 pause() 那里发生了原因不明的阻塞。为什么?又如何解决?

展开
收起
a123456678 2016-06-07 19:03:50 2262 0
1 条回答
写回答
取消 提交回答
  • 码农|Coder| Pythonista

    首先,不要把布尔变量与布尔值比较……如果你觉得 done == false 是可以接受的,那你为什么不写成 (done == false) == true 呢?为什么不写成 ((done == false) == true) == true 呢……

    通过 strace 可以看到,pause(2) 只被调用了一次,它立即被 SIGIO 打断了,于是调用 getch(3),得不到输出,只能等到被 SIGALRM 打断。读到数据之后正要返回呢,又来了个 SIGIO 信号 :-D

    为什么 SIGIO 会在这个时候跑出来呢?因为终端这时候是可以写的!虽然它叫标准输入,但是它的文件描述符标志和标准输出是一样的:

    >>> cat /proc/20583/fdinfo/1
    pos:    0
    flags:  0104002
    mnt_id: 22
    >>> cat /proc/20583/fdinfo/0
    pos:    0
    flags:  0104002
    mnt_id: 22

    你也可以往标准输入里写点东西试试看 :-)

    PS: 在信号处理函数里用了不少不可重入函数了吧?

    2019-07-17 19:30:47
    赞同 展开评论 打赏
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载

相关实验场景

更多