1、有些程序处理从特定设备来的数据。这些与特定设备相关的程序必须控制与设备的连接。Unix系统中最常见的设备是终端。
2、终端驱动程序有很多设置。各个设置的特定值决定了终端驱动程序的模式。为用户编写的程序通常需要设置终端驱动程序为特定的模式。
3、键盘输入分为3类,终端驱动程序对这些输入做不同的处理。大多数建代表常规数据,它们从驱动程序传输到程序。有些键调用驱动程序中的编辑函数。如果按下删除键,驱动程序将前一个字符从它的行缓冲中删除,并将命令发送到终端屏幕,使之从显示器中删除字符。最后,有些键调用处理控制函数。Ctrl-C键告诉驱动程序调用内核中某个函数,这个函数给进程发送一个信号。终端驱动程序支持若干种处理控制函数,它们都通过发送信号到进程来实现控制。
man 2 signal
4、信号是从内核发送给进程的一种简短消息。信号可能来自用户、其他进程或内核本身。进程可以告诉内涵,在它收到信号时需要做出怎样的响应。
5、
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);
}