UNIX 6th code reading - 対話型端末その2

はじめに

前回は対話型端末の外枠を確認しました。今回は端末の読み書き処理を見ていきます。

インターフェイスにKLを使用しているという前提で読み解いていきます。

Lions本で言うと25章にあたります。練習問題を除いた最後の章です。このUNIX v6 code readingメモ書きも最終回です。

入出力の全体像

入出力の全体像を絵にしてみました。

文字入力

ユーザ(人)により文字が入力され、プログラムがその文字を読み込む、という流れを絵にするとこんな感じです。

ユーザによって文字が入力されると、KLのread data bufferにその文字が入力され、受信完了割り込みが発生します。

klrint( )(8078行目)が受信完了割り込みハンドラで、read data bufferの内容をtty構造体のraw queueの後尾に追加します。

また、ユーザプログラムが端末に対しreadシステムコールを発行すると、カーネル内部でKLのデバイスドライバのklread( )(8062行目)ハンドラが実行されます。

raw queueからデータを読み込んでは、erase(#), kill(@), escape(/)などを処理しつつtty構造体のcanonical queueに追加していきます。

そして、canonical queueのデータがユーザ領域にコピーされていきます。

これが端末の文字入力処理です。

文字出力

今度はプログラムが端末に文字を出力するときの流れを絵にするとこんな感じです。

ユーザプログラムが端末に対しwriteシステムコールを発行すると、カーネル内部でKLのデバイスドライバのklwrite( )(8066行目)ハンドラが実行されます。

ユーザ領域のデータを、tab(\t)やnewline(\n)などを処理しつつtty構造体のout queueに追加していきます。

そして、KLのwrite data bufferにout queueの先頭データをコピーします。すると端末が出力動作を開始します。出力動作が完了すると出力完了割り込みが発生します。

klxint( )(8070行目)が出力完了割り込みハンドラで、out queueから次のデータをKL write data bufferに送り込みます。

これが端末の文字出力処理です。

コードメモ

klrint( )
  • arguments
    • dev : 割り込みベクタのマスクされた新PS。適切に設定されていればminor番号は正しい値が入っている
  • 8081 : kl11[ ]から適切なtty構造体を取得
  • 8082-8083 : デバイスの受信data buffer registerから値を取得
  • 8084 : デバイスの受信status registerをセットして、受信ready状態にする
  • 8085-8086 : 受信したデータが空だったら、送信data buffer registerに受信結果を格納して端末に送信する?なんのため?
  • 8087 : ttyinput( )を実行し、raw queueに受信したデータを追加する
ttyinput( )
  • arguments
    • ac : 入力文字(1byte data)
    • atp : tty構造体のポインタ
  • 8342-8343 : 入力文字がリターン(\r)で、かつ、端末がリターンのみで改行を扱っていたら、入力文字を改行(\n)に変更する
  • 8344-8348 : 端末が無処理モード(入力文字列をそのままプログラムに送るモード?)で動作しておらず、かつ、入力文字がquitかdeleteならば、プロセスにSIGINTかSIGQITシグナルを送り、flushtty( )でキューを空にしてreturn。そのプロセスを終了させる
  • 8349-8351 : raw queueにデータが溜まりすぎていたら、flushtty( )でキューを空にしてreturn. 必要以上に端末から入力をすると処理されず捨てられてしまう
  • 8353-8354 : 端末が小文字しか扱えない場合、入力文字を大文字から小文字へ変換する
  • 8355 : putc( )を使って、入力文字をraw queueへ送る
  • 8356-8359 : 端末が無処理モードで動作しているか、入力文字が改行かeot(Ctrl-D)ならば、端末から入力を待っているプロセスを起こし、デリミタをraw queueへ送り、デリミタカウンタをインクリメントする
  • 8361-8363 : 端末がECHOモードならば、入力された文字を端末に出力するためにttyoutput( )とttstart( )を実行する
klread( )
  • arguments
    • dev : device No
  • 8063 : kl11[ ]から適切なtty構造体を取得しttread( )に渡す
ttread( )
  • arguments
    • atp : tty構造体のポインタ
  • 8541-8542 : 端末がオープンされていなければreturn
  • 8543-8544 : canonical queueにデータが残っているか、canon( )の結果が0でなければ、canonical queueの内容をpassc( )でユーザ領域に送る
canon( )

raw queueからcanonical queueへデータを送り込む関数で、canonical queueにデータが格納されたら1が返ってくる

  • arguments
    • atp : tty構造体のポインタ
  • 8283 : プロセッサ優先度を5に上げる
  • 8284 : 無処理キューのデリミタカウンタが0ならば
    • 8285-8287 : 端末がオープンしていなければ0でreturn. オープンしていれば、端末から入力データが送られてくるまで寝る
  • 8289 : プロセッサ優先度を0に戻す
  • 8291 : 作業用配列(文字列)の3文字目を見るように設定。後の処理で、見ている文字の2文字前まで見るケースがあるので3文字目を見るように設定している
  • 8292 : raw queueのデータを1byte取得しては処理を繰り返す
    • 8293-8295 : 取得した文字がデリミタ(all 1)ならば、デリミタのカウンタをデクリメントしてループを抜ける。デリミタで区切られた文字列単位で処理を行う?
    • 8297 : 端末が無処理モードで動作していなければ
      • 8298-8307 : 先行した文字がバックスラッシュでなければ。文字がeraseで、削除する文字があるならばbpをデクリメントし処理を続行。文字がkillならば、loopに戻って蓄積していた行内のデータを捨てる。eot(Ctrl-D)ならばそれを無視する。
      • 8308-8312 : 先行した文字がバックスラッシュならば特殊文字の処理を行う。maptab[ ]を見て0でない(特殊処理が必要)、かつ、取得した文字と対応するmaptab[ ]の内容が同じか、端末が小文字しか扱えないならばif文が真になる。バックスラッシュの前の文字がバックスラッシュでなければcにmaptab[ ]の内容をコピーする。その後bpを一文字戻す。バックスラッシュが2つ続いていた場合、それは1文字バックスラッシュを表す
    • 8315 : 作業用文字列に文字を追加
    • 8316-8317 : 作業用文字列がいっぱいになったらbreakでループを抜ける
  • 8319-8323 : 作業用文字列に格納した文字列をcanonical queueに追加
  • 8324 : 1でreturn
wflushtty( )

output処理が完了されるまで寝る。完了したら入力queueを空にする

  • arguments
    • atp : tty構造体へのポインタ
  • 8222 : プロセッサ優先度を5に
  • 8223-8225 : out queue(端末へ出力待ちの状態にあるデータ)にデータが残っていたら、tty構造体をASLEEP状態にして寝る
  • 8227 : flushtty( )を実行
  • 8228 : プロセッサ優先度を0に戻す
flushtty( )

tty構造体のqueueを空にする。

  • arguments
    • atp : tty構造体へのポインタ
  • 8258-8259 : tty構造体のcanonical queue, out queueを空にする
  • 8260-8261 : canonical queue, out queueを待っているプロセス(どんなプロセス?)を起こす
  • 8262 : PSを退避
  • 8263 : プロセッサ優先度を5に
  • 8264 : raw queueを空に
  • 8265 : デリミタカウンタを0にクリア
  • 8266 : 退避していたPSを元に戻す
klwrite( )
  • arguments
    • dev : device No
  • 8067 : kl11[ ]から適切なtty構造体を取得しttwrite( )に渡す
ttwrite( )

out queueにユーザプログラムから渡されたデータを追加する。

  • arguments
    • atp : tty構造体のポインタ
  • 8556-8557 : 端末がオープンしていなければ何もせずreturn
  • 8558 : ユーザプログラムから送られてきたデータを1文字ずつ処理
    • 8559 : プロセッサ優先度を5に上げる
    • 8560-8564 : out queueにデータが溜まっていたら(出力待ちデータが溜まっていたら)、ttstart( )を実行して、出力処理を促し、自分はASLEEP状態になって寝る。出力待ちデータがある程度消化されると起こされる
    • 8565 : プロセッサ優先度を0に戻す
    • 8566 : ttyoutput( )を実行して、out queueに文字を追加する
ttstart( )
  • arguments
    • atp : tty構造体のポインタ
  • 8514-8516 : SSTART状態のとき。何を意味する状態なのかよくわからない
  • 8518-8519 : 端末が送信readyでないか、送信遅延処理中ならばreturn
  • 8520 : out queueにデータがあれば、キューの先頭から1文字取得して
    • 8521-8522 : 取得した文字が7bit以内のデータならASCII文字。それを端末に出力するためにKL data buffer registerに値をセットする
    • 8523-8525 : ASCII文字でなければ、それは遅延を合図するためにout queueに挿入された文字(どこで挿入される?)。timeout( )を呼んで、時間が経過したらttrstrt( )が呼ばれるように設定する。そしてTIMEOUT状態にして、端末への文字出力処理を抑制する
ttrstrt( )

TIMEOUT状態(端末への出力処理を抑制している状態。出力を遅延させている)を解除する。

  • arguments
    • atp : tty構造体のポインタ
  • 8491-8492 : tty構造体のTIMEOUT状態を解除し、ttstart( )を実行し、端末への文字出力を行う
ttyoutput( )
  • arguments
    • ac : 出力文字
    • tp : tty構造体のポインタ
  • 8386-8387 : 出力文字がeot(Ctrl-D)で、かつ、無処理モードでなければ何もせずreturn
  • 8390-8395 : 出力文字がtab(\t)で、かつ、端末がtabの代わりに空白を出力するモードならば、行内の出力文字数が8の倍数になるまで空白を出力してreturn
  • 8399-8406 : 端末が大文字を扱えないならば'('などの特殊文字に対してバックスラッシュを出力する
    • 8407-8408 : 小文字を大文字に変換
  • 8412-8413 : 端末がリターン(\r)のみで改行を表していたら'\n'を'\r'に変換する
  • 8414-8415 : out queueに出力文字を追加。追加に失敗したらreturn
  • 8423-8478 : 出力文字が偶数パリティを持つ8bitのデータになるように調整
klxint( )
  • arguments
    • dev : 割り込みベクタのマスクされた新PS。適切に設定されていればminor番号は正しい値が入っている
  • 8072 : kl11[ ]から適切なtty構造体を取得
  • 8073 : ttstart( )を実行し、out queueのデータが残っていたら端末への文字出力を続けさせる
  • 8074-8075 : out queueのデータが空になるか、ある程度消化されたら、out queueのデータ消化待ちで寝ていたプロセスを起こす

終わりに

ようやくLions本の内容を全て追うことができました。このUNIX v6 code readingメモ書きも最終回を迎えました。

さらっと流したところをもっと詳細に追いたい、マニュアルやドキュメントにもっと目を通したい、ユーザランドのcodeも読みたい(libc相当のものやinit, getty, login, sh, その他)、もっとわかりやすい絵を追加したい、もっとわかりやすい文章に見直したい、などなど色々な欲望は残っているのですが、一応一区切りです。

今まで見ていただいてどうもありがとうございました。