(備忘録)(7/12:ご指摘に対応)
やりたかったこと
getchar()で入力待ちループをしているときに、Ctrl+Cが押されたことを取得しこれをもとにターミナルに画面表示をしたかった
(そういう要望であったため、Ctrl+C以外を使用するなどは不可)
はじめに作ったソース
signal()でSIGINT(Ctrl+C 押下時のシグナル)を設定
// シグナルハンドラ処理
static void ctrl_c_event( int sig )
{
// Ctrl+C押下を取得し、これをもとに画面表示
test_disp(0xff); // 画面表示処理
}
// メイン
int main(int argc, char *argv[])
{
char input;
signal( SIGINT, ctrl_c_event );
// ループで標準入力を取得 入力をもとに画面表示を行う処理
while(1){
input = getchar();
test_disp(input); // 画面表示処理
}
return 0;
}
解決…?
これで問題ないかと思っていたが、シグナルハンドラ内でのprintfは推奨されない らしい。
※参考にしたqiitaの記事
また、signalを使用するのも推奨されない らしい
対策
ので、対策として以下のように変更した。
Ctrl+C 押下時のシグナルハンドラでフラグを有効化
// シグナルハンドラ処理
static volatile sig_atomic_t ctrl_c_flag;
static void ctrl_c_event( int sig )
{
ctrl_c_flag = 1; // フラグを有効化
}
getchar()をread()に変更し、read()のコールをselect()によって制御するように変更
read()のタイムアウト後の処理でフラグのチェックを行い、フラグが有効であればCtrl+Cが押下されたことを通知
また、signalをsigactionに変更
// メイン
int main(int argc, char *argv[])
{
char input;
static struct sigaction sig_ctrl_c; // Ctrl+C通知用
// sigaction初期化
sigemptyset(&sig_ctrl_c.sa_mask); // シグナルマスクをクリア
sig_ctrl_c.sa_handler = ctrl_c_event; // Ctrl+C通知関数を登録
// シグナルハンドラ登録
sigaction( SIGINT, &sig_ctrl_c, NULL);
while(1){
FD_ZERO(&fd_group); // グループを初期化
FD_SET(STDIN_FILENO, &fd_group); // 標準入力をグループに追加
if( select(STDIN_FILENO + 1, &fd_group, NULL, NULL, &timeout) == -1) return 0;
// 入力を監視/処理
if (FD_ISSET(STDIN_FILENO, &fd_group)) {
// 標準入力からデータを読み込み
if (read(STDIN_FILENO, &data, sizeof(data)) > 0) {
test_disp(input); // 画面表示処理
}
}
// Ctrl+C イベント取得
if(ctrlc_event_flag){
test_disp(0xff); // 画面表示処理
ctrl_c_flag = false; // フラグをクリア
}
usleep(10 * 1000); // 負荷軽減(10ミリ秒)
}
return 0;
}