![](https://habrastorage.org/web/e86/4ed/36b/e864ed36bd8144ec84b264c7b941ccab.jpg)
このギャップを修正してソースコードを開きたいですか? TTY、MASTER、SLAVE、N_TTY、VT、PTS、PTMX ...概念の山、仮想デバイス、厄介な魔法? すべてがどのように始まったかを覚えていれば、これはかなり論理的な絵になります...
1.スクラッチから始めて、落ち着いて
TTY:パレオゾイ
![](https://habrastorage.org/web/2f6/e80/44b/2f6e8044bc8a42769493222aef4cc121.jpg)
サブスクライバAは、電気信号に変換されるキーボード文字を入力します。 最も普通のケーブルでは、信号は加入者Bのテレタイプに「実行」され、普通の紙自体にすでに印刷されています。 信号がデュプレックスの場合、非常に幸運であり、サブスクライバーBはすぐに答えを書くことができます。 そうでない場合は、フィードバックのために最初に2本目のワイヤを接続する必要があります。
![](https://habrastorage.org/web/a25/045/dec/a25045decb2c4e3880d64778b654963d.jpg)
ここで、Teletype Corporationでは、彼らはまだ近い将来に製品の将来がどうなるかを知らず、確かに彼らはTTYの略語がテレタイプより長生きすることを疑っていません。 彼らの陰謀を損なうのではなく、先に進みましょう。
TTY:MEZOZOI
![](https://habrastorage.org/web/850/04c/e89/85004ce8945e4d36b2dc4010e32aa470.jpg)
事実、主要なエンジニアリングマインドは、車輪を再発明せず、既存の安価で手頃な価格のメカニズムを新しいニーズに適合させないことに決めました。 テレタイプはコンピューターに直接接続され(以前のように別のテレタイプではなく)、このビジネスをコンソールと呼びました 。 入力オペレーターは、入力された文字が紙に即座に印刷される方法を確認しますが、これはOSに参加せずに発生します- コンソールにタイプライターが保存される原理のおかげです。
TTY:パレオゲン
![](https://habrastorage.org/web/f10/8eb/6cd/f108eb6cd72a416b97519b0675be2cc4.jpg)
- rawモード (行編集は実行されません;エスケープシーケンスは通常の文字として認識されます;入力された文字はすぐにプロセスに転送されます);
- クックモード (特殊文字が認識され、プロセスに対して停止および割り込み信号が生成されます。終了キーはReturnキーを押した後にのみプロセスに転送されます)。
予想される質問:テレタイプが既に印刷したものをどのように「消去」できますか? 視覚的な編集操作のために、Unixバージョン7は特定の文字の印刷を提供します。たとえば、@-行全体を消去し、#-最後の文字を消去します。 つまり、テレタイプがld @ lk#sを出力し、オペレーターがReturnキーを押すと、lsコマンドが実行されます。 これはTTY LINE DISCIPLINEではありません(後で説明します)が、OSレベルでの入力処理に関してはすでに大きな一歩です。
![](https://habrastorage.org/web/43e/a21/3ee/43ea213ee4df456c98010ef97c7c06fe.jpg)
右に目を向けます:これはVT100です。これはPDP-11と恋に落ち着いて調和できる最初の端末の1つで、Unixバージョン7でサポートされています。
OSレベルでは、コンソールとスマートターミナルの両方が、非同期データストリームを一連の文字に変換するUARTインターフェイスを介して接続する文字デバイスとして認識されるようになりました。 OSの場合、これらは基本的に同じです。唯一の違いは、端末画面の文字を消去するか、テレタイプを使用して顔の文字を印刷するかです。
![](https://habrastorage.org/web/5ad/ba0/f06/5adba0f068db4a8d9538539c88087b57.jpg)
左側には、コンピューターとコンソール(またはスマート端末)の相互作用の一般的な図が示されています。 このスキームには、PDP-11コンソールのオペレーターにまったく喜ばれない1つの欠点があります。1つのセッション(またはセッション)は1つのコンソールに関連付けられ、ユーザーはバックグラウンドで複数のプロセスを開始できますが、一度にアクティブになるのは1つのTTYだけです一つ。
そして、私たちの貧しいオペレーターは、文字通りの意味で、突然複数のセッションで作業するようになった場合、あるコンソールから別のコンソールに切り替えることを強制されます。
TTY:ネオゲン
![](https://habrastorage.org/web/028/d7e/a7c/028d7ea7c2cb443cb22ff2aa244bc461.jpg)
さらに、モノクロターミナルの寿命を延ばすことができるようになりました。CGA、Hercules、EGAグラフィックがサポートされています。 貧しいオペレーターは呼吸が楽で、鉄の獣の形のテレタイプ(およびスマート端末)は悪夢でのみ彼を脅かします。
ただ一つのことをオペレータにアドバイスします:何のために/ devディレクトリに行かないでください-結局のところ、いくつかのttyXが彼を待っており、同じ古き良きテレタイプが仮想コンソールの内部に住んでいることを思い出させます。
TTY:アントローゲン
前のステップで、コンソールを仮想化しました(PC MAGAZINEのソリッドエディションは嘘をつきません)。 それはどういう意味ですか-I / Oメカニズム全体が書き直され、根本的に変更されましたか? それでは、なぜ仮想デバイスはまだttyXなのですか? 簡単です。仮想コンソールは最も物理的なものとしてエミュレートされ、UARTドライバーの場所はおそらく他の誰かが取っています。 変更されたスキームについて、もう少し詳しく説明します。
フィニッシュラインには1つのステップがありますが、少なくともLinus Torvaldsを尊重しない限り、見逃すことはできません。 1993年になり、ついにLinux 0.95のソースコードを調べる幸運が訪れました。 なぜこのリリースなのか? その中で、最新のディストリビューションに最も近いTTY抽象化がすでに形成されています。3つの別個のレイヤーが形成されています( TTYX-TTY_LINE_DISCIPLINE-TTY_DRIVER )。
さらに、Linux 1.0は1年後にリリースされ、XFree86プロジェクトによって提供されるウィンドウインターフェイスが表示されます。 この時点から、 仮想端末も仮想コンソールに追加され、ユーザーはグラフィカルシェルを離れることなく起動できます(ただし、改善されたio-magicの微妙さと詳細に突入する前に、Ubuntuに戻ります) 04/16。
2.ブッシュの周りのビートを止め、内側を見る
仮想クリーチャーとその場所
/ dev /ディレクトリ内の一部のデバイスのみが毎日使用されます:/ dev / sdaX、/ dev / mem、/ dev / zero、/ dev / random ... これらのデバイスは、/ ttyX、/ vcsX、/ vcsaX、および/ ptmxおよび/ pts / Xです。 実際には、それらについてさらに議論します。
![](https://habrastorage.org/web/cf1/c99/181/cf1c9918187f4e3aa880d368fc1b35b4.jpg)
そして、最初のオブジェクトは仮想コンソールです。 このような各オブジェクトには、少なくとも
彼らとチャットする機会があるかどうかを確認してください。 Ctrl-Alt-FX(またはchvt X、Xはコンソール番号、たとえばCtrl-Alt-F1)を実行し、Xが1、2 ... 6.に等しくなることに注意してください。同時に、最初の起動時に仮想コンソールが開きます。ユーザー名とパスワードを入力して、新しいセッションを作成するように求められます。 Xが7の場合、ネイティブグラフィックペナティットに戻り、/ tty7がXServerに関連付けられていることを理解します。 どうぞ 8、9、10から63まで—人生の兆候はありません。
![](https://habrastorage.org/web/021/487/1a5/0214871a570d42b09b69b776580d729b.jpg)
コンソールはいくつかの段階で初期化されます。 最初に、サブシステムの初期化中にGrubから制御を受け取ったカーネルは、デバッグ情報を表示するように設計されたプライマリコンソール(ブートコンソール)を作成するconsole_init関数を呼び出します。 このコンソールは、文字列の出力を最も基本的な方法で実行します。BIOSに直接アクセスし、biosregs構造を初期化および設定する「putchar」を介して、0x10割り込みを使用して文字をコンソールに出力します。
その後、「fs-initcall」と「console-initcall」の実行中に、6つの本格的な仮想コンソール(「実コンソール」)の仮想デバイスと仮想構造が作成されます。 これらのコンソールのアクティブ化は、最初のカーネルプロセス/ sbin / initによって実行されます。これは、gettyプログラムを起動し、構成ファイル/etc/init/console.confおよび/etc/init/ttyX.confを読み取り、その後、ウェルカムファイルなどの内容をコンソールに表示しますログインして起動します。 次に、XServerはdev / tty7でコンソールのアクティベーションを開始し、そこでグラフィカルシェルが実行されます。
![](https://habrastorage.org/web/676/3df/0f6/6763df0f6a3e49efbc2646991cedc175.jpg)
ただし、まだ質問があります。 不明なオブジェクト/ dev / tty0とは何ですか? また、各/ dev / ttyXが仮想コンソールデバイスである場合、なぜ/ dev / consoleと/ dev / ttyが必要なのでしょうか? 答えを得るには、tty1に移動し(Ctrl-Alt-F1を押して)、バックグラウンドで次のスクリプトを実行します。
sleep 10 echo “tty0” > /dev/tty0 echo “tty” > /dev/tty echo “console” > /dev/console
次に、たとえばtty4に移動して、数秒待ちます。 終了後、次の図が表示されます。
![](https://habrastorage.org/web/cf7/8a9/3a6/cf78a93a6dc542d1b28d8ba1e1a21484.jpg)
ロールの分布が明確になります:/ dev / tty0 = / dev / console = current console、つまり どちらも常に現在目の前にあるコンソールに関連付けられており、/ dev / ttyはプロセスが開始されたコンソールを「記憶」しています。 したがって、プロセスの実行中に、現在のアクティブなコンソールに応じて、プレイ中に/ dev / tty0および/ dev / consoleが決定されましたが、/ dev / ttyは変更されませんでした。
/ devディレクトリを離れるのは急いでいません。 さらに興味深いオブジェクトが12個以上あります:/ dev / vcsX(仮想コンソール画面)および/ dev / vcsaX(属性付き仮想コンソール画面)。 別の経験:tty5に移動し、滞在の痕跡を残してから、他のコンソールに移動し(番号3にします)、/ dev / vcs5で「cat」を作成し、去ったコンソール5の状態を正確に確認します彼女は数秒前に。 この場合、それぞれ、/ dev / vcs3と/ dev / vcs(および/ dev / vcsa)は現在使用中のコンソール3を参照します。
/ dev / vcsXは
![](https://habrastorage.org/web/f05/de1/da9/f05de1da96304620a5752d9ab52190d4.jpg)
トップビューベター
それでは、しばらくtty動物学の研究を止めて、仮想デバイスがその一部であるtty抽象化そのものに進みましょう。 tty-complexの一般的な構造を見て、3つのコンポーネントを選択しましょう。
- / dev / ttyXは、ファイルシステム内の仮想コンソールデバイスであり、UARTドライバーに取って代わり、既によく知っています。 デバイス/ dev / vcsXと/ dev / vcsaXは同じレベルにあり、それらとの通信は/ dev / ttyXを介して直接実行されます。
- TTY Line Disciplineは、入力されたコマンドのECHOを作成し、編集する機能を提供するドライバーです。 また、この層のドライバーは、一連の制御シーケンス中に信号を生成します(^ C、^ Zなど)。 デフォルトでは、ここでN_TTYが支配しますが、このモジュールは、たとえばドライバーに置き換えることができます。これについては、後ほど実験します。
- TTYドライバーは、コンソールを初期化して開くための一連のメソッドと、I / O操作を処理し、切り替え時にコンソールを一時停止し、再開するメソッドを提供するドライバーであり、もちろん、ユーザーから受け取ったコマンドがアクティブなプロセスに転送されることを保証します。
PDP-11オペレーターの苦情を覚えていますか? 彼は、ある物理コンソールから別のコンソールに移動するのに時間を費やすことを嫌いました。 現在の状況は次のとおりです。デフォルトでは7つの仮想コンソールがあり、その前には車輪付きのオフィスチェアがあります(もちろん仮想)。 あるコンソールから別のコンソールに切り替えると、オペレーティングシステムは椅子を目的のTTYに移動し、椅子と一緒に物理ioデバイスのセットも「切り替え」ます。新しいコンソールの状態はモニター、キーボード入力、など
同時に、最初のttyからのプロセスは引き続き動作します。仮想コンソールのファイルからコマンドを読み取り、このファイルに書き込みますが、「椅子」から引き裂かれているため、イベント(同じ^ Cおよび^ Z)を受信しません。物理デバイスは「椅子」とともに「左」-「椅子」が戻るとすぐに、バッファに「出力」を蓄積してモニターに送信することしかできません。
![](https://habrastorage.org/web/607/06d/d58/60706dd5893c4aaab92dcf16ee062935.jpg)
地球センターへの旅
はい、すべてが上から非常に見栄えのするように見えます。 しかし、%username%は、ttyコンポーネントの3レベルの対話がどのようにコードに直接実装されているかを確認したいでしょうか? 答えを得るには、天国から地上へ、さらに言えば-アンダーグラウンドで、Ubuntuソースコードの腸に入る必要があります(カーネルバージョン4.4で作業します)。
積極的に動きましょう-tty抽象化がどの構造を介して単一の全体にリンクされているかを把握します。
まず、tty_ldiscフィールド(これは2層目のドライバーメソッドの構造です)、tty_driverフィールド(3層目のドライバーです)、tty_operations(ドライバーメソッドの構造です)を持つtty_structです。 3番目のレイヤーは、便宜上、「tty_struct」で直接レンダリングされます)。
![](https://habrastorage.org/web/82b/e02/9dc/82be029dc643498482dd596c696b913e.jpg)
つまり、tty_structはTTY_LINE_DISCIPLINEおよびTTY_DRIVERレイヤーへのアクセスを提供します。 それにアクセスできました-tty-abstractionのスタックの2/3です。目の前にあると考えてください。 ここで、仮想デバイスファイルからこの構造への移行がどのように実行されるかを理解する必要があります。 答えは簡単です。構造 "tty_file_private"には、タイプ "tty_struct"のフィールドがあります。 したがって、第1レベルの仮想デバイスのファイルを参照すると、簡単に上位レベルにアクセスできます。
パズルは開発中ですが、これだけでは十分ではありません。 (qemuとcgdbを使用して)カーネルを調べ、ユーザーがキーボードから入力した1文字の出力(エコー)のバックトレースを検討します。
![](https://habrastorage.org/web/0ef/c0c/640/0efc0c640299474da2122da6a3fb7021.jpg)
したがって、私たちはtty-stack Iレベルにいます。 システムコール「write」があり、ttyで関数「tty_write」によって処理されます。 仮想デバイスのファイル構造へのポインターと、シンボル付きのバッファーが渡されます。 tty_write関数では、tty_structのインスタンスがファイルから受信されます。 ゲームを受け入れると、「tty_struct」は最初にドライバーTTY_LINE_DISCIPLINE-「tty_ldisc」を呼び出します。デフォルトはN_TTYです。 最初のレベルが完了しました!
N_TTYはバトンを取得します。次に、n_tty_writeメソッドを呼び出してから、バッファをoutput_process_block関数に渡します。これにより、キャリッジ以外の位置文字が入力されたことを確認して、tty_structにtty_driverを呼び出します。 そうです、レベルIIIに移行しています。
「Tty_struct」は仲介者の役割を正常に果たしており、現在-「console_driver」と呼ばれるttyドライバーの「con_write」メソッドがすでに実行されています。 3番目のレベルのドライバーはその仕事を喜んで行いますが、それは1つです。多くのコンソールがあります。 「Tty_struct」が再び助けになり、ドライバーに「vc」構造の必要なインスタンスを与えます(特定のコンソールの状態を担当し、キーボード、画面設定、および一連のグラフィカル表示メソッドを含みます)。
Console_driverはコンソールをロックし、vc_dataを呼び出して最終的にキャラクターをエコーします。 Vc_dataは恐怖で気づきました。彼らは、彼女に任せられたコンソールの幸福の問題ではなく、行動のために彼女に目を向けました。 これはただ一つのことを意味します:今度のケースではVGAが表す「consw」メソッドの助けを求める時です(異なるカーネル構成では、例えばフレームバッファかもしれません)。 そして確かに-VGAはすぐにそれを取り上げ、カーソルを隠し、文字を印刷し、必要に応じて画面をスクロールするか新しい行に移動し、カーソルを移動して表示します。 「Vc」が息を吐きます:「console_driver」がジョブを受け入れ、コンソールのロックが解除されました。 また、3つのレベルがすべて正常に完了し、各コンポーネントがその使命を果たしたため、息を吐くことができます。
![](https://habrastorage.org/web/0e5/aa6/d8e/0e5aa6d8e51742bcb7f4937df9b48f33.jpg)
3.仮想端末に目を向ける
しかし、それだけではありません。今度は、 ターミナルエミュレータを使用して 、さらに別のクラスの住民/開発者の代表者と知り合うときです。 これらは、たとえばCtrl-Alt-TまたはCtrl-Shift-Tを使用して、グラフィカルシェルを備えたコンソールから起動するのと同じxtermまたはgnome-terminalです。
それらは個別の鳥小屋/ dev / pts(=擬似端末スレーブ)にあり、0、1、2などの番号が付けられたファイルです。 現在のターミナルでpsを実行し、確認します-/ dev / pts / 1です。 Alt + 5を押します-仮想デバイスファイルが/ dev / pts / 20であるターミナルを開く順に4番目に移動します。 どの端末でも、bashは私たちに会います;各端末には独自のプロセスのセットがあります。 これまでのところ、驚きはありません。
![](https://habrastorage.org/web/d66/d9a/36c/d66d9a36c26746c8a65b5acb7118e4d9.jpg)
![](https://habrastorage.org/web/860/833/3ff/8608333ffc3d43e2a51d9b256f6f77c6.jpg)
しかし、注意:/ dev / pts / Xは動的に作成され、同じコンソールで実行されます/ dev / tty7、ログインを必要とせず、最も興味深いのは「椅子」ルールがないことです:複数の仮想端末を開いて同時に作業方法を観察できますそれらのそれぞれ。 繰り返しますが、%username%には疑問があるかもしれません:tty抽象化の原則はここに保存されていますか?スレーブデバイスはこの原則にどのように適合しますか?
新しいオブジェクトはそれ自体を待たせません:単一のコピーでは、ptmxファイルは同じディレクトリ/ devにあります(実際には、複数の場合があります:グラフィカルシェルを備えた複数のコンソールが実行されている場合)。 人によると、/ dev / ptmxを開くと、その先頭部分「ptm」に関連付けられた擬似端末/ dev / pts / Xの下位部分が作成されます(アクセスはファイル記述子を通じて行われますが、実際のファイルは作成されません)。 次に、「ptm」がgrantptおよびunlockpt関数に渡されます。これにより、/ dev / pts / Xを直接開くことができます。これは、仮想コンソールとまったく同じように動作します(上記の機能を除く)。
私たちにとって、tty抽象化の精神では、これは次のことを意味します。ユーザーがターミナルエミュレータを実行したい場合、XServerは/ dev / ptmxを呼び出して仮想デバイス/ dev / pts / Xを作成します。 強力な「マルチプレクサ」/ dev / ptmxが親切にこれを行い、デバイスファイルをターミナルインスタンスに割り当て、... / dev / pts / Xが/ dev / ttyXの代わりになり、レイヤードライバーTTY_LINE_DISCIPLINEがそれに割り当てられ、TTY_DRIVERがそれを穏やかに引き継ぎます。 / dev / pts / X上のスタックは、見慣れた外観になります。 ターミナルエミュレータのメカニズムを研究するタスクは、 仮想コンソールを使用してスムーズに前のストーリーに要約しますが、詳細な研究には別の記事が必要です(これは将来の計画に含まれています!)。
![](https://habrastorage.org/web/3a2/f47/5da/3a2f475da6c841f18c8cd24256d145ca.jpg)
4. TTY LINE DISCIPLINEで遊ぼう
ちょっと、tty-「古生代」を思い出してください。TTY_LINE_DISCIPLINEレイヤーが独立したレイヤーでもなく、本格的な最新の機能を持っていなかった時代がありました。 それ以降に行われた変更の重みを推定してみましょう。
最初に、N_TTYを実際に処理していることを確認します。
![](https://habrastorage.org/web/1f0/6cb/cb2/1f06cbcb2ef747159e96d07e21895c84.jpg)
比較ではすべてがわかっているため、根本的に行動し、sttyの助けを借りてすべての便利なN_TTY機能を無効にします。
![](https://habrastorage.org/web/af7/d7d/ebf/af7d7debf89f450fa9881770e761e119.jpg)
結果は、N_TTYの責任範囲を明確に示しており、これがないと、出力はフォーマットされず、入力は表示されません。 さらに、新しいターミナルを開くと、LINE_DISCIPLINEの整合性と整合性が確信できます。 私たちが得る効果は、どのコンポーネントが入力全体を処理するかを正確に知っていることを示唆しており、各仮想端末に対して個別に変更することができ、モジュールをロードすることでこのコンポーネントを置き換えることができると聞いたことを覚えています。
残念ながら、N_TTYはそれ自体がカーネルの一部です。 そのため、Linuxで提供され、モジュールとしてロードされるLINE_DISCIPLINEレイヤーの他のドライバーをベースとして使用します。 それらのイメージと肖像で、n_tty.cソースファイルを変更します。
- モジュール出荷関数を追加します。この関数では、tty_register_ldisc関数が呼び出されます。これにより、カーネルが私たちの規律に馴染みます。 この関数の最初のパラメーターは一意の識別子であり、2番目はドライバーメソッドを持つ構造体へのポインターです。
- モジュールを「アンロード」する関数を追加します。この関数では、それぞれtty_unregister_ldiscが呼び出されます。
- 構造「tty_ldisc_ops」で、新しいドライバー名を設定します。
- モジュールがtty_io.cファイルから必要な関数を「認識」することを確認します(「EXPORT SYMBOL」マクロでは満足できません。これにより、必要なすべての関数を手動で追加するか、tty_io.cとリンクする必要があります)。
![](https://habrastorage.org/web/c7c/15f/a4a/c7c15fa4aad348ddb45988adcf9b9ea2.jpg)
ここで、私たちの規律のラインを元のラインと区別するいくつかの機能を追加します。 サービスシーケンスを処理するのはTTY_LINE_DISCIPLINEであるため、このフィールドを思い起こさないことは罪です。 これを行うには、関数「n_tty_receive_char_special」を開きます。TTY_LINE_DISCIPLINEは、入力された文字が特殊かどうかを確認し、見つかったときに対応する信号を送信します。 たとえば、Ctrl + ZとCtrl + Cに対して生成された信号を交換します。
![](https://habrastorage.org/web/a5a/bf3/f60/a5abf3f6014c40c0a18af7f8bfdc0550.jpg)
その後、修正したファイルから直接カーネルモジュールour_ldisc.koを取得します。 ダウンロードして、ダウンロードが成功したことを確認してください。 「our_modyfied_ldisc」が実際にTTY_LINE_DISCIPLINEとして登録されていることを確認します。 ターミナルを開いて、pts番号を見てみましょう。 その後、ドライバーを/ dev / pts / XのTTY_LINE_DISCIPLINEレイヤーに対応させます。
![](https://habrastorage.org/web/e7d/988/c83/e7d988c83b304e63bedbacdc8ab1ecb0.jpg)
「stty echo cooked」コマンドを使用して、新しいディシプリンラインを設定します。これで、ターミナルは通常のモードで動作します。 永遠のサイクルでテストプログラムを実行し、Ctrl + ZとCtrl + Cの効果を比較します。
![](https://habrastorage.org/web/470/ae5/901/470ae59014a4439ca3f1eddb0a030312.jpg)
目的の結果が得られました。信号生成は、単一のターミナルエミュレータのTTY_LINE_DISCIPLINE レイヤーのドライバーのレベルで個別に再定義されます。 ファンタジーの仕事にはフィールドがあります。サービスシーケンスの処理のトリックから、コマンドのカスタムフィルターまでです。
結論
あなたにとって、%username%、仮想コンソールと端末エミュレータの秘密はもはや秘密ではありません 、無差別な魔法は魔法ではありませんが、柔軟なttyサブシステムを作成するための長い道のりを歩んできた技術、そしてテレタイプは古代の産物ではなく、発明です、私たちのもの、家庭用)、その子孫なしでは現代のコンピューターを提示する気はありません。
魅力的な物語を語るのが大好きです。 ライブで聴きたいですか? NeoQUEST-2017 Face-to-Faceに来てください。ハードウェアから暗号化まで、 多くの興味深いレポートが待っています! サイトに登録する場合、入場は無料です。