Ctrlプログラムで+Dシグナルをキャプチャし、そのシグナル ハンドラーを作成したいと考えています。どうやってやるの?私はCに取り組んでおり、 Linuxシステムを使用しています。
7 に答える
他の人がすでに言ったように、Control+Dを処理するには、「ファイルの終わり」を処理します。
Control+Dは、ユーザーと標準入力として表示される疑似ファイルとの間の通信の一部です。具体的に「ファイルの終わり」を意味するわけではありませんが、より一般的には「これまでに入力した入力をフラッシュする」ことを意味します。フラッシュとはread()
、プログラム内の stdin に対するすべての呼び出しが、最後のフラッシュ以降に入力された入力の長さで返されることを意味します。行が空でない場合、ユーザーはまだ「return」を入力していませんが、入力はプログラムで使用可能になります。行が空の場合、read()
ゼロが返され、「ファイルの終わり」と解釈されます。
したがって、Control+Dを使用してプログラムを終了する場合、行の先頭でのみ機能するか、2 回実行した場合 (1 回目はフラッシュし、2 回目はread()
0 を返します)。
それを試してみてください:
$ cat
foo
(type Control-D once)
foofoo (read has returned "foo")
(type Control-D again)
$
最小限の例:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }
int main(){
setvbuf(stdout,NULL,_IONBF,0);
struct termios old_termios, new_termios;
tcgetattr(0,&old_termios);
signal( SIGINT, sig_hnd );
new_termios = old_termios;
new_termios.c_cc[VEOF] = 3; // ^C
new_termios.c_cc[VINTR] = 4; // ^D
tcsetattr(0,TCSANOW,&new_termios);
char line[256]; int len;
do{
len=read(0,line,256); line[len]='\0';
if( len <0 ) printf("(len: %i)",len);
if( len==0 ) printf("(VEOF)");
if( len >0 ){
if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
}
}while( line[0] != 'q' );
tcsetattr(0,TCSANOW,&old_termios);
}
プログラムは、VEOF 文字 (Ctrl-D から) を Ctrl-C に変更し、VINTR 文字 (Ctrl-C から) を Ctrl-D に変更します。Ctrl-D を押すと、ターミナル ドライバは SIGINT をプログラムのシグナル ハンドラに送信します。
注: VINTR を押すと端末の入力バッファが消去されるため、VINTR キーを押す前に行に入力された文字を読み取ることはできません。
信号を処理する必要はありません。
ISIG が端末フラグに設定されていないことを確認する必要があります。それだけです。
標準入力でのブロックを回避するために select を使用した完全な包含例を次に示します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/select.h>
#define STDIN_FILENO 0
struct termios org_opts;
/** Select to check if stdin has pending input */
int pending_input(void) {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
/** Input terminal mode; save old, setup new */
void setup_terminal(void) {
struct termios new_opts;
tcgetattr(STDIN_FILENO, &org_opts);
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}
/** Shutdown terminal mode */
void reset_terminal(void) {
tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
}
/** Return next input or -1 if none */
int next_input(void) {
if (!pending_input())
return -1;
int rtn = fgetc(stdin);
printf("Found: %d\n", rtn);
return(rtn);
}
int main()
{
setup_terminal();
printf("Press Q to quit...\n");
for (;;) {
int key = next_input();
if (key != -1) {
if ((key == 113) || (key == 81)) {
printf("\nNormal exit\n");
break;
}
}
}
reset_terminal();
return 0;
}
出力:
doug-2:rust-sys-sterm doug$ cc junk.c
doug-2:rust-sys-sterm doug$ ./a.out
Press Q to quit...
Found: 4
Found: 3
Found: 27
Found: 26
Found: 113
Normal exit
注意。3は対照Cであり、4は対照Dである。26はコントロールzです。113は「q」です。完全な表については、 http : //en.wikipedia.org/wiki/ASCII#ASCII_control_charactersを参照してください。
私の知る限り、Ctrl+Dはシステムによって標準入力の最後に変換されるため、アプリはシグナルを受け取りません。
Ctrl+をインターセプトする唯一の方法Dは、システム API を直接操作することだと思います(tty へのアクセスなど)。
TTY レイヤーは ^D を EOF に変換するため、poll() を使用して fd #1 の POLLHUP を監視できます。