Posts p6重写linux命令-信号与控制
Post
Cancel

p6重写linux命令-信号与控制

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;
}
  1. 击键的同时,字符回显到屏幕上,直到按下回车键,程序才接收到输出。

  2. Ctrl-C 结束输入并终止程序

运行的程序并不做这些操作,缓冲,回显,编辑,控制键盘的处理都是由终端驱动程序完成

1.2 非规范处理

  1. stty -icanon关闭终端缓存

  2. stty -echo 关闭回显

1.3 终端模式小结

  1. 规范模式

    有缓冲,有回显。仅当接收到回车键才将这些缓冲的字符发送给程序。

  2. 非规范模式

    缓冲与编辑功能被关闭。简单的来说,就是输入的字符立刻被发送给程序。

  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;
        }
    }
}

上述程序存在的两个问题:

  1. 用户必须按下回车键,程序才能够接受到数据。
  2. 当用户按下回车键时,程序接收的是整行数据。

终端处理规范模式

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 信号的来源

  1. 用户

例如 ctrl + c 等命令

  1. 内核

程序出错时,内核给进程发出信号,例如浮点溢出,等。

  1. 进程

一个进程可以通过系统调用kill给另一个进程发送信号。 详见 man 7 signal

3.2 进程如何处理信号

  1. 默认处理(通常是消亡)

    进程通过一下调用来恢复默认处理

    signal(SIGINT,SIG_DFL)

  2. 忽略信号

    signal(SIGINT ,SIG_IGN)

  3. 调用另外一个函数

    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 信号处理的一点小总结

  1. 为什么要引入信号处理? 回想前面处理程序are you sure? y/n. 为了避免终端长时间等待,我们将终端调整为非阻塞状态,循环3次,每次读取输入,最后再将终端调整为阻塞状态。

  2. 那么问题来了, 在我们将终端调整为非阻塞状态后,如果我们强制按下ctr + c 会出现什么结果?登录用户会直接退出。 why? 此时终端为非阻塞态,不会读入任何终端输入。

  3. 引入信号量,信号处理。 在捕捉到ctr + c 后,强制调用f,使得终端变为阻塞态。

  • 默认处理

  • 忽略信号

  • 调用另外一个函数处理

This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags