// 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() 那里发生了原因不明的阻塞。为什么?又如何解决?
首先,不要把布尔变量与布尔值比较……如果你觉得 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: 在信号处理函数里用了不少不可重入函数了吧?
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。