0
Help us understand the problem. What are the problem?

posted at

updated at

bashのTMOUT周りについて

bashにはTMOUTと呼ばれる変数がある。
これは文字通りタイムアウトの秒数を設定するための組み込みの変数で、
例えば以下のようにするとbashはTMOUT秒たったあとに終了する。

$ TMOUT=10
#10秒以上放置

man bashを見ると以下のように書いてあった。

TMOUT
If set to a value greater than zero, TMOUT is treated as the default timeout for the read builtin.The select command terminates if input does not arrive after TMOUT seconds when input is coming from a terminal. In an interactive shell, the value is interpreted as the number of seconds to wait for a line of input after issuing the primary prompt. Bash terminates after waiting for that number of seconds if a complete line of input does not arrive.

翻訳すると以下のような感じ

TMOUT変数にゼロより大きい値が設定されている場合、タイムアウトが設定される。
selectコマンドや対話モードのシェルのときに入力が一定時間発生しないとTMOUT秒後にbashは終了する。

この仕組みが実装されているのは eval.cのread_command()の中で以下のようになっている。


int read_command()
{

SHELL_VAR *tmout_var;
  int tmout_len, result;
  SigHandler *old_alrm;

  set_current_prompt_level (1);
  global_command = (COMMAND *)NULL;

  /* Only do timeouts if interactive. */
  tmout_var = (SHELL_VAR *)NULL;
  tmout_len = 0;
  old_alrm = (SigHandler *)NULL;

  if (interactive)
    {
      tmout_var = find_variable ("TMOUT");

      if (tmout_var && var_isset (tmout_var))
    {
      tmout_len = atoi (value_cell (tmout_var));
      if (tmout_len > 0)
        {
          old_alrm = set_signal_handler (SIGALRM, alrm_catcher);
          alarm (tmout_len);
        }
    }
    }

  QUIT;

  current_command_line_count = 0;
  /*この関数が、パーサなどを呼び出しコマンドオブジェクトを生成する。*/
  result = parse_command ();

  if (interactive && tmout_var && (tmout_len > 0))
    {
      /*シグナルハンドラを解除する。*/
      alarm(0);

      set_signal_handler (SIGALRM, old_alrm);
    }

  return (result);
}

このコードを見ると、タイムアウトはシグナルハンドラとして実装されていることがわかる。
alarm()システムコールは0より大きい整数が渡されると、カーネルがそのプロセスに対してSIGALRMを発行するように設定する。
bashがシグナルを受け取ると、set_signal_handler()によって設定された関数が呼び出される。
この関数がbashを終了させる。
alarm()システムコールに0を渡すと、その設定が解除される。(man alarmを参照)
bashはこの手順を繰り返している。

このコードを見るとおもしろいことがわかる。
上のコードではコマンドのパースが終了したあとにシグナルハンドラを解除するという動作を行っている。
つまり、こんなことが実際に起こるのかはわからないが、コマンドの入力が完了したとしても、
待機時間中にコマンドの解析が終わらなかった場合、bashは終了すると思う。(おもしろい)

TMOUTを1秒にして、パースに1秒以上かかるコマンドを入力すれば、確認できるが
コマンドが思いつかなかったので、実験はしていない。


[追記]
コマンドを思いついたので追記する。

$ TMOUT=1; echo {1..255}{1..255}{1..255}{1..255}

このコマンドを実行すれば、大体の場合はブレース展開している間にタイムアウトになると思う。
自分の場合はパース中にタイムアウトした。

ちなみに、

$ {1..255}{1..255}{1..255}{1..255}

はバッシュのメーリングリストで見た。
単体で実行すると、メモリの使いすぎでプロセスがキルされる。


もしこの考え方が正しかったら、bashのマニュアルは
”コマンドのパースにTMOUT秒以上の時間がかかったらbashは終了します。”
と書いたほうがいいと思った。

bashは歴史のあるプログラムであり、いろんな人の目が入っていることはわかるが、
このタイムアウトの設定コードはあまりきれいに思えないのは自分だけだろうか。
何かしらの理由があってこのような書き方になっているとは思うが、
もうちょっといい書き方があるのではないかと思う。
なので、今度の記事ではこのへんをもうちょっと詳しく見てみようと思う。

自分の考えは完全に間違っている可能性があります。
もし知っている人は教えてくれると嬉しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
0
Help us understand the problem. What are the problem?