1. 终端驱动程序模式
1.1 规范模式
1
2
3
4
5
6
7
8
9
10
11
int main(int ac,char *av[]){
int c;
while ((c = getchar()) != EOF){
if (c == 'z')
c = 'a';
else if (islower(c))
c++;
putchar(c);
}
return 0;
}
击键的同时,字符回显到屏幕上,直到按下回车键,程序才接收到输出。
Ctrl-C 结束输入并终止程序
运行的程序并不做这些操作,缓冲,回显,编辑,控制键盘的处理都是由终端驱动程序完成
1.2 非规范处理
stty -icanon
关闭终端缓存stty -echo
关闭回显
1.3 终端模式小结
规范模式
有缓冲,有回显。仅当接收到回车键才将这些缓冲的字符发送给程序。
非规范模式
缓冲与编辑功能被关闭。简单的来说,就是输入的字符立刻被发送给程序。
rwa模式
所有处理都被关闭。
2. 程序示例
编写一段程序,要求如下:
对用户显示提示问题,接受输入。 y返回0,n返回1;
2.1 版本1
1
2
ttystate.c_lflag &= ~ICANON; // 不缓冲
ttystate.c_cc[VMIN] = 1 ; // 每次只读一个char
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define QUESTION "Do you want another transaction?"
int get_resopnse(char *);
int main(){
int resopnse;
resopnse = get_resopnse(QUESTION);
}
int get_resopnse(char *question){
printf("%s(y/n)?",question);
while (1){
switch (getchar()) {
case 'y':
case 'Y': return 0;
case 'n':
case 'N':
case 'EOF': return 1;
}
}
}
上述程序存在的两个问题:
- 用户必须按下回车键,程序才能够接受到数据。
- 当用户按下回车键时,程序接收的是整行数据。
终端处理规范模式
2.2 改进1 – 及时响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#define QUESTION "Do you want another transaction?"
int get_resopnse(char *);
tty_mode(int);
set_mode();
int main(){
int resopnse;
tty_mode(0);
set_mode();
resopnse = get_resopnse(QUESTION);
tty_mode(1);
return resopnse;
}
int get_resopnse(char *question){
printf("%s(y/n)?",question);
int input;
while (1){
switch (input = getchar()) {
case 'y':
case 'Y': return 0;
case 'n':
case 'N':
case 'EOF': return 1;
default:
printf("\ncannot understand %c,",input);
printf("Please type y or n \n");
}
}
}
tty_mode(int how){
static struct termios original_mode;
if (how == 0)
tcgetattr(0,&original_mode);
else
return tcsetattr(0,TCSANOW,&original_mode);
}
set_mode(){
struct termios ttystate;
tcgetattr(0,&ttystate);
ttystate.c_lflag &= ~ICANON; // 不缓冲
ttystate.c_cc[VMIN] = 1 ; // just one char
tcsetattr(0,TCSANOW,&ttystate);
}
2.3 改进2 – 非阻塞输入
文件的非阻塞读入: 调用fcontl(),改变文件为非阻塞,调用read(),如果能获得输入,返回输入字符。如果不能,返回0;
3. 信号
3.1 信号的来源
- 用户
例如 ctrl + c
等命令
- 内核
程序出错时,内核给进程发出信号,例如浮点溢出,等。
- 进程
一个进程可以通过系统调用kill给另一个进程发送信号。 详见 man 7 signal
3.2 进程如何处理信号
默认处理(通常是消亡)
进程通过一下调用来恢复默认处理
signal(SIGINT,SIG_DFL)
忽略信号
signal(SIGINT ,SIG_IGN)
调用另外一个函数
signal(signum,functionname);
3.3 一个进程处理信号的小demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
main(){
void f(int);
int i ;
signal(SIGINT,f);
for ( i = 0;i<5;i++){
printf("hello\n");
sleep(1);
}
}
void f(int signum){
printf("OUCh\n");
}
3.4 信号处理的一点小总结
为什么要引入信号处理? 回想前面处理程序
are you sure? y/n
. 为了避免终端长时间等待,我们将终端调整为非阻塞状态,循环3次,每次读取输入,最后再将终端调整为阻塞状态。那么问题来了, 在我们将终端调整为非阻塞状态后,如果我们强制按下
ctr + c
会出现什么结果?登录用户会直接退出。 why? 此时终端为非阻塞态,不会读入任何终端输入。引入信号量,信号处理。 在捕捉到
ctr + c
后,强制调用f,使得终端变为阻塞态。
默认处理
忽略信号
调用另外一个函数处理