終端控制和信號——《Unix/Linux編程實踐教程》讀書筆記(第6章)

一、有些程序處理從特定設備來的數據。這些與特定設備相關的程序必須控制與設備的鏈接。Unix系統中最多見的設備是終端。 node

二、終端驅動程序有不少設置。各個設置的特定值決定了終端驅動程序的模式。爲用戶編寫的程序一般須要設置終端驅動程序爲特定的模式。 ios

三、鍵盤輸入分爲3類,終端驅動程序對這些輸入作不一樣的處理。大多數建表明常規數據,它們從驅動程序傳輸到程序。有些鍵調用驅動程序中的編輯函數。若是按下刪除鍵,驅動程序將前一個字符從它的行緩衝中刪除,並將命令發送到終端屏幕,使之從顯示器中刪除字符。最後,有些鍵調用處理控制函數。Ctrl-C鍵告訴驅動程序調用內核中某個函數,這個函數給進程發送一個信號。終端驅動程序支持若干種處理控制函數,它們都經過發送信號到進程來實現控制。 shell

man 2 signal



四、信號是從內核發送給進程的一種簡短消息。信號可能來自用戶、其餘進程或內核自己。進程能夠告訴內涵,在它收到信號時須要作出怎樣的響應。 函數

五、 this

man 2 fcntl



#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */);



六、
man 2 signal
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
    /* 
     * signal() returns the previous value of the signal handler,
     * or SIG_ERR on error. In the event of an error,
     * errno is set to indicate the cause.
     */



七、
/*
 * play_aganin04.c
 * purpose: ask if user wants another transaction
 * method: set tty into char-by-char mode and no echo mode,
 *         set tty into no-delay mode
 *         read char, return result
 *         resets terminal modes on SIGINT, ignores SIGQUIT
 * returns: 0->yes, 1->no, 2->timeout
 * better: reset terminal mode on Interrupt
 */

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

#define ASK "Do you want another transaction"
#define TRIES (3)           /* max tries */
#define SLEEPTIME (2)       /* time per try */
#define BEEP putchar('\a')  /* alert user */

int get_response(char *, int);
char get_ok_char(void);
void tty_mode(int);
//void set_crmode(void);
void set_cr_noecho_mode(void);
void set_nodelay_mode(void);

int main(void)
{
    int response;
    void ctrl_c_handler(int);

    tty_mode(0);                    /* save tty mode */
    //set_crmode();                 /* set char-by-char mode */
    set_cr_noecho_mode();
    set_nodelay_mode();
    signal(SIGINT, ctrl_c_handler); /* handle INT */
    signal(SIGQUIT, SIG_IGN);       /* ignore QUIT signals */
    response = get_response(ASK, TRIES);
    tty_mode(1);                    /* restore tty mode */
    printf("\n");
    return response;
}

int get_response(char *question, int maxtries)
/*
 * purpose: ask a question and wait for a y/n answer or maxtries
 * method: use a getchar and ignore non y/n answers
 * returns: 0->yes, 1->no
 */
{
    int input;

    printf("%s (y/n)?", question);
    fflush(stdout);
    while (1)
    {
        sleep(SLEEPTIME);
        input = tolower(get_ok_char());
        if (input == 'y')
            return 0;
        if (input == 'n')
            return 1;
        if (maxtries-- == 0)
            return 2;
        BEEP;
    }
}

/*
 * skip over non-legal chars and return y, Y, n, N or EOF
 */
char get_ok_char(void)
{
    char c;
    while ((c = getchar()) != EOF && strchr("yYnN", c) == NULL)
        ;
    return c;
}

void set_cr_noecho_mode(void)
/*
 * purpose: put file descriptor 0 into char-by-char and noecho mode
 * method: use bits in termios
 */
{
    struct termios ttystate;

    tcgetattr(0, &ttystate);
    ttystate.c_lflag &= ~ICANON;        /* no buffering */
    ttystate.c_lflag &= ~ECHO;          /* no echo either */
    ttystate.c_cc[VMIN] = 1;            /* get 1 char at a time */
    tcsetattr(0, TCSANOW, &ttystate);   /* install settings */
}

void set_nodelay_mode(void)
/*
 * purpose: put file descriptor 0 into no_delay mode
 * method: use fcntl to set bits
 * notes: tcsetattr() will do something similar, but it is complicated
 */
{
    int termflags;
    termflags = fcntl(0, F_GETFL);      /* read curr settings */
    termflags |= O_NDELAY;              /* flip on nodelay bit */
    fcntl(0, F_SETFL, termflags);       /* and install them */
}

/* how == 0 -> save current mode, how == 1 -> restore mode */
/* this version handles termios and fcntl flags */
void tty_mode(int how)
{
    static struct termios original_mode;
    static int original_flags;
    static int stored = 0;
    if (how == 0)
    {
        tcgetattr(0, &original_mode);
        original_flags = fcntl(0, F_GETFL);
        stored = 1;
    }
    else
    {
        tcsetattr(0, TCSANOW, &original_mode);
        fcntl(0, F_SETFL, original_flags);
    }
}

void ctrl_c_handler(int signum)
/*
 * purpose: called if SIGINT is detected
 * action: reset tty and scram
 */
{
    tty_mode(1);
    printf("\nCtrl-C pressed.\n");
    exit(1);
}
相關文章
相關標籤/搜索