POSIX端末インターフェースは、 POSIX規格およびSingle Unix Specificationで定義されているように、プログラム用のアプリケーションプログラミングインターフェースと、端末ユーザーに対する一連の動作要件の両方を含む、汎用的な抽象化です。これは、BSDバージョン4および第7版Unixの端末インターフェースから発展した歴史的なものです。
Unixシステムでは、多数の入出力デバイスが「端末」とみなされます。[ 1 ] [ 2 ] これらには以下が含まれます。
メインフレームの同時代のシステムとは異なり、他のミニコンピュータのオペレーティングシステムと同様に、オリジナルのUnixシステムはダム端末専用に開発され、それは今日でも変わっていません。[ 6 ] 端末は文字指向のデバイスであり、デバイスから受信およびデバイスに送信される文字のストリームで構成されます。[ 6 ] [ 7 ] 文字のストリームは、制御文字、エスケープコード、特殊文字を組み込んで構造化されていますが、I/Oプロトコルは、スマート端末またはインテリジェント端末のI/Oプロトコルのように構造化されていません。フィールドフォーマット仕様はありません。入力データの画面全体(入力フォーム)のブロック送信はありません。
それとは対照的に、メインフレームでは一般的にブロック指向の端末が使用される。
端末の「機能」は、純粋なテレタイプライターでは利用できないさまざまなダム端末機能で構成されており、プログラムはこれらを利用できます。これらは(主に)端末との間で送受信できるエスケープコードで構成されています。端末に送信されるエスケープコードは、CRT端末(またはソフトウェア端末エミュレータ)が実行できるものの、テレタイプライターでは実行できないさまざまな機能を実行します。たとえば、端末のカーソルを画面上の位置に移動したり、画面全体または一部をクリアしてスクロールしたり、接続されているプリンタデバイスのオン/オフを切り替えたり、プログラム可能なファンクションキーを使用したり、表示色や属性(反転ビデオなど)を変更したり、表示タイトル文字列を設定したりします。端末から受信されるエスケープコードは、ファンクションキー、矢印キー、その他の特殊キーストローク(ホームキー、エンドキー、ヘルプキー、PgUpキー、PgDnキー、挿入キー、削除キーなど)などを表します。[ 8 ] [ 9 ]
これらの機能は、システム管理者が構成するデータベースにエンコードされ、プログラムからterminfoライブラリ(古いtermcapライブラリに取って代わるもの)を介してアクセスされます。さらに、cursesライブラリやncursesライブラリなどのライブラリもこのデータベース上に構築されています。アプリケーションプログラムは、端末機能を使用して、ウィンドウ、ダイアログボックス、ボタン、ラベル、入力フィールド、メニューなどを備えたテキストユーザーインターフェイスを提供します。 [ 10 ] [ 11 ]
TERMet al.(端末対応)プログラムの入出力が使用する端末の特定の機能セットは、プログラムやライブラリにハードワイヤードされるのではなく、データベースから取得され、TERM環境変数(および、termcap および terminfo ライブラリの場合はオプションで、それぞれTERMCAP環境TERMINFO変数と)によって制御されます。[ 10 ]この変数は、その端末を入出力に使用するプログラムを生成する端末モニタプログラム によって設定されるか、または明示的に設定されることがあります。例:
TERMローカル端末がどのシリアルポートに接続されているか、またローカル仮想端末またはローカルシステムコンソールによってどの端末タイプが提供されているかを定義します。TERMログイン直後に環境変数を正しいタイプに手動で設定します。(通常、システム管理者がリモート端末を使用するダイヤルアップユーザーが最も頻繁に使用すると判断した、gettyプログラムによってダイヤルアップ回線に設定される端末タイプは、ダイヤルアップユーザーが使用する端末タイプと一致するため、ユーザーは端末タイプを上書きする必要はありません。)TERMTERMエミュレートする端末の種類を指定する環境変数を設定します。エミュレートされた端末は、実際の端末ハードウェアと完全に一致するとは限らず、端末エミュレータには専用のタイプ名があります。xtermたとえば、xterm プログラムでは (デフォルトで) を端末タイプとして設定します。[ 13 ] GNU Screenプログラムではscreenを端末タイプとして設定します。端末はジョブ制御機能を提供します。対話的に、端末のユーザーは、現在実行中のジョブを一時停止し、ジョブを生成した対話型ジョブ制御シェルに戻る制御文字を送信したり、ジョブを「バックグラウンド」に配置したり、別のバックグラウンドジョブをフォアグラウンドに切り替えたり(必要に応じて一時停止を解除したり)するコマンドを実行したりできます。[ 14 ] [ 15 ]
厳密に言えば、Unix では端末デバイスは、I/O 命令によるデバイスハードウェアの物理的制御と文字の入出力のためのデバイス割り込み要求の処理を担当する基盤となるtty デバイスドライバと、ライン ディシプリンで構成されます。ライン ディシプリンは実際のデバイスハードウェアとは独立しており、複数の制御端末を担当する端末集中装置デバイスにも擬似端末にも同じライン ディシプリンを使用できます。実際、ライン ディシプリン (または、BSD、AIX、その他のシステムの場合はラインディシプリン) は、すべての端末デバイスで同じです。ローカル エコー、行編集、入力モードの処理、出力モードの処理、文字マッピングを担当するのはライン ディシプリンです。これらはすべて実際のハードウェアとは独立しており、tty デバイスドライバによって提供される単純な抽象化で処理されます。文字を送信し、文字を受信し、さまざまなハードウェア状態を設定します。[ 16 ] [ 17 ]
第7 版 Unix、BSDシステム、macOS を含む派生システム、およびLinuxでは、各端末デバイスを複数のライン ディシプリン間で切り替えることができます。[ 18 ] AT & T STREAMSシステムでは、ライン ディシプリンは STREAMS モジュールであり、STREAMS I/O スタックにプッシュしたりポップしたりできます。[ 19 ]
POSIX端末インターフェースは、様々なUnixシステムの端末インターフェースを基に開発されたものである。
Unix 32V および Seventh Edition Unix で提供され、BSD バージョン 4 では古い端末ドライバとして紹介されていた端末インターフェースは、主にテレタイプライターを端末として使用することを想定したシンプルなものでした。入力は 1 行ずつ行われ、オペレーティングシステム内の端末ドライバ (端末自体ではなく) が簡単な行編集機能を提供していました。編集が行われるバッファはカーネルによって管理されていました。端末入力を読み取るアプリケーションは、return端末で行編集を終了するキーが押されたときにのみバッファの内容を受け取ります。端末からシステムに送信されるキーは、編集バッファの現在の内容全体を消去 (「終了」) し、通常は「@」記号の後に改行シーケンスが続く形で表示され、印刷位置を新しい空白行に移動させました。端末からシステムに送信されるキーは、編集バッファの末尾の最後の文字を消去し、通常は「#」記号として表示されます。ユーザーは、これが前の文字の「消去」を意味することを認識する必要がありました(テレタイプライターは、紙に印刷された文字を物理的に消去することができないためです)。[ 20 ] [ 21 ] [ 22 ] [ 23 ] [ 18 ]@#
プログラミングの観点から見ると、端末デバイスには、送信および受信ボーレート、「消去」および「終了」文字(前述のように行編集を実行)、「割り込み」および「終了」文字(端末が制御端末となっているすべてのプロセスにシグナルを生成)、「開始」および「停止」文字(モデムフロー制御に使用)、「ファイルの終わり」文字(キャリッジリターンのように動作するが、システムコールによってバッファから破棄されるため、長さゼロの結果が返される可能性がある)、およびカーネルの端末ドライバによってローカルエコーがエミュレートされるかどうか、モデムフロー制御が有効になっているかどうか、さまざまな出力遅延の長さ、キャリッジリターン文字のマッピング、および 3 つの入力モードを決定するさまざまな基本モードフラグがありました。[ 24 ]read()
入力モードは以下の3つでした。
行モードでは、行規律がすべての行編集機能を実行し、「割り込み」および「終了」制御文字を認識して、それらをプロセスに送信される信号に変換します。端末から読み取るアプリケーションプログラムは、ユーザーがリターンキーを押して行編集を完了した後、行全体を受け取ります。[ 21 ] [ 25 ]
cbreak モードは、一度に 2 つの文字を処理するモードのうちの 1 つです。(Stephen R. Bourne は冗談で( Bourne 1983 、p. 288)、「半調理済み」で「珍しい」モードと呼んでいました。)行規律は行編集を行わず、行編集機能の制御シーケンスは通常の文字入力として扱われます。端末から読み取るアプリケーション プログラムは、文字が読み取るために入力キューで利用可能になるとすぐに文字を受け取ります。ただし、「割り込み」および「終了」制御文字、ならびにモデム フロー制御文字は、引き続き特別に処理され、入力ストリームから削除されます。[ 26 ] [ 27 ]
これらのモードと制御文字すべてを照会および変更するためのプログラムインターフェースは、ioctl()システムコールでした。(これは、第 6 版 Unix のstty()およびシステムコールに取って代わりました。) [ 29 ] [ 30 ] 「消去」文字と「終了」文字は、デフォルトの および から変更可能でしたが、長年にわたり、端末デバイスドライバのプリセットのデフォルト値であり、ログインプロセスの一部としてのみ端末デバイス設定を変更する多くの Unix システムでは、ユーザーがユーザー名とパスワードを入力した後で実行されるシステムログインスクリプトで、ログインとパスワードのプロンプトでの誤りは、テレタイプライター端末から継承された歴史的な編集キー文字を使用して修正する必要がありました。[ 23 ]gtty()#@
BSD Unixにはジョブ制御と、拡張機能を備えた新しい端末ドライバが付属していた。[ 18 ] これらの拡張機能には、追加の(これもプログラムで変更可能な)特殊文字が含まれていた。
SUBEMSIGTSTPETBSYNDC2これらの追加モードと制御文字すべてを照会および変更するためのプログラムインターフェースは、依然としてioctl()システムコールであり、その作成者(Leffler et al. 1989 、p. 262)はそれを「かなり煩雑なインターフェース」と表現した。オリジナルの第 7 版 Unix の機能はすべて保持され、新しい機能は追加の操作コードによって追加されたため、プログラムインターフェースは明らかに成長し、機能の重複がいくつか生じた。[ 31 ] ioctl()
System III では、 Seventh Edition のioctl()フラグの取得と設定、および制御文字の取得と設定という別々の操作を、termioフラグと制御文字の両方を保持する構造体を使用する呼び出しに統合した新しいプログラミング インターフェースが導入されました。これにより、単一の操作でフラグを取得し、別の単一の操作で設定できるようになりました。また、Seventh Edition インターフェースのフラグの一部を複数の別々のフラグに分割し、いくつかの追加機能を追加しましたが、ジョブ制御や 4BSD の cook-mode 拡張機能はサポートしていません。[ 32 ] 例えば、Seventh Edition の「cooked」、「cbreak」、「raw」モードを別の抽象化に置き換えました。信号生成文字の認識は入力モードとは独立しており、入力モードは canonical と non-canonical の 2 つのみです。(これにより、Seventh Edition および BSD には存在しない端末入力モード、つまり信号生成が無効になっている canonical モードが可能になります。)
System IIIの後継システム(System Vを含む)は、同じインターフェースを使用していた。
POSIX 標準が汎用端末インターフェースの定義によって解決した主要な問題の一つは、プログラムインターフェースの多さでした。標準が制定された時点では、ほとんどの Unix がライン規律の概念と BSD のジョブ制御機能を採用していたため、端末の動作はシステム間でかなり統一されていましたが、ioctl()システムコールを介した端末へのプログラムインターフェースは混乱していました。異なる Unix はioctl()、異なる (シンボル) 名前と異なるフラグを持つ異なる操作を提供していました。移植可能なソースコードには、ソフトウェアプラットフォーム間の違いに対応するために、かなりの量の条件付きコンパイルを含める必要がありましたが、それらは概念的にはすべて Unix でした。[ 33 ]
POSIX 標準は、標準化された名前とパラメータを持つioctl()ライブラリ関数のセット (もちろん、プラットフォーム固有の操作によって内部的に実装される場合もある) でシステムを完全に置き換えます。System V Unix のデータ構造がPOSIXデータ構造のテンプレートとして使用され、フィールドは、フィールドを指定するためにエイリアスデータ型を使用するようになったことを除いて、ほとんど変更されていません。これにより、実装者は、C および C++ プログラミング言語のデータ型 (一部のプロセッサ アーキテクチャでは不便なサイズになる可能性がある) を明示的に必要とするのではなく、複数のプロセッサ アーキテクチャ間で簡単に移植できるようになります。[ 33 ] [ 34 ]ioctl()termiotermiosunsigned shortchar
POSIX also introduced support for job control, with the termios structure containing suspend and delayed-suspend characters in addition to the control characters supported by System III and System V. It did not add any of the cooked-mode extensions from BSD, although SunOS 4.x, System V Release 4 systems including Solaris, HP-UX, AIX, newer BSDs, macOS, and Linux have implemented them as extensions to termios.
Each process in the system has either a single controlling terminal, or no controlling terminal at all. A process inherits its controlling terminal from its parent, and the only operations upon a process are acquiring a controlling terminal, by a process that has no controlling terminal, and relinquishing it, by a process that has a controlling terminal.[33]
No portable way of acquiring a controlling terminal is defined, the method being implementation defined. The standard defines the O_NOCTTY flag for the open() system call, which is the way of preventing what is otherwise the conventional way of acquiring a controlling terminal (a process with no controlling terminal open()s a terminal device file that isn't already the controlling terminal for some other process, without specifying the O_NOCTTY flag[35]) but leaves its conventional semantics optional.
Each process also is a member of a process group. Each terminal device records a process group that is termed its foreground process group. The process groups control terminal access and signal delivery. Signals generated at the terminal are sent to all processes that are members of the terminal's foreground process group. read() and write() I/O operations on a terminal by a process that is not a member of the terminal's foreground process group will and may optionally (respectively) cause signals (SIGTTIN and SIGTTOU respectively) to be sent to the invoking process. Various terminal-mode-altering library functions have the same behaviour as write(), except that they always generate the signals, even if that functionality is turned off for write() itself.[36][37]
termios data structure The data structure used by all of the terminal library calls is the termios structure,[38] whose C and C++ programming language definition is as follows:[34]
structtermios{tcflag_tc_iflag;// Input modestcflag_tc_oflag;// Output modestcflag_tc_cflag;// Control modestcflag_tc_lflag;// Local modescc_tc_cc[NCCS];// Control characters};The order of the fields within the termios structure is not defined, and implementations are allowed to add non-standard fields.[34] Indeed, implementations have to add non-standard fields for recording input and output baud rates. These are recorded in the structure, in an implementation-defined form, and accessed via accessor functions, rather than by direct manipulation of the field values, as is the case for the standardized structure fields.[39]
The data type aliases tcflag_t and cc_t, as well as the symbolic constant NCCS and symbolic constants for the various mode flags, control character names, and baud rates, are all defined in a standard header termios.h. (This is not to be confused with the similarly named header termio.h from System III and System V, which defines a similar termio structure and a lot of similarly named symbolic constants. This interface is specific to System III and System V, and code that uses it will not necessarily be portable to other systems.)[40]
The structure's fields are (in summary, for details see the main article):
c_iflagc_oflagc_cflagc_lflagSIGTTOU signal by the write() system call[39]The library functions are (in summary, for details see the main article):
tcgetattr()termios構造体にクエリする[ 43 ]tcsetattr()termios、必要に応じてキューイングされた出力が排出されるのを待ち、キューイングされた入力をフラッシュします[ 43 ]cfgetispeed()termios構造体内の実装定義フィールドから入力ボーレートを照会する[ 44 ]cfgetospeed()termios構造体内の実装定義フィールドから出力ボーレートを照会する[ 44 ]cfsetispeed()termios構造体[ 44 ]の実装定義フィールドで入力ボーレートを設定します。cfsetospeed()termios構造体[ 44 ]の実装定義フィールドで出力ボーレートを設定します。tcsendbreak()tcdrain()tcflush()tcflow()tcgetpgrp()tcsetpgrp()データ構造の配列メンバーは、(プログラムで変更可能な)すべての特殊文字を指定します。配列へのインデックスは、右の表に示すように、特殊文字タイプごとに1つずつ、シンボル定数です。(配列内のさらに2つのエントリは、非標準モードの入力処理に関係しており、以下で説明します。)[ 43 ]c_cc[]termios
プログラムで変更できない特殊文字は、ラインフィード(ASCII LF)とキャリッジリターン(ASCII CR)です。[ 47 ]
入力処理は、端末デバイス上のシステムコールの動作read()と、ライン規律の行編集および信号生成特性を決定します。第 7 版 Unix および BSD バージョン 4 の場合とは異なり、System III および System V の場合と同様に、行編集は、正規モードと非正規モードの 2 つのモードのいずれかで動作します。両者の基本的な違いは、read()システムコールのブロッキング/非ブロッキング要件 (ファイルディスクリプタO_NONBLOCKのフラグで指定)の観点から、データが「読み取り可能」になるタイミングです。[ 48 ]open()fcntl()
標準モードでは、データは行編集バッファに蓄積され、ユーザー(端末)が行区切り文字を送信して行編集を終了するまで「読み取り可能」になりません。行区切り文字は特殊文字であり、ファイルの終わり、行の終わり、および改行(ASCII LF)です。前二者はプログラムで設定できますが、後者は固定です。後者二者は行編集バッファに含まれますが、前者は含まれません。[ 49 ]
より厳密には、行編集バッファには、行区切り文字(read()読み取り時に破棄される場合とされない場合がある)で区切られた0行以上の行が蓄積され、行編集はバッファ内の最後の行区切り文字(存在する場合)に続く行編集バッファの部分に対して実行されます。したがって、たとえば、「消去」文字(それが何にプログラムされているかは関係ありません)は、行バッファ内の最後の文字を、直前の行区切り文字まで(ただし、行区切り文字自体は含まない)のみ消去します。[ 49 ]
非正規モードでは、データはバッファ(行編集バッファである場合もそうでない場合もある。実装によっては「処理済み入力」と「生入力」のキューが別々になっている)に蓄積され、データ構造のと という2 つの入力制御パラメータの値に応じて「読み取り可能」になります。どちらも符号なし量です( は符号なし型のエイリアスである必要があるため)。前者は最小文字数を指定し、後者はタイムアウトを 10 分の 1 単位で指定します。[ 50 ] 可能性は 4 つあります。c_cc[MIN]c_cc[TIME]termioscc_t
c_cc[TIME]どちらもゼロですc_cc[MIN]read()バッファ内のデータ(データがゼロの場合はゼロを返す可能性あり)をすぐに返します。[ 51 ]c_cc[TIME]はゼロではなく、ゼロですc_cc[MIN]read()システムコールの開始によってトリガーされるか、または単一の文字が受信された場合に作動します。言い換えれば、read()指定された最大合計時間待機し、ゼロデータを返す場合があり、受信したデータはすぐに返します。[ 51 ]c_cc[TIME]はゼロであり、はゼロではないc_cc[MIN]read()最小限のデータ量(呼び出し元がシステムコールで読み取る準備ができている量よりも多い場合がある)を待ち、ゼロのデータを返さず、無期限に待機する可能性があります。[ 51 ]c_cc[TIME]両方ともゼロではないc_cc[MIN]read()最小限のデータ量(システムコールで呼び出し元が読み取る準備ができている量よりも多い場合があります)を待ち、ゼロのデータを返さず、無期限に待機する可能性がありますが、バッファに少なくとも1文字が読み取られる場合は、指定されたタイムアウトよりも長く待機することはありません。[ 51 ]出力処理は、System III/System V時代からほとんど変わっていません。出力モード制御フラグによって、さまざまなオプションが決定されます。