Linuxシステムコールの勉強(その11)

Linuxシステムコール

Linuxシステムコール

前回はこちら


今回は、RAWモードにおける制御文字であるVMINとVTIMEについて説明します。ioctl()を使った端末制御については今回が最後です。


まずはVMINを変更するプログラムのソースとその動作を見て見ます。

      1 #include <sys/ioctl.h>
      2 #include <stdio.h>
      3 #include <asm/termbits.h>
      4
      5 int main()
      6 {
      7     char c[BUFSIZ];
      8     struct termio tm, tm_save;
      9     int  inputNum;
     10     int  fd_stdin, fd_stdout;
     11
     12     fd_stdin  = fileno(stdin);
     13     fd_stdout = fileno(stdout);
     14
     15     /* 現在の設定を格納 */
     16     ioctl(fd_stdin, TCGETA, &tm);
     17     tm_save = tm;
     18
     19     /* 入力したい文字数を取得 */
     20     printf("何文字入力しますか?");
     21     fscanf(stdin,"%d", &inputNum);
     22
     23     /* 端末に設定を反映 */
     24     tm.c_lflag &= ~ICANON;
     25     tm.c_cc[VMIN] = inputNum;
     26     ioctl(fd_stdin, TCSETAF, &tm);
     27
     28     printf("%d文字入力してください?", inputNum);
     29     fflush(stdout);
     30     read(fd_stdin, &c, inputNum);
     31
     32     // 整形用 //
     33     write(fd_stdout, "\n", 1);
     34
     35     write(fd_stdout, c, inputNum);
     36
     37     // 整形用 //
     38     write(fd_stdout, "\n", 1);
     39
     40     /* 設定を元に戻す */
     41     ioctl(fd_stdin, TCSETAF, &tm_save);
     42
     43     return 0;
     44 }


24行目でRAWモードへ移行し、25行目でVMINの値を標準入力からの入力値に変更しています。ではこれを動かしてみましょう。

[itotto@itotto ]$ gcc -o change_vmin change_vmin.c

[itotto@itotto ]$ ./change_vmin
何文字入力しますか?5
5文字入力してください?12345
12345

[itotto@itotto ]$ ./change_vmin
何文字入力しますか?10
5文字入力してください?1234567890
1234567890

[itotto@itotto ]$

入力文字数として"5"を入力した場合にはVMINの値は5になっています。この際、read()が動作するのは5文字のデータ入力が完了したタイミングです。同様に入力文字数として"10"を入力した場合には10文字入力したタイミングでread()が動作しています。


つまりVMINの値はバッファに何バイトのデータが入ったらread()を呼び出すかという事を決めるための値である事が分かります。


続いてVTIMEの値について調べるために以下のプログラムを使用します。

      1 #include <sys/ioctl.h>
      2 #include <stdio.h>
      3 #include <asm/termbits.h>
      4
      5 int main()
      6 {
      7     char c;
      8     struct termio tm, tm_save;
      9     int  fd_stdin;
     10     int  n, result;
     11
     12     fd_stdin  = fileno(stdin);
     13
     14     /* 現在の設定を格納 */
     15     ioctl(fd_stdin, TCGETA, &tm);
     16     tm_save = tm;
     17
     18     /* 端末に設定を反映 */
     19     tm.c_lflag    &= ~(ECHO | ICANON);
     20     tm.c_cc[VMIN]  = 0;
     21     tm.c_cc[VTIME] = 1;
     22     ioctl(fd_stdin, TCSETAF, &tm);
     23
     24     result = 0;
     25
     26     printf("終了する場合はCtrl + x\n");
     27
     28     while(1)
     29     {
     30         // キー入力なし //
     31         if ((n = read(fd_stdin, &c, 1)) == 0)
     32         {
     33             printf("入力文字なし\n");
     34         }
     35
     36         // システムコール(read())でエラー //
     37         else if (n == -1)
     38         {
     39             perror("read");
     40             result = 1;
     41             break ;
     42         }
     43
     44         // 入力文字がctrl+xの場合 //
     45         else if (c == '\x18')
     46         {
     47             result = 0;
     48             break;
     49         }
     50         else
     51             printf("%c\n", c);
     52     }
     53
     54     /* 設定を元に戻す */
     55     ioctl(fd_stdin, TCSETAF, &tm_save);
     56
     57     return result;
     58 }


19行目でRAWモードへ移行し、20行目でVMINの値を0に、21行目でVTIMEの値を変更しています。VMINが0という事は入力を受け付けるバッファの単位が0なので、即時読み取りを意味しています。
ではこれもためしに動かしてみます。

[itotto@itotto ]$ gcc -o ts_get_keycode ts_get_keycode.c

[itotto@itotto ]$ ./ts_get_keycode
終了する場合はCtrl + x
入力文字なし
a
入力文字なし
入力文字なし
v
入力文字なし
入力文字なし
入力文字なし
b
v
入力文字なし
入力文字なし

[itotto@itotto ]$

このとおり、キー入力が無ければ「入力文字なし」と表示されますが、何かキーを押すとその文字が表示されます。


実際に試してみると分かりますが、VTIMEを0にした場合と1にした場合では、「入力文字なし」と表示されるタイミング異なります。0にした場合はフォローのしようのないくらい、あっという間に画面表示が流されます。対して1に設定した場合には適度な間隔で表示が更新されます。
これより、VTIMEはread()を実行した際に、バッファからデータの受け渡しが無い場合に待つ時間をあらわすものであることがわかります。ちなみに単位は1/10秒です。


で。
このVMINとVTIMEを使ってESCキーとファンクションキーの区別が出来たりするよという事がこの本には書いてありますが、あまり面白くないので割愛します。また必要になったら読み返そう。


最後にキーコードを調べるプログラム(xevのCUI版)をサンプルとして作成して動作させてみます。

      1 #include <sys/ioctl.h>
      2 #include <stdio.h>
      3 #include <asm/termbits.h>
      4
      5 int main()
      6 {
      7     char c;
      8     struct termio tm, tm_save;
      9     int  fd_stdin;
     10     int  n, result;
     11
     12     fd_stdin  = fileno(stdin);
     13
     14     /* 現在の設定を格納 */
     15     ioctl(fd_stdin, TCGETA, &tm);
     16     tm_save = tm;
     17
     18     /* 端末に設定を反映 */
     19     tm.c_lflag    &= ~(ECHO | ECHONL |ICANON);
     20     tm.c_cc[VMIN]  = 1;
     21     tm.c_cc[VTIME] = 0;
     22
     23     tm.c_iflag &= ~ICRNL ; // \r -> \n変換をしない //
     24     tm.c_oflag &= ~ONLCR ; // \n -> \r変換をしない //
     25     tm.c_lflag &= ~ISIG  ; // シグナルを無視する   //
     26
     27     tm.c_iflag &= ~IXOFF ; // c-s, c-qの制御(入力) //
     28     tm.c_iflag &= ~IXON  ; // c-s, c-qの制御(出力) //
     29
     30     ioctl(fd_stdin, TCSETAF, &tm);
     31
     32     result = 0;
     33
     34     printf("終了する場合はCtrl + x\n");
     35
     36     while(1)
     37     {
     38         // キー入力なし //
     39         if (read(fd_stdin, &c, 1) == -1)
     40         {
     41             perror("read");
     42             result = 1;
     43             break ;
     44         }
     45
     46         // 入力文字がctrl+xの場合 //
     47         else if (c == '\x18')
     48         {
     49             result = 0;
     50             break;
     51         }
     52         else
     53             printf("%02x : %c\r\n",c ,c);
     54     }
     55
     56     /* 設定を元に戻す */
     57     ioctl(fd_stdin, TCSETAF, &tm_save);
     58
     59     return result;
     60 }
[itotto@itotto ]$ gcc -o get_keycode get_keycode.c

[itotto@itotto ]$ ./get_keycode
終了する場合はCtrl + x
61 : a
62 : b
63 : c
64 : d
21 : !
48 : H
5e : ^
5d : ]

[itotto@itotto ]$


次回からプロセス間通信(名前付きパイプ)に入ります。端末長かった...。


次へ進む