【超ざっくり解説シリーズ】シェル(Shell)とは?シェルについて初心者にもわかりやすく解説するよというnoteを読み、ちょっと内容がひどかったので、Twitterで言及したものの、ちゃんと説明したほうがよかろうと考えて急遽、この記事を書くことにした。YokohamaのLinux基礎初級「シェルとコマンドライン」の中でやっている。この内容をやる時期としてはかなり難しい部類に入り、かつ重要であることから、最近は端末デバイスを直接叩くといったことを含めて2時間以上じっくり取り組むことが多い。内容自体はそれほど難しくはないと思うのだが、理解に至るには入出力デバイスと接続カーネルとシステムコールプロセスファイルディスクリプタデバイススペシャルファイルに対する理解が前提となるため、ちゃんと認識できていないと難しいかもしれない。このあたりはOSによって非常に大きな差異があるところなので、ここではLinuxを前提に説明する。Yokohamaの授業を受けてみてほしい。丁寧な説明とハンズオンによって理解できるまでしっかりと授業を行っている。カーネルカーネルはCPUの特権モードで動作するプログラムである。実際に定義としてはそれしかなく、カーネルが実際にどのような仕事を担っているかというのも特に決まってはいない。いえ、カーネルにやってほしい重要なこととして「リソース管理」というものがある。最近のOSはタイムシェアリングシステムなので物理的な装置数以上のプロセスを起動することができるから、プロセスが好き勝手にリソースを使ってしまうと色々奪い合ってしまうのだ。わかりやすいのは、「ハードディスクのアームをどっち方向にどれだけ動かすか」というモーター制御を奪い合ってしまうという状況ではないだろうか。いうわけでリソース管理をするためにカーネルとしてはプロセス管理をしなければならぬ、ということになる。カーネルというのは基本的にそういうことをする。コンピュータを使う上で必要な環境を整える、コンピュータという舞台を作る、というのがカーネルの仕事だ。ここが改善されるとそれを使う全てが改善されるため開発にも力が入るというものだ。端末とコマンドラインカーネルはプロセスを起動する仕組みを提供し、起動したプロセスを管理してくれるのだが、ここで問題になるのは「どうやってプロセスを起動するか」だ。これでは任意のタイミングで必要なプログラムを実行するという方法がない。プロセスやカーネルにユーザーが対話的にやりとりする方法がない。いや、実際にそう時代もあったのだ。コンピュータがパンチカードマシンだった時代、プログラムを実行するというのはパンチカードを差し込んで読み込ませることだった。タイムシェアリングシステム時代になると、ユーザーとコンピュータは「やりとり」をする必要が生まれた。それも、できるだけ使いやすく。ここで使われるようになったのが「テレタイプ」である。テレタイプというのは通信機能(送受信機能)をもったタイプライタである。言ってみればモールス信号とFAXの中間みたいな感じで、電話回線を通じて(手動交換機を通じて)ほかのテレタイプの紙に印字できる。テレタイプ自体は1920年代には存在していたもので、1960年代にコンピュータに接続して使われるようになった。当時の「最もポピュラーのコンピュータとユーザーが意思疎通するための機械」だったのだ。ユーザーは、タイプライタ同様にテレタイプでタイピングをする、あるいは予め打ち込んで紙テープに出力したものを読み込ませることでコンピュータに対して意思を伝える。コンピュータからの出力はテレタイプを通じて紙に印字される。それ自体が「OS」と呼ばれることが多かった。Linuxでも端末のことをテレタイプ(tty)と呼ぶが、実はUnixですらテレタイプは過去のものだった。1970年代にはビデオディスプレイ端末が登場しており(Unixは1973年に登場)、Unixの端末というと紙テープではなく画面を使うもののイメージが強い。紙テープから画面になることの違いは、「入出力環境が複数行になる」ということである。Unixのエディタコマンドとしてex(1)とvi(1)がある。ed)はラインエディタであり、一行で入出力ができる。紙テープに適している。ビデオディスプレイなら複数行を表示し、それを見ながら編集できる。それがvi(VIsual)なのである。ひとつ大きな点として「表示上での削除が可能になる」ということがある。キーボードから打ったものをそのまま解釈してプロセスを起動する、という方法より「よりよい方法」が登場する。それが「コマンドライン」である。コマンドラインは、ユーザーはその意図を「行」に記述することができる。行単位なので、改行したときがその意思の確定である。このコマンドラインを快適なインターフェイスとして利用できるようにしたのが「シェル」である。出力ファイルディスクリプタを別のプロセスの入力ファイルディスクリプタへのパイプにする操作など様々な機能を搭載した「超高機能インターフェイス」であった。ユーザーは1行の「コマンドライン」の中で実に様々な操作を行うことができる。今にすればshなんて貧弱極まりないのだが、当時の感覚からすれば「キーボードから打ったものがそのままコンピュータの動作になる」などというのと比べ信じられないほどいろんなことができ、様々に応用できるスグレモノだったのだ。これがスグレモノである点として、「プログラムを記述する必要性が下がる」というのがある。インターフェイスが単にキーボードから打った内容に基づきプロセスを起動するだけであるならば、全ての操作は事前にプログラムを用意しておかなければ行えない。shは実に様々な操作をsh自身によって提供し、なにより応用が効くようになっていたので、圧倒的にプログラムを書くのではなくコマンドラインを打つことで目的を達成できるようになったのだ。基本的には、bashであれ、zshであれ、それらの「機能が豊富で便利」という点を強化したにすぎず、基本的には変わらない。シェル自身もプログラムであり、別に特別なものではない。使えるインターフェイスだ」というだけの話である。カーネルに対して特別にくっついているわけではないし、Linuxシェルはユーザースペースで動作し、別にシステムコールを直に叩くわけではなく汎用性のあるライブラリ(例えばglibc)をロードし、実行する「対話的プログラム」でしかない。シェルが登場したとき、コンピュータとやりとりするための機械はテレタイプ、あるいはビデオディスプレイ端末であった、ということだ。Unixはそれがどちらであっても、あるいはどこのメーカーのテレタイプであっても抽象的に扱えるように「デバイスファイル」という仕組みによって扱うようになっている。そのため、「端末デバイス」という抽象化したレベルで取り扱うことができ、この端末デバイスは、テレタイプのような一行のものなのか、あるいはビデオディスプレイ端末のような複数行のものなのか、という情報と合わせて取り扱うことができる。現在は端末デバイスではなく、ビデオデバイスを通じて出力し、キーボードを通じて入力するのが普通だ。Server→ビデオデバイス(カーネルインターフェイス)→ビデオドライバ(カーネル)→ビデオカード(ハードウェア)」の構成であるが、シェルは依然として端末デバイスを必要とする。現在本当に端末に繋がっているコンピュータなんてまずないので、物理的に端末を意味する端末デバイスファイルが用意できない。そこで、実際には端末はなくても、端末デバイスとして扱える仮想端末デバイスと、コマンドラインの入出力に適した仮想端末ソフトウェアが使用される。「テキストモード」においては、「仮想コンソール」と呼ばれることが多いプログラムを使用する。仮想端末デバイスの提供(Linuxでは/dev/tty*)を行う。その上でシェルを起動すれば、シェルは/dev/tty?を端末として扱うことができるわけだ。client(あるいはWaylandクライアント)である。グラフィカルアプリなのだが、これはこれで仮想端末を提供する。Linuxでは/dev/pts/*である。この上でシェルを起動すれば、シェルは/dev/pts/?を端末として扱うことができる。/dev/pts/0ということである。端末エミュレータはキーボードを処理しない。serverから受け取ったキー入力を仮想端末デバイスを通じて入力するようになっている。「シェルの標準入力はデフォルトでキーボードに繋がっている」というのは嘘で、対話的シェルは入出力ともに端末デバイスに繋がっている。仮想コンソールの場合、シェル→端末デバイス→仮想コンソール→キーボードと間接的につながっているのだが、ターミナルエミュレータの場合は全く繋がっていない。ちなみに、「端末」といえば、DECのVT-100である。1978年にリリースされたビデオディスプレイ端末で、まぁとにかく売れに売れて、端末といえばVT-100というレベルだった。VT-100の仕様が一般的で、VT-100と互換仕様の端末も出たし、仮想端末もだいたいVT-100と互換になっていたりする(そして、その設定がある)。xtermは、それを端末として扱うための色々なものを、自前でもっていたりする。