UNIX 6th code reading - 対話型端末
はじめに
今回は対話型端末の処理方法を見ていきます。
Lions本で言うと24章にあたります。
対話型端末とは
対話型端末とは何なのか、まずは調べてみました。
テレタイプ端末というのがそれにあたるようです。
http://ja.wikipedia.org/wiki/%E3%83%86%E3%83%AC%E3%82%BF%E3%82%A4%E3%83%97%E7%AB%AF%E6%9C%AB
上記リンク先に載っている写真のように、キーボード(タイプライタ)とディスプレイ(プリンタ)が1つになったような装置です。
YouTubeにテレタイプ端末の1つであるASR-33という端末を使ってPDP11/40を操作している動画がありました。
この動画で、テレタイプ端末がどういうものかわかったと思います。
ttyというキーワードが出てきますが、これはテレタイプ端末(tele type writer)から来ているようです。今でもUNIX系のコマンドにttyコマンドというものがあります。
tty構造体
ここからカーネルの実装を見ていきます。
7926行目からtty構造体が定義されていて、この構造体を使用して端末制御のコードが実装されています。
struct tty { struct clist t_rawq; struct clist t_canq; struct clist t_outq; int t_flags; int *t_addr; char t_delct; char t_col; char t_erase; char t_kill; char t_state; char t_char; int t_speeds; int t_dev; };
最初の3つのclistはデータ転送に使用するキューです。各キューの使い方は、次回のエントリで確認する予定です。
sttyシステムコール
sttyシステムコールを使って、カーネルで使用しているtty構造体の一部データをセットすることができます。
tty構造体のうち、データをセットできるのは以下の要素です。フラグ、削除文字(デフォルトでは'#')、行クリア文字(デフォルトでは'@')、動作スピードを制御できます。
int t_flags; char t_erase; char t_kill; int t_speeds;
ユーザプログラムで、以下のarg構造体に値を入れ、それをsttyシステムコールに渡すことで端末の設定を行うことができます。
struct { char ispeed, ospeed; char erase, kill; int mode; } *arg;
stty( )
stty( )(8183行目)はsttyシステムコールのハンドラです。端末の情報を設定します。
端末から情報を取得するgttyシステムコールとは、処理がほとんど共通で、処理の大部分を共通処理関数であるsgtty( )に任せています。
sgtty( )は、端末に対応したinodeを取得し(ユーザからファイルディスクリプタが渡され、u.u_ofile->file->inodeと辿ることで取得できる。inodeはキャラクタ型でないといけない)、そこからdevice Noを取得します。
device Noが取得できるとmajor番号とminor番号がわかります。major番号を使ってキャラクタデバイスドライバのtty制御ハンドラ(d_sgtty( ))を呼び出し、その中でminor番号を使って適切なttyを取得し、情報の設定・取得を行います。
gttyシステムコール
gttyシステムコールを使って、カーネルで使用しているtty構造体の一部データを取得することができます。
上記arg構造体をgttyシステムコールに渡すと、現在の端末の情報が格納されて返ってきます。
gtty( )
gtty( )(8165行目)はgttyシステムコールのハンドラです。
stty( )との共通処理関数であるsgtty( )を実行して処理の大部分を任せます。
情報が取得できたら、ユーザから渡されたarg構造体に結果を格納してユーザに返します。
KLデバイスドライバ
次にKLのデバイスドライバ(unix/kl.c)を見ていきます。
KLのレジスタ
ソースを見る前に、KLが持っているレジスタを確認します。
2bytesのレジスタを4つ持っています。受信、送信でそれぞれステータスとデータを扱うレジスタがあります。
- 受信status register(rcsr)
- 受信data buffer register(rbuf)
- 送信status register(tcsr)
- 送信data buffer register(tbuf)
デバイスドライバの中では8008行目あたりでKLのアドレスが設定され、レジスタアクセス用の構造体が定義されています。
#define KLADDR 0177560 #define KLBASE 0177500 struct klregs { int klrcsr; int klrbuf; int kltcsr; int kltbuf; }
KL端末は複数存在し得、各端末ごとに独立したアドレスを持っています。KLADDR, KLBASE, 端末のmajor, minor番号から適切なアドレスを決定し、addr->klrcsrなどとすることで目的のレジスタを触ることができます。
tty構造体
KLのデバイスドライバではtty構造体の配列であるkl11[ ]が定義されており(8015行目)、端末の個数分tty構造体が用意されます。各端末に対応するminor番号を使って、kl11[ ]から適切なtty構造体を取得して使用します。
open, close処理
klopen( )(8023行目), klclose( )(8055行目)が端末のopen, close処理を行うハンドラです。
端末情報は子プロセスに引き継がれる(forkではu.u_ofileとp.p_ttypはコピーされる)ので、通常は一度しかopen処理は行われません。
また、その端末を使用しているプロセスが全ていなくなるとき(ログアウト時やpower off時?)にclose処理が行われます。
klopen( )はtty構造体とKLのレジスタに適切な値をセットします。klclose( )はtty構造体を初期化します。
その他の読み、書き、読み書き割り込みハンドラは次回のエントリで見ていく予定です。
コードメモ
stty( )
- 8187-8190 : u.u_arg[0]に上で書いたarg構造体のポインタが格納されている。u.u_arg[0]がspeedを、u.u_arg[1]がerase, killを、u.u_arg[2]がmodeを指すように設定
- 8191 : sgtty( )を呼び出す
gtty( )
- 8171 : tty構造体のデータを格納するためのint[3]のポインタを引数にsgtty( )を実行
- 8172-8173 : sgtty( )実行中にエラーがあればreturn
- 8174-8177 : 引数で渡されたarg構造体に、tty構造体から取得したデータを格納する
sgtty( )
- arguments
- v : set, get flag. 0 -> set, 1 -> get. d_sgtty( )の引数に使われる
klsgtty( )
- arguments
- dev : device No
- v : set, get flag. ttystty( )の引数に使われる
- 8093 : tty構造体の配列であるkl11[ ]から、KL用のtty構造体を取得する
- 8094 : 8093行目で取得したtty構造体と引数のvを使ってttystty( )を呼ぶ
ttystty( )
- arguments
- atp : tty構造体へのポインタ
- av : read, write flag
- 8582 : 引数のflagが0でなければ(tty構造体への値セットならば)
- 8583-8587 : tty構造体のspeed, erase, kill, flagsをセットし、1でreturn.
- 8589 : tty構造体からデータを読み出す場合。wflushtty( )を実行し、キューを空に
- 8590-8595 : ユーザから渡されたarg構造体のデータをtty構造体(実際はint[3]. gtty( )参照)へ格納し、0でreturn.
klopen( )
- arguments
- dev : device No
- flag :
- 8026-8028 : minor番号の妥当性チェック。問題があればENXIOエラーでreturn
- 8030 : kl11[ ]から、minor番号を使って、扱うべきtty構造体を取得
- 8031-8033 : 端末をopenしようとしているプロセスが端末を持っていなければ、先ほど取得したtty構造体を割り当て。tty構造体にdevice Noをセット
- 8039-8044 : 端末のデバイスのregisterのアドレス計算
- 8045 : 端末の状態をチェックし、オープンしていなければ
- 8046-8049 : 端末の状態初期設定(tty構造体)
- 8051 : 端末のregisterセット
klclose( )
- arguments
- dev : device No
- 8057 : kl11[ ]から適切なtty構造体を取得
- 8058 : wflushtty( )を使って、tty構造体が持つキューを空にする
- 8059 : tty構造体のstateを0クリア