502
603

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

初学者のための正しいシェルとカーネルの概念 ~ 大学も技術者認定機関も間違いだらけ

Last updated at Posted at 2022-12-11

なんだろう、嘘つくのやめてもらっていいですか?

大学も技術者認定機関も、いつまで古いまたは間違ったシェルとカーネルの概念を説明し続けるのでしょうか? シェルはカーネルの言葉をユーザーの言葉に翻訳したり、出力結果をユーザーに中継したり、カーネルを防御したりする層ではありません。指定したコマンドを実行するだけのプログラムです。勉強中の学生か代理執筆業者が適当な文献を調べて書いたとしか思えません。そして他人の説明を自分の言葉に置き換えるのが上手い人がおかしな説明をさらに広めています。個人サイトやオンライン学習サイト程度であれば適当なことを書いていても気にも留めませんが、大学や技術者認定機関のような正しいことを書いているに違いないと思えるような所までもが間違ったことを書いているから困ったものです。

みなさんは大学や技術者認定機関が言っていることなら正しいと思いこんでいないでしょうか? そんなことありません。某研究所は不正な論文やら捏造と改ざんで世間を騒がせましたよね? どのような論文であれ研究結果であれ、第三者による追試が行われてないなら、それは一人または一グループによる主張にすぎません。講義で使うレベルの資料を公開したものなんてその程度です。肩書を持っている人が言ってることだから正しいんだと鵜呑みにするのではなく、自分でちゃんと調べる癖をつけましょう。この記事ではシェルに関するよく見かける説明のどこが間違っているのかの指摘と、その間違いがどこから生まれたのかという考察をしています。

【お願い】毎度のように、はてブその他でこっそり記事の問題点を指摘する方が多いですが、QiitaのコメントかTwitterで報告してください。たまたま見ていれば気が付きますが、それじゃ記事を改善できません。細かい問題は後で修正します。

注意 この記事の図は「どこかの図」にそっくりかもしれませんが、たまたまその図を参考にしただけであって特定の解説に対しての指摘ではありません。似たような図を用いた解説は日本だけではなく世界中で無数に見つかりますし、どこがオリジナルなのかはわかりません。つまり特定の誰かではなく、あちこちにケンカを売っています :-P。私の解説に納得できないというのであれば、自分で調べて記事にすると良いと思います。詳しく調べればもっと正確で良い記事が書けることでしょう。

備考 この記事では OS の名前を UNIX と書いています。なんたら BSD や macOS は UNIX ですが Linux は厳密には UNIX ではありません。ただしこの記事の範囲においては Linux も UNIX とほぼ同じと考えて構いません。Windows は UNIX ではありませんが、WSL を使うことで Linux の CLI 環境とほぼ同じものが使えるので参考にはなるでしょう。ちなみに Linux が主流の時代に UNIX と書いた理由は他の解説が UNIX の解説として書かれていることが多いのでそれに合わせているだけです。また現代の標準的なユーザーインターフェースである GUI シェルの話は基本的にしていないということに注意してください。この記事に限らず UNIX の話をしている時や単にシェルと言った場合、大抵の場合はコンピュータを CLI 環境で利用している話が前提となっています。

用語について この記事ではシェル、カーネル、ミドルウェアに属さないソフトウェアを全てアプリケーション(アプリ)として扱っています。CLI コマンド(コマンド)もアプリケーションです。最近では GUI のものをアプリと呼ぶようになっている気がしますが、元々アプリケーションとは応用ソフトウェアとも呼ばれ、特定の目的を持って開発された専用のプログラムのことです。コマンドをアプリに含めるのか疑問に思うかもしれませんが、実際の所、何が特定の目的で何がそうでないかはコマンドかどうかでは区別できません。例えばカレンダーを表示する UNIX 標準の cal コマンドは特定の目的のプログラムでしょうか? 技術的にはアプリとコマンドに違いはありません。したがってより大きなカテゴリとしてアプリケーションがあり、その中にコマンドや GUI アプリやウェブアプリなどが含まれるとしています。ミドルウェアも技術的にはアプリケーションと大きな違いはないのですが「OS とアプリケーションの間に入るもの」という一般的な定義があるため別扱いにしています。

参考記事 この記事を書くにあたって以下の記事を参考にさせていただきました。(矛盾はないようにしたつもり……)

シェルとは?

シェルとは CLI 環境用のユーザーインターフェースで、ユーザーからの入力を受け付け、その意味を解釈して、プログラムを実行するためのプログラムです。シェルの説明として以下のような図がよく用いられています。しかしこれは何故シェルと呼ばれているのか?という用語の由来を説明したものであってシェルの役割や機能を説明したものではありません。もちろん OS の姿でもありません。

シェルとは?

現在 OS の中心にあるものはカーネル (kernel) と呼ばれています。とある文献 によるとメインフレーム用の OS/360 では中心にあるものの名前は nucleus と呼ばれており、UNIX の前身とも言える Multics では supervisor と呼ばれていたようです。シェルという用語は Multics で誕生しており、リンク先の資料から推測するに、その頃にはまだカーネルという用語は OS の中心にあるものを指す用語としては使われていなかったように思えます。shell と kernel という言葉は一般的な英語の名詞であり、おそらく UNIX でシェルとカーネルと呼ぶのがしっくりきたため、この言葉を拝借したのでしょう。しかし話はここまでです。これは用語の由来であってシェルというプログラムや UNIX という OS の説明ではありません。用語の由来を説明した図を使ってシェルや UNIX を説明しているために多くの間違いが生まれています。

シェルの機能の一つはコマンドラインインタプリタの機能です。以下にその機能の流れを示します。なぜこのような流れであるかの詳細をこの記事では説明しています。

シェルのコマンドインタプリタ機能の流れ

補足 シェルは JCL(またはジョブ管理システム)とは全く違うものです

メインフレームユーザーがしばしば勘違いしているようですが、シェルはジョブ管理システムではありませんし、シェルスクリプトは JCL(ジョブコントロール言語)でもありません。

私はメインフレームについては詳しくありませんが、メインフレームユーザーは JCL 相当の機能が欲しいが UNIX に JCL がないためにシェルスクリプトで代用しているようです。また JCL の機能の一つ、時間指定でプログラムを起動するために CRON(クローン)を補助的に使っているようです。

シェルはジョブ管理システムではありません。全く異なる仕組みのもので機能も異なります。代用として使用しているだけなのに JCL は UNIX では「シェル+CRON で書く」と教わるようで、シェルスクリプトを JCL と同じものだと勘違いしている人が多いように思えます。

第一章 正しいシェルとカーネルの概念と呼び出し階層

シェルはカーネルの出力結果を翻訳していない

シェルとカーネルについて検索すると以下のような図と共に「シェルがカーネルの言葉を人間がわかるように翻訳している」という説明をよく見かけます。しかしそれは間違った説明です。シェルはカーネルの言葉を人間の言葉に翻訳していません。以下の図でどこが間違っているかを赤文字で指摘しています。

間違ったシェルとカーネルの概念

あちこちで見かける説明のほとんどは他のなにかを参考にして書き写しただけだと思われます。では最初の一人は一体何を間違えたのでしょうか?それは「データの流れを追加してしまった」ことです。この図は元々は単に「UNIX のシェルとカーネルの関係」を表しただけのものだったと私は考えています。その図を流用しターミナルと余計なデータの流れを追加してしまったためにおかしな図になったのです。上記の「より適切なシェルとカーネルの概念」の図は、ターミナルとデータの流れを削除し「ユーザーからは UNIX がこのように見える」という表現に変更しています。

一番の問題点はカーネルからユーザーへ戻るデータの流れです。カーネルからユーザーへの出力はシェルの上を通りません。実際のユーザーへの出力はハードウェアに出力されます。一般的にハードウェアはカーネルの中にあるものとして書かれています。ユーザーはハードウェア(ディスプレイやプリンタ)に出力された結果をコンピュータの外から見ます。例えばプリンタへの出力は紙に印刷されますよね? その印刷された紙を見るのにコンピュータは必要ありません。ディスプレイも同じです。コンピュータの出力先はあくまでディスプレイでありユーザーに出力されているわけではないのです。そこに気がつくとカーネルからの出力がシェルの上を通ってユーザーへ伝わっているというのがおかしいと気がつくはずです。

なぜユーザーに出力を戻すようなことを書いてしまったのかと言うと、ユーザーとターミナル(ソフト)を結びつけてしまったからです。ターミナルソフトの中に出力結果が表示されるために、出力結果がターミナルソフトに戻っているはずだと考えてしまい、それは入力とは逆向きにカーネルからシェルを経由して戻っているのだと勘違いしてしまい、データの流れがシェルを通っているのだからシェルが何かをしているのだろうと考えてしまい、シェルが人間がわかるように翻訳しているなどというありもしない動作を書く羽目になってしまっているのです。

ターミナルソフトはターミナルエミュレータとも言われ、実際のターミナル(端末)と呼ばれるハードウェア(周辺機器)をソフトウェアで再現した(エミュレートした)複雑な仕組みのソフトウェアです。シェルとカーネルの概念図と一緒に説明できるようなものではありません。初学者にわかりやすく説明するためにはシェルとカーネルの概念図からはターミナルソフトは削除しなければいけません。

この図はあくまで「シェルとカーネルの概念図」です。それ以外のことを一緒に説明しようとしてはいけません。私が提示したより適切な概念図も「シェルとカーネルの概念図」という点では正しいと思いますが「UNIX の内部構造」は適切に表現していません。例えばアプリケーションなどの重要な層を省略しています。しかしそれらを加えると複雑な図になってしまい、初学者にシェルとカーネルの概念を捉えるのは難しくなるでしょう。したがって別に説明する必要があります。説明は一歩ずつです。中途半端に説明を加えて間違ったものにしてしまうぐらいなら、概念は概念として「正確ではないけどこのような感じ」と説明する方がはるかにマシです。

ターミナルはもともと周辺機器(ハードウェア)

ユーザーはシェルを触ることも見ることもできません。ユーザーが本当に触っているのはキーボードであり、見ているのはディスプレイです。概念で捉えていると当たり前のことが見えなくなってしまいます。

キーボードやディスプレイはコンピュータに搭載されている USB ポートやディスプレイ端子などにケーブルで接続されています。ユーザーからの入力はケーブルの中を通ってコンピュータにたどりつき、一番最初にデータを受け取るソフトウェアはカーネル(の中にあるハードウェアを制御しているドライバ)です。また一番最後に出力を行うソフトウェアもディスプレイに出力するためのカーネルです。入力を書くのであればキーボードから始まり、出力を書くのであればディスプレイでなければなりません。それらはハードウェアでありユーザーへのデータの本当の出入り口はシェルではなくカーネルなのです。

ターミナル(端末)とはもともとは、キーボードとディスプレイをあわせた周辺機器(ハードウェア)の名前です。古くはディスプレイの代わりにプリンタが使われており、タイプライターのような見た目のテレタイプ端末 (TTY) が使われていました。テレタイプ端末のイメージがわかなければ YouTube に動画がいくつもあるのでそれらを参照してください。テレタイプ端末をコンピュータに接続するというイメージを利用すれば、ターミナルとシェルの違いは簡単にわかります。この時、シェルを使っているときのイメージと実際のデータの流れとでテレタイプ端末の位置が正反対になっていることに注意してください。

シェルの使用イメージと実際のデータの流れ

上記の二つの違いがシェルとカーネルの概念図にデータの流れを追加してしまうことの問題点を明らかにしています。ユーザーがシェルを操作しているというのは概念です。概念と実際のデータの流れとは異なっています。もちろん常に実際のデータの流れで考えていると複雑になりすぎて逆に理解するのが難しくなってしまうので概念で考えるというのは重要なことです。しかし概念は実際の姿を表しているとは限らないことに注意する必要があります。

テレタイプ端末はキーボードとディスプレイ相当のものでしかなく、シェルのために作られた道具ではありません。ユーザーのキー入力をシェルが受け取るのは、シェルがキー入力を受け取るべきタイミングではカーネルがシェルへとキー入力を転送しているからであって、別のソフトウェアでキー入力が必要な時は、カーネルはそちらにキー入力を転送します。現在のターミナルソフトはシェルを起動するための GUI アプリのように扱われていますが、ユーザーへの入出力の仕組みは(GUI アプリ部分の追加処理を除き)同じような形になっています。初期状態ではシェルがキー入力を受け取りますが、シェルからコマンドを起動するとユーザーのキー入力はそのコマンドが受け取るようになります。そしてコマンドからユーザーへデータを出力する時にわざわざシェルを経由する必要はないというのは、上記の図の実際のデータの流れを見ると納得できると思います。

ハードウェアだったターミナルはソフトウェアで実装(エミュレート)され、現在はコンピュータの中に GUI アプリとして存在してます。そのデータの流れの全てを書き入れるのは大変なので省略しますが、この図のカーネルとハードウェアの間にさらに GUI のためのウインドウシステムやターミナルソフトが追加されるという複雑な形になります。だから概念で考えた方が楽なのですが、そこにターミナルとデータの流れを追加して一緒に考えてしまうとおかしなことになってしまいます。

シェルは「通訳」ではなく「受付」である

シェルの役目の一つはユーザーインターフェースです。いろんな層を経て最終的にカーネルの機能が呼びされるという流れがあり、シェルがユーザーとカーネルの間にあるのは事実です。しかし間にあると言っても複数の意味があります。間違った説明ではシェルは通訳としてカーネルとユーザーの言葉を「翻訳」していると説明されています。人間の通訳者は一般的に「英語→日本語」「日本語→英語」のようにお互いの言語を相互に翻訳しています。しかしシェルが行う翻訳は「ユーザー→カーネル」の一方通行です。カーネルの言葉を人間の言葉に翻訳しているという間違った説明が生まれた原因の一つは、通訳者が行っている双方向の翻訳のイメージに引きづられてしまったからだと考えられます。

「翻訳している」という説明にも違和感を覚えます。シェルが翻訳していると説明している人は、どういう処理のことを指して翻訳と言っているのでしょうか? 実際の所、シェルが行っている仕事は「通訳」ではなく「受付」です。例えば本当に話をしたいのは担当者(コマンド)であっても、その前に受付(シェル)にお願いして担当者を呼び出してもらいますよね? そのイメージです。最初に話をするのは受付なので担当者との間に受付がいるとみなすことが出来ますが、だからといって受付が担当者との会話に参加するわけではありません。

シェルは受付である

シェルとカーネルの概念図はシェルとカーネルの話しかしてないので、いろいろなものが省略されています。その一つが「コマンド」です。シェルが行うのは受付の仕事として担当者(コマンド)を呼び出す所までで、その後はコマンドによって処理されます。ユーザーがシェルから出力結果を受け取っているように勘違いしてしまう理由は、実際に処理している相手が誰だか分かりづらいからでしょう。例えばシェルに日時を聞いた時、シェルは date コマンドを呼び出しているだけで実際に日時を答えるのは date コマンドです。しかし目の前についたてがあって、その向こう側に座っていた受付のシェルさんが日時処理の専門家の date コマンドさんに入れ替わっていたとしても、それが見えなければシェルさんが日時を答えたと錯覚してしまいます。コマンドの処理はたいていの場合一瞬で終わります。date コマンドさんは自分の仕事が終わったら、またすぐにシェルさんへと交代するのでユーザーが話しかけるのは常にシェルさんです。一瞬の出来事なので別の人にすり替わっていることに気が付かないでしょう。このような勘違いはテキストエディタのような長時間起動する対話型アプリを使っていればユーザーは常にシェルと対話しているわけではないと気づくのではないかと思います。ユーザーがテキストファイル編集の専門家と会話している最中、シェルはその会話が終わるまで待っているだけで何もしていません。

受付(シェル)が呼び出すのは会社の中心人物(カーネル)ではないという点も重要です。仕事は可能な限り担当者が担当します。必要な場合には担当者(コマンド)の上司(システムライブラリ)や特に重要な案件は最終的に社長(カーネル)にまで話が行くと思いますが、毎回社長を呼び出していては時間がかかりますよね? このイメージはカーネルの機能(システムコール)の呼び出しは遅く、可能な限りコマンドのレベルで処理してカーネルの呼び出しは必要最小限にした方が速いというソフトウェアの話にもつながります。また受付(シェル)は基本的に担当者(コマンド)を呼び出しますが、飛び込み営業を断るなど受付だけで対応できることもあります。専門家の方が仕事が速いですが、いちいち呼び出すのはそれはそれで時間がかかってしまうので、簡単なことはコマンドを呼び出すよりもシェルで行ったほうが速いというのも実際のシェルスクリプトにもあてはまる話です。

「OS はシェルとカーネルで OS 以外はアプリ」は間違い

シェルとカーネルの概念図を流用したのか、シェルとカーネルだけが OS の範囲かのように説明しているものを見かけます。また OS とアプリケーションを別々に書いており、まるでアプリケーションは OS の一部ではないかのような説明も見かけます。それらは間違った説明です。

現代のOSの定義

注意 上記の右の図の私の OS の範囲にアプリケーションが含まれていることに納得できない人は、この記事の第六章の『詳解 UNIX プログラミング」掲載の図』を参照してください。有名な技術書でも同様のことを言っています。

ここでカーネルのすべてを OS に含めていないことが気になるかもしれません。含めてもいいかなとも思ったのですが、カーネルの領域のソフトウェアであっても OS ベンダーが開発していないソフトウェア(ドライバなど)があります。その中にはハードウェアメーカーが開発しソースコードが公開されていないプロプライエタリ(独占的な)ドライバというものもあり、OS には含まれずにドライバ単体で配布されていたりします。つまりカーネルの領域に含まれるソフトウェアには「追加ドライバ」があります。人それぞれ考え方が違うと思いますが私はドライバを開発しただけでは OS を開発したとまでは言えないだろうと考えています。

OS の定義は昔のように単純ではありません。国家試験の情報処理技術者試験を(昔に?)勉強した方は「狭義の OS」や「広義の OS」と言った OS の定義を聞いたことがあるのではないかと思いますが、あれはメインフレーム時代の OS の定義であり OS の機能を説明した用語もメインフレーム用語です。情報処理技術者試験が開始されたのは 1969 年です。UNIX が誕生したのも同じ年ですが、さすがにまだ研究用だった UNIX の問題がその頃に出題されるわけがないので初期の情報処理技術者試験はメインフレーム用技術者のための試験です。狭義の OS や 広義の OS の定義が成り立っていたのは 1990 年代の始めぐらいまでのコンピュータメーカーがハードウェアのおまけとしてソフトウェア(OS)をつけていた時代までの話です。当時はコンパイラやインタプリタやテキストエディタや各コマンドは、コンピュータメーカーが OS の一部として開発・提供するものであり、それが広義の OS の定義となっています。

補足 確かな情報ではありませんが「広義の OS」や「狭義の OS」は JIS で定義されていたらしいです。定義されている場所は「JISX0001 情報処理用語―基本用語」だろうと思って見てみたのですが「基本ソフトウェア」や「応用ソフトウェア」という用語はありましたが「広義の OS」や「狭義の OS」は載っていませんでした。JISX0001 は 1970 年に制定され何度か改訂が行われているため「広義の OS」や「狭義の OS」は廃止された用語ではないかと疑っています。たまーに最近でもこれらの用語を使っているのを見かけますが、廃止された用語だと確かめられれば古い定義だから今更持ち出すなと堂々と言えるのですが、どなたか詳細をご存じないでしょうか?

現在は、ソフトウェアの開発や提供方法は多様化しており、シェルもコマンドもカーネル(の一部のドライバ)もミドルウェアもアプリケーションも、ソフトウェアのほぼ全てに「OS ベンダーが開発したもの」と「OS ベンダー以外で開発されたもの」が存在しています。OS ベンダーは自分たちが開発したかどうかに関係なく、優れたソフトウェアを OS の一部として組み込んでいます。コンパイラでさえ誰かが作ったものを採用しています。そのような変化が起きたのは 1990 年代半ばから大きく広まったオープンソース文化による影響が大きいです。その頃にパッケージ管理システムが登場し、今では多数のソフトウェアが OS 標準のパッケージ管理システムから簡単に追加インストールできるため、どこからどこまでが OS の範囲であるかわからなくなっています。アンインストールすることができない最小構成の範囲が OS の範囲だという考え方もあるかもしれませんが、Debian Linux では Perl が、RedHat Linux では Python が最小構成に含まれておりアンインストールすることはできません。現在 OS の定義があるとすれば「OS ベンダーが OS と決めたものが OS である」という定義ぐらいしかできません。

1990 年代半ばは UNIX 全盛期時代が終わり、パソコンの性能が大きく向上し、インターネットが民間に開放され、プレステが誕生し、多数のプログラミング言語やオープンソースソフトウェアが生まれ、オブジェクト指向プログラミングが普及し、Linux 時代へと移行した境目の時代です。UNIX を学ぶということは、往々にして前時代のコンピュータの世界を学んでいることになりがちなので注意が必要です。2000 年代半ば以降の世界は今とそれほど変わらないのですが(例えばクラウドサービス AWS が登場したのが 2006 年、スマホの誕生が 2007 年)、30 年以上前の世界は現在当たり前のようにあるものの多くがなかった時代で、20 年前と 30 年前とではコンピュータとソフトウェア開発の世界は比べ物にならないぐらい大きく異なっています。そのため 30 年以上前の技術的な常識は現在では通じなくなっているものが多数あります。

アプリケーションの層はシェルとカーネルの間にある

特に海外で多いような気がしますが、なぜかシェルの外側にアプリケーションの層を書いているものが結構あります。しかしそれは呼び出し階層(ユーザーに近い層を外側に書いてカーネルまでの呼び出しの流れを表した図)としては間違いです。間違いであることは「シェルはユーザーインターフェースなのだから外側に決まってるだろ」の一言でわかると思います。シェルはユーザーが直接使用するソフトウェアなのでカーネルに近い内側ではなく外側に書くべき層です。

アプリケーション層はシェルの外ではない

間違った呼び出し階層はシェルの外側にアプリケーションの層があります。これだとカーネルに近い所にシェルがあってアプリケーションはシェルという余計な中間層を経由してカーネルの機能を呼び出さなければならないように見えてしまいます。真実は逆です。アプリケーション(コマンドや GUI アプリなど)の方がカーネルに近い所にあって、シェルおよびシェルスクリプトはアプリケーション(コマンド)の層を経由してカーネルの機能を呼び出さなければなりません。シェルスクリプトは一般的に遅いと言われていますが、その理由の一つは間にアプリケーションの層を経由しなければならないからです。ソフトウェアの速度はこれだけで決まる単純なものではありませんが、一般論として余計な層がないアプリケーション(他の言語)の方が速いというのは言うまでもないでしょう。

なぜこのような間違いをしてしまうのか悩みましたが、おそらく「シェルとカーネルだけが OS で、アプリケーションは OS ではないという間違った発想」から来ているものだと推測しています。シェルとカーネルの概念図にはシェルとカーネルしか出てきておらず、この二つが OS の全てであるかのように錯覚してしまいますが、もちろんそうではありません。前項で説明した通り UNIX コマンドは OS の一部 で、なおかつアプリケーションです。

シェルとカーネルだけが OS だと勘違した上に、さらにどちらも OS であるためこの二つを分離することができない、つまりシェルとカーネルは連続していなければならないという間違った思い込みも考えられます。そしてアプリケーションは OS 上で動くと表現され、OS ではないからとシェルの外側に書きます。その結果生まれたのが上記の左側の図の「間違った呼び出し階層」です。(元から呼び出し階層として書いていない可能性もあります。この話については第四章で紹介しています)

シェルがユーザーインターフェースでユーザーが一番最初に触るソフトウェアであることを知っているのであれば、上記の右側の図の「正しい呼び出し階層」のように外側(ユーザーに近い場所)に書くはずです。シェルとカーネルは共に OS ですが、この二つは分離することが可能で、アプリケーションの層はシェルとカーネルの間にあります。アプリケーションには OS の一部である UNIX コマンドだけではなく、誰でも好きなアプリケーションを追加することができます。

シェルはカーネルを外部から保護する層ではない

たまにシェルの役目としてカーネルを保護しているなどという説明が見つかります。シェルはカーネルを覆っている層というのは(技術的には間違っていますが)概念的には間違ってはいません。ただし覆っているというだけでカーネルを外部から保護しているわけではありません。中心にある核(カーネル)を保護してるのはクルミや卵や貝の殻(シェル)の話でしょう? それらはたとえ話に過ぎないので忘れてください。核の周りにあるからシェルと呼ばれているだけで、核を守っているからシェルと呼ばれているわけではないのです。シェルはユーザーの不正な操作や間違った操作からカーネルを守るような機能を備えてはいません。そもそもシェルにそのような機能を持たせたとしてもアプリケーションはシェルを経由しないので意味がないのです。アプリケーションはシェルとカーネルの間にあります。

シェルはカーネルを保護するそうではない

アプリケーションには OS の標準コマンドだけではなく、追加でインストールするコマンドや GUI アプリなども含まれます。それらは誰でも作ることができます。あなたが OS の標準コマンドだけでは足りないなと思って、なにかの言語で便利に使える追加コマンドを作成したのなら、それはアプリケーションを作ったということです。一部を除きアプリケーションはシェルを経由せずにカーネルの機能(システムコール)を呼び出します。誰でも自由に作ることができるアプリケーションはシェルを経由せずにカーネルの機能を呼び出すわけで、シェルにカーネルを保護する機能を持たせたとしても意味がないというのが上記の図でわかると思います。カーネルをユーザーの不正な操作から保護しているのはカーネル自身です。

補足ですが、シェルはカーネルを保護していませんが「あるものを保護するシェル」であれば存在します。これについては第四章を参照してください。

シェルスクリプトはカーネルの機能を呼び出せない

シェルとカーネルの概念図から導き出される誤解の一つは、シェルおよびシェルスクリプトからカーネルの機能(システムコール)を呼び出すことができるという誤解です。シェルが内部で呼び出しているシステムコールもありますが、プロセス管理やファイルアクセスといった限られた数のシステムコールしか使用していません。他の多くの言語は、任意のシステムコールを呼び出すことが出来るか、システムコールの呼び出しが必要ないぐらい言語機能やライブラリが充実していますがシェルスクリプトはそうではありません。そもそもシェルスクリプトはカーネルの機能を使うのが目的の言語ではなく、あくまでコマンドを連携させて一連の流れを自動化するものです。コマンドは適切なものがなければ自分で作るもので、シェルスクリプトは他の言語と共に使うのが大前提です。シェルスクリプトだけではダメなのです。つまりシェルスクリプトを学ぶのであれば他の言語も学びましょうということです。

この誤解もシェルとカーネルの概念図にアプリケーションの層が書かれていないから生まれたものでしょう。シェルのすぐ内部にカーネルが書いてあればシェルが人間の言葉を翻訳してカーネルの機能(システムコール)を呼び出していると勘違いしても不思議ではありません。シェルスクリプトおよびアプリケーションの層を付け加えると以下のようになります。

シェルはカーネルの機能を呼び出せない

シェルスクリプトはシェルよりもわずかに外に位置する層です。その理由はシェルスクリプトがユーザーに代わって自動的にコマンドを入力するものだからです。シェルスクリプトに書いてあるコードは人間がシェルから入力するものと全く同じです。したがって面倒という点を無視すれば「シェルスクリプトによる自動処理」は「人間の手作業」と同等ということになります。それがシェルスクリプトが最も外側の層である理由です。

シェルスクリプトから任意のシステムコールを呼び出せないのに加え、標準的な UNIX コマンド(またはそのサブセットである POSIX コマンド)もまた、システムコールの全てを使用しているわけではありません。これは OS の基本的なコマンドだけでは OS の能力を引き出せないということを意味しています。したがってシェルスクリプトを有効活用にするには OS に含まれていないコマンドの存在が重要です。かつては OS の能力を引き出すには自分でアプリケーションを作らなければなりませんでした。しかし現在はオープンソース文化のおかげで自由に使える多数のアプリケーションが開発され、標準的なパッケージマネージャーから簡単にインストールして使うことができるようになりました。それらを使うことで低機能な UNIX コマンドだけを使って悪戦苦闘する必要性も大きく減りました。

もちろん、既存のコマンドで不十分な場合は自分で作ることも可能です。コマンドを作る言語は C 言語である必要はありません。Go や Rust などのその他のコンパイラ言語でも、Python や Ruby と言ったスクリプト言語でも構いません。ほとんどの言語はシステムコールを直接、または言語の標準機能やライブラリを通して使うことができるので OS の能力を引き出すことが可能です。後発の言語は C 言語やシェルスクリプトよりも生産性や移植性が高いというメリットがあります。これらの言語の多くは UNIX 全盛期時代にはなかった(普及してなかった)もので、直近の 30 年間の大きな改善です。シェルスクリプトと基本的なコマンドだけでは限界があるので他の言語やその他のコマンドを学ぶ必要がありますが、他の言語を学べばシェルスクリプトがいらないかというとそうではありません。その他の言語は言語の関数を組み合わせてコマンド(またはアプリケーション)を作るための言語で、シェルスクリプトはそのコマンドを組み合わせるための言語で用途が全く異なるからです。

シェルスクリプトを学ぶということはシェルスクリプトの言語の文法や awk や sed といった伝統的な UNIX コマンドの使い方を学ぶこと(だけ)ではありません。UNIX コマンドは誰でも作ることができる数多くのコマンドの中の一部に過ぎず、将来別のコマンドに置き換えられてしまう可能性があるようなものです。シェルスクリプトの言語でさえ将来は別の文法を持った言語に変わる可能性があります。重要なのはコマンドを組み合わせる考え方を学び、その考え方に従って自分で(他の言語で)コマンドを作る方法を学ぶことです。なんでもシェルスクリプトと既存の UNIX コマンドだけでやるという考え方は正しい UNIX 哲学の考え方ではありません。手元にある道具だけでなんとかするのではなく、新しい便利な道具を探して使う、無ければ作る、それが UNIX 哲学の考え方です。

アプリケーションがシェルを呼び出すシェルアウト

ここまではアプリケーションはシェルを呼び出さないと書いていましたが、シェルアウトと呼ばれる例外があります。シェルアウトという用語は聞き慣れず調べても情報も少なくて困ったのですが The Art of UNIX Programming である程度説明されていました。以下は「7.2.1 専用プログラムに処理を委ねる」からの引用です。

コストの低いプロセス起動によって実現可能になるプロセス間の共同作業としてもっとも単純な形態は、特別な仕事の実行のために別のプログラムを起動するというものだろう。system(3) 呼び出しによって Unix シェルコマンドが呼び出されることが多いので、これは呼び出されたプログラムへのシェルアウトと呼ばれることが多い。(中略)古典的なUnixのシェルアウトは、メール、ニュースプログラムからのエディタ呼び出しである。Unixの伝統では、一般的なテキスト入力を必要とするプログラムにそのためのエディタを組み込んだりはしない。代わりに、編集が必要になったときには、ユーザーの好みのエディタを呼び出せるようにするのである。子プログラムは、通常、ファイルシステムを介して親と通信する。指定された位置のファイルを読み書きするのである。エディタやメーラのシェルアウトは、このような仕組みになっている。

この本やネットから雑に調べた結果から、あるプログラムからシェル経由でコマンドを呼び出すことをシェルアウトと呼ぶようです。(2022-12-12 公開時にシェルアウトの定義に自信がなかったために曖昧に書いていた文章を修正しています。コメント参照)

シェルアウトとは?

シェルアウトのパターンではアプリケーションからシェルを呼び出すという形になります。例外もそれなりにありますが、最近のアプリケーションはアプリケーションの中からシェルや別のコマンドを呼び出すことは少なくなっています。確かにターミナルで動くメールソフトを作る場合、新規メールの作成のために専用のテキストエディタを実装するよりも、すでにあるテキストエディタを呼び出す方が簡単です。git コマンドでもコミットメッセージの作成でユーザーの好みのテキストエディが使用できるようになっています。しかし GUI アプリやウェブアプリのメールソフトではアプリ自身にテキストエディタの機能が組み込まれているのがほとんどです。そのような目的に使える汎用的なコンポーネント(GUI ライブラリ)もあり、わざわざ作っているのではなく組み合わせて作っています。

テキストエディタは対話型コマンドの例ですが、対話型ではないコマンドを呼び出す場合にもシェルを経由することがあります。ただし一般的な計算や文字列処理、よく知られたアルゴリズムなどは別のコマンドで処理させなくとも、その言語で同等のライブラリが提供されており、それを使うだけで簡単に処理できます。自分で処理を書くよりも「すでにコマンドがある」のだからそれを利用するというのは、コマンドを組み合わせる UNIX 哲学的な考え方です。しかし今は「すでにライブラリがある」のだからそれを利用すれば良くなっています。つまりコマンドを使うほうが良いというのは「ライブラリがない時代に自分で処理を書くよりも」なのです。ライブラリができてしまった今はライブラリを使っても十分 UNIX 哲学的です。組み合わせるものがコマンドからライブラリに変わっただけです。

各言語のネイティブなライブラリが充実した現在ではシェルアウトおよびシェル経由でのコマンド実行は「コマンドはあるが言語用のライブラリがない場合にのみ使用する例外的なパターン」と言えるでしょう。しかしながら各言語のライブラリが少なかった昔はシェルアウトは今よりも一般的なパターンだったと思われます。1990 年代の初めの頃までは現在よく使われている言語の多くは普及どころか誕生もしていなかったからです。GUI アプリであってもフロントエンドの GUI インターフェースだけを GUI アプリとして作って実際の処理はコマンドを実行しているだけというパターンもあります。それを踏まえれば古い記事でアプリケーションがシェルを呼び出すように見える図が書かれている理由も理解できます。

ただ一つ疑問なのは、なぜ外部コマンドを呼び出すのにシェルを経由するのか?です。外部コマンドを呼び出す場合、シェル経由で実行せずともコマンドを直接実行する手段があります。シェル経由の場合、シェルのルールで引数を正しくエスケープしなければ予期せぬ結果を引き起こす可能性があり、場合によっては OS コマンドインジェクションの脆弱性の原因になります。そのため最近では意図的にシェル経由で実行したい場合でない限り、直接呼び出すのが推奨されているというのが私の認識です。「Shelling Out Sucks」でもシェルアウトの問題点が指摘されています。シェルアウトという用語を聞かないのは、昔はよく使われていたが最近はシェル経由で呼び出すことが少なくなったからなのかもしれません。

ミドルウェアはカーネルのすぐ上にある OS の拡張機能

少々シェルとは関係の薄い話になるのですが OS の構造としては重要なミドルウェアの話です。ミドルウェアがどのようなものかわからない人がいるかもしれないので補足すると、簡単に言えばミドルウェアは OS の拡張機能です。OS が提供している機能を超える機能を提供しており、ミドルウェアを使用するとアプリケーションの開発が簡素化されます。コマンドやライブラリなども考え方によっては OS を拡張している機能と言えなくはないのですが、ミドルウェアは一般的に OS の起動と同時に起動してシステムで動く複数のアプリケーションへ統一したサービスを提供するという点でより OS に近い機能を持っていると言えます。GUI を提供するウインドウシステムもミドルウェアの一種と考えてよいでしょう。現在は OS に組み込まれている TCP/IP スタックも以前はミドルウェアとして提供されていたそうです。OS の機能に含まれても不思議ではないぐらいシステム寄りの機能ということです。

余談ですがミドルウェアという用語は 1968 年のソフトウェアエンジニアリングに関する NATO 会議で作られた言葉のようです。したがって UNIX (1969) が誕生するよりも前からあった言葉で、もともとはメインフレーム時代に必要になったソフトウェアだということがわかります。

ミドルウェアはアプリケーションと OS(というよりむしろカーネル)の間に入るものという一般的な定義から、アプリケーションとカーネルの間に書くのが適切でしょう。とは言ってもミドルウェアを導入したらアプリケーションから必ずミドルウェアを経由してカーネルの機能を呼び出さなければならなくなるという意味ではありません。必要なときにアプリケーションから利用できる便利な OS の拡張機能です。

ミドルウェアはOSの拡張機能

ミドルウェアがアプリケーションとカーネルの間に入るからと言ってオーバーヘッドでアプリケーションが遅くなるということにはなりません。むしろ一般的にはミドルウェアを使ったほうがアプリケーションは速くなります。これはミドルウェアが常に起動しているため素早く応答することが可能で、多くのメモリを効率よく使って高いパフォーマンスを実現しているからです。ミドルウェアを使わなくても、その機能が必要であれば結局は同等の処理を自前で書かなければなりません。自前で書いたものが専門家が作ったミドルウェアよりも高いパフォーマンスを実現できることはまずないでしょう。技術力ではなく開発期間や投入した人的リソースの問題です。有名なミドルウェアともなればかなりの開発コストをかけて作られています。もっともミドルウェアが提供している機能が自分が欲しい機能に対して過剰すぎる場合は、簡易的な実装をすることで高いパフォーマンスを出せる場合はあります。しかしそれは適材適所の問題です。パフォーマンスが必要ならコンピュータの性能を上げることでも解決できますし、それよりも生産性や信頼性のほうが重要な場合が多いでしょう。一つの技術でどんなものにも対応できるという万能の技術はありません(参考 銀の弾丸はない)。ミドルウェアの機能が必要なら使い、そうでないときは使わない、混ぜて使えばいいだけの話です。

いくつかのミドルウェアは複数のコンピュータとアプリケーションからなるシステムを協調して動作させるという重要な仕事を行っています。大規模なシステムでは高い性能や信頼性を実現するのは大変です。特に重要なミドルウェアがデータベース管理システム (DBMS) です。データの読み込みだけしか行わないのであれば、複数台のコンピュータを連携させることはさほど難しくありませんが、データベースは一般的に書き込みが発生します。複数台のコンピュータを用いるシステムではデータを正しく更新すること(トランザクション処理)が難しくなりますが、多くの DBMS は複数のコンピュータにまたがるデータベースのトランザクション処理に対応しています。またデータの更新が伴うデータベースではスケールアウト(サーバーの台数を増やしてシステムの処理能力を高めること)が難しくなりますが、そのような機能にも対応しており比較的簡単に実現することが可能です。大規模なシステム開発ではミドルウェアは必須のソフトウェアといえます。

ミドルウェアは難しく高度な機能を簡単なインターフェースで利用できるようになっています。もちろん高度な機能を使いこなすにはそれだけ技術が必要ですが、それができるのが技術者というものです。技術が必要だと言っても高度な機能を自分で作るよりかははるかに簡単です。ミドルウェアを使うことでアプリケーションの開発が容易になり、開発者はデータ管理などの本質的ではない煩わしい処理に振り回されること無く、本当に作るべき機能の開発に集中することが出来ます。例えばトランザクションのような難しい処理(複数台構成のシステムで予期せぬ電源断が発生してもデータの整合性が壊れないように自動的に正しくロールバックするのは容易なことではありません)を自分で実装するよりも専門の会社または団体が時間をかけて開発し、広く使われて実績のあるミドルウェアを使う方が信頼性が高くなるのは当然のことです。

昔は無料のソフトウェアは信用できないし性能も悪いなどと言われていた時期もありましたが、現在は無料で使えるオープンソースのソフトウェアも性能も信頼性も高くなっており安心して使うことができます(というかあちこちで使われています)。無料ということはサポートがないのでは?と思うかもしれませんが、大きなミドルウェアであればオープンソースでも有償サポートがあります。もしミドルウェアを使わずにシステムを開発しようとすると開発コストが大きくかかってしまうでしょう。難しいことは自前でやるよりも専門家(ソフトウェア)にやらせたほうが良いのは言うまでもありません。

ちなみに、ミドルウェアは「間に入るソフトウェア」という意味でしかなく、別の意味で使われている場合もあるので注意してください。例えばウェブアプリケーションのフレームワークの処理に割り込ませるプラグインをミドルウェアと呼んだりします。

シェルやコマンドはアプリケーションにとって必須ではない

現在のシステム開発においてウェブシステムは避けて通れませんが、ウェブシステムを動かす環境であればシェルやコマンドは必須ではありません。これは UNIX を対話的に使っていた時代との大きな違いです。シェルとカーネルの概念図からはシェルがカーネルとのインターフェースとして必ず必要な印象を受けるかもしれませんがそうではありません。

シェルはアプリケーションにとって必須ではない

ウェブシステムではコンピュータはネットワーク(インターネット)経由で使います。ウェブアプリであればブラウザから使うことが多いでしょう。その場合にシェルやコマンドを使うことは一般的にありません。このような用途のためにシェルを含む CLI 環境をいっさい搭載しない Distroless という OS でさえ存在します。このことはシェルはアプリケーションにとって必須のものではないということを意味しています。例えばミドルウェアだけのシステムもあります。ミドルウェアだけでどうやって使うんだ?と思うかもしれませんが、この場合は別のコンピュータにアプリケーションがあって、アプリケーションからはネットワーク経由でミドルウェア(例えばデータベースサーバー)の機能を使います。

小規模なシステムでは一台のコンピューターにアプリケーションもミドルウェアも組み込むことがあります。今のコンピュータの性能は十分高いので個人ブログや中小企業で使う内部システム程度であればこのような構成でも十分でしょう。しかしアクセス数が予想できないインターネット上のシステム(例えば話題になったら大幅にアクセス数が増えピークを過ぎれば大幅に減る)や 24 時間 365 日停止できないような高い信頼性が求められるシステムでは一台のコンピュータでシステムを構築することはできません(この世界に災害が起きない場所はありません)。そのような場合は複数台のコンピュータで一つのシステムを構築します。

複数台のコンピュータを使ったシステムは一般的に分散システムと呼ばれます。大規模なシステムでは一台のコンピュータでは処理をまかないきれないので、ウェブアプリ用、データベース用などの役割ごとにコンピュータに分けます。一台でも高性能のコンピュータを導入すればすむじゃないかと思うかもしれませんが、ピーク時とそうでないときで負荷が大きく異なる場合は何もしていない時間が発生します。つまり高性能のコンピュータを導入すると性能を使い切れないので無駄が多いのです。クラウドではそのような問題に対処するために仮想マシンを使って負荷に応じてコンピュータの台数を自動で増減(オートスケール)する仕組みを提供しており、コンピュータを使った分だけ課金されるようになっています。このような大規模なシステムを支えているのがミドルウェアです。

コンピュータを使った(大規模)システムは一台のコンピュータで処理する集中コンピューティングから、複数台のコンピュータを利用するクラウドへと拡張されていきました。現在のコンピュータは安価で高性能になりましたが、それと同時にシステムの規模も大きくなり要求される性能や信頼性も高くなっています。

(出典 総務省(2019)「平成の情報化に関する調査研究」 P18 より)
image.png

上記の補足 クラウドが集中コンピューティングとなっているが、クラウドを一つとみなしているだけで、クラウドの中は一般的に分散コンピューティング(複数台構成)である

シェルスクリプトはミドルウェアと相性が悪い

ミドルウェアは OS の拡張機能として優れた機能を提供していますが、実はシェルスクリプトから使うには相性が悪く、ミドルウェアの機能や性能を引き出すことが困難です。これはミドルウェアがネットワーク経由で機能を提供しており、シェルスクリプトの言語機能にネットワークアクセスの機能がないのとコマンドの入出力にデータ型がない(文字列型しかない)のが原因です。この話はシェルスクリプトはどういう所に使うものなのか?という話につながります。

シェルスクリプトからミドルウェアを使う場合、外部コマンドを経由しなければなりません。しかしそれだと外部コマンド呼び出しのオーバーヘッドで、他の言語の API(内部的にはソケットやネットワーク通信)よりも数桁レベルで桁違いに遅くなってしまいます。使うだけなら例えば RDBMS ベンダーが用意している管理用の CLI コマンド(mysql コマンド)などを使って SQL を実行したりできますが、このような CLI コマンドは主にメンテナンス用に使うもので起動が遅く、通常のデータ処理を行うために頻繁に呼び出すような使い方としては設計されていません。ほとんどのミドルウェアは API 経由で使うものとして設計されており、シェルスクリプトに限ればミドルウェアを使うことで逆に遅くなってしまう可能性すらあります。

シェルスクリプトとミドルウェアは相性が悪い

一部のシェルに限れば /dev/tcp/host/port からネットワークを使うことができるのですが、tcp と書いてあるように TCP プロトコルレベルでの対応で HTTP プロトコルのような上位層のプロトコルには対応していません。HTTP プロトコルなどに対応したシェルスクリプト用のライブラリもありません。つまり HTTP ヘッダーの内容を自分でパースして、HTTP メソッド(GET, POST, PUT, DELETE) やエンコーディング、マルチパート (multipart/form-data) や認証など大きな HTTP プロトコルの仕様に自前で対応しなければならないため、シェルスクリプトでまともなウェブシステムを作ろうとしたら開発時間や開発コストがかかることを意味しています。シェルスクリプト用のウェブフレームワークもないことはないのですが、やはり他の言語のものと比べれば簡易的な機能しか持ち合わせていません。HTTP 以外のプロトコルに対応したい場合は、もちろん別にライブラリが必要です。他の言語であれば、ほとんどの言語で上位層のプロトコルに対応したライブラリが用意されています。こういった所からもシェルスクリプトの適用範囲の狭さ(頑張ればできないことはないが現実的ではない)がわかります。

最近はクラウドを使ったシステムも一般的になりましたが、クラウドプラットフォームで提供されているサービスもネットワークから利用することが前提になっています。これはシェルスクリプトを使うシステムはクラウドプラットフォームとの相性も悪い事を意味しています。各クラウドベンダーは CLI 用のツールも用意していますが基本的に管理用のツールです。シェルスクリプトはスケールさせることが困難です。一台構成のウェブシステムをスケールする場合、まず最初にウェブアプリとデータベースを分離させることを考えますが、ネットワークが使えないシェルスクリプトや UNIX コマンドはローカルのファイルシステムにしかアクセスできず分離が困難です。ネットワークファイルシステム (S3 など) もありますが遅いです。クラウドベンダーまたはサードパーティは各言語用のライブラリを提供していますが、そこにシェルスクリプト用のライブラリはありません。その代わりにあるのが呼び出しが遅い CLI コマンドというわけです。せっかくのクラウドも専用サーバーや VPS サーバーのようなコンピュータを借りて使うレンタルサーバーのような使い方しかできないでしょう。もしクラウドシステムでシェルスクリプトを使う場合、コンピュータの構成管理など一台のコンピュータで行える範囲か、死活監視プログラムのようにそれほど負荷の大きくない部分での補助的な使い方に限定されます。

昔は GUI アプリでユーザーインターフェースのみを GUI で開発しメインの処理はコマンドを実行していたこともありますが、今はコマンドに代わるライブラリがあります。CGI を使ったウェブシステムは遅い代わりにメモリ使用量が少ないというメリットもありましたが、大容量のメモリが使えるようになった現在ではメモリを使って高速化させるほうがメリットが大きいです。シェルスクリプトでも書き込みが発生せずシーケンシャルな読み込みだけで十分な場合に限ればやれなくもないと思いますが、その程度であれば他の言語を使っても簡単に実装できます。適材適所を心がけましょう。シェルスクリプトに適している用途はシェル上で手作業で行う内容を手軽に自動化することです。

正しいシェルとカーネルの概念図と呼び出し階層はこれだ!

注意 正しいと言っちゃっていますが、考え方によって細かいバリエーションはあります。でもまあ悪くないと思います。少なくとも初学者がこれからのソフトウェア開発を学んでいく過程で混乱することはないかと。

この記事は基本的に CLI 環境の話ではあるのですが、現代のソフトウェア開発は CLI 環境だけで終わるようなものではありません。OS の基本的な知識を CLI 環境の範囲だけで終わらせてしまっては、それこそ間違った知識が広まってしまうのでそれは避けねばなりません。GUI / TUI アプリ、およびウェブアプリを図に書き加えるならば以下のようになります。

シェルとカーネルの概念図と呼び出し階層

見てわかる通り単純な同心円では現在のシステムをうまく表すことができません。このような切れ込みが必要です。CLI 環境の場合、つまりシェル及びシェルスクリプトを使う場合とそれ以外では経由する層が全く違うので、これらは分けて考えなければなりません。

ただし上記の図では、少々包括すぎて初学者には複雑に感じるかもしれません。私が書いた変な図を教えたくないという人もいるでしょう。したがって シェルを学びたい人のために CLI 環境に限定するという前提で以下の図を私はオススメします。シェルを教えるのにシェルから呼び出すコマンドのことを教えないのはありえません。シェルとカーネルの概念図ではなくシェルを学ぶ人のための図です。

シェル使用時の概念図

CLI 環境、つまりシェルは主にプログラマがコンピュータを対話的に使うための道具です。そしてシェルスクリプトはその操作を自動化するものです。シェルやシェルスクリプトの知識というのはどちらかと言えばエンドユーザー寄りの知識です。シェルおよびシェルスクリプトを学ぶことは今も重要であると私は考えていますが、これらは手作業または自動化のツールに過ぎず、それだけの知識では実際のシステム開発の現場では全く足りません。シェルやシェルスクリプトの知識だけがあれば十分なシステム開発はありませんので、技術者を目指すのであれば幅広い知識を身につけるようにしましょう。持っている知識は多くて損をすることはないはずです。幅広い知識を身につけて初めて適材適所で適切な道具を選択できるようになります。

第二章 シェルとコマンドの処理とデータの流れ

この章全般に共通する話として、ユーザーからの入力またはディスプレイの出力に「実際にはカーネルを経由している」というデータの流れを省略しているということに注意してください。具体的には次のような流れが省略されています。

  • ユーザーがシェルや対話型のコマンドへキー入力した
    • → 実際にはユーザーのキー入力はカーネルが受け取りシェルや適切なコマンドに転送している
  • コマンドがディスプレイへ結果を出力した
    • → 実際にはコマンドからの出力はカーネルが受け取りディスプレイに出力している

シェルはコマンドの出力結果を中継しない

次の図は第一章の「間違ったシェルとカーネルの概念」に基づいた図です。シェルがユーザーの入力をカーネルがわかる言葉に翻訳、またはカーネルの出力をシェルがユーザーがわかる言葉に翻訳しているという間違いに基づいてコマンド実行とデータの流れを表すとこのようになるでしょう。元の図からして間違っているので以下のような解釈ももちろん間違いです。

間違ったコマンド実行とデータの流れ

この図の大きな問題点は、シェルスクリプトから呼び出すコマンドの存在が抜けている所です。実際に処理をしているコマンドが抜けている以上、これらの要素だけで正しい図を書くことはできません。

補足 ls コマンドの実行ではなく、シェル自身に組み込まれている機能を使った場合はシェルがカーネルに要求してその結果を受け取ることもあります。例えば echo *.txt を実行すると、シェルがファイルやディレクトリ一覧をカーネルに取得要求し、その結果をシェル(に組み込まれている echo コマンド)が出力します。ただしこれは一部の機能に限った話で、ほとんどのコマンドはシェルには組み込まれておらず、カーネルに要求しているのはシェルから呼び出されたコマンドです。

シェルは依頼したコマンド実行が終わるまで停止する

シェルが行うのは基本的にコマンドの実行をカーネルに依頼する所までです。まずユーザーインターフェースとして、シェルはユーザーがキー入力した ls という文字列を読み取ります。念の為ですがユーザーから送られてくるのは ls という文字列であって ls コマンドの実行ファイル(プログラム)がシェルに送られてくるわけではありません。何を言ってるんだと思われるかもしれませんが、このように解釈できるような説明をしている所があるのです。シェルはユーザーが入力した文字列を読み取ってその意味を解釈し、カーネルにそのコマンドの実行を依頼してコマンドの終了を待ちます。

正しいコマンド実行とデータの流れ

補足 コマンド実行中はシェルは(基本的に)何もしていないと書きましたが、コマンドと同時にシェルが動く場合もあります。例えば while read line; do echo "[$line]"; done < <(seq 10) のようなコードを実行すると seq コマンドとシェルは同時に並行して動きます。しかしそれはそのようなコードを書いたからであって、シェルが裏で勝手に翻訳作業や何かをしているということではありません。

対話型コマンド実行中はそのコマンドが入力を受け取る

対話型コマンド(ここではテキストエディタの vi コマンド)を実行した場合も同様に、テキストエディタで保存して終了するまではシェルは何もしません。ここでのポイントは対話型コマンドの実行中にユーザーのキー入力を受け取るのは誰なのか?です。シェルは何もしていないのだから対話型コマンドということです。シェルがユーザーの入力操作を vi コマンドに中継するようなことはありませんvi コマンドの実行中はユーザーの入力は vi コマンドが直接受け取っています。

対話型コマンドのデータの流れ

パイプによるプロセス間通信はシェルを経由しない

パイプで繋いだ一連のコマンドのデータ通信の流れにもシェルは関与しません。シェルは一連のコマンドの標準入出力をつなげて実行するところまでが仕事です。つまりあるコマンドの結果をシェルが受け取って別のコマンドに転送しているわけではないということです。コマンド同士はパイプでつながっており、直接データを転送しています。

パイプ通信のデータの流れ

補足ですがパイプでつながったコマンドはそれぞれ並行して動作します。とは言っても ls コマンドがデータを出力する前に grep がデータを処理することはできないので、どれか一つのコマンドがボトルネックになります。それぞれのコマンドが十分速い場合、ボトルネックになるのは主にファイルの読み書き部分です。読み書きのファイルサイズが小さければファイルキャッシュとしてメモリに乗るのでファイルの読み書き部分でのボトルネックはそれほど大きなものにはなりませんが、メモリに乗り切らないほど大きなデータを扱う場合は注意が必要です。結局所大きなデータを読み書きする場合には効率よくメモリを使用するデータベースを使った方が良いというありきたりな結論になります。

シェルスクリプトからのコマンド実行の流れ

シェルスクリプトは簡単に言えばユーザーの操作を自動化したものなので、ユーザーの位置をシェルスクリプトに入れ替えた形になります。シェルスクリプトはユーザーまたは任意のシステムから実行されます。自動化ではコマンドの出力結果を元に次に実行する処理を決定することがあるため、コマンドの出力結果は画面に出力するのではなくシェルが受け取ることがしばしば発生します。

シェルスクリプトからのコマンド実行とデータの流れ

コマンドの出力結果をシェルが受け取る場合というのは、具体的には read コマンドによる標準入力の読み取りやコマンド置換 (var=$(...)) からの変数の代入などを使った場合です。シェルを手作業で使っている場合でもこれらの方法を使うことでシェルが出力を受け取ることもできるのですが、手作業で使う場合には出力結果は画面に出力し、その結果を人間が見て次に実行する処理を決定すれば良いので、出力結果をシェルが受け取ることはあまりありません。

補足として 上記の図の点線部分に CRON(クローン)サーバーや CGI サーバーといったミドルウェアからのシェルスクリプト実行の流れも書いています。この場合、シェルスクリプトを人間が実行する代わりに、指定時間やインターネットからのアクセス時に CRON・CGI サーバーがシェルスクリプト(または任意の言語で作られたプログラム)を実行します。この例に限らずなにかしらのシステムからシェルスクリプトを実行する場合はこのような流れになります。

第三章 シェルとカーネルの概念図 vs UNIX (OS) の構造

この記事はシェルとカーネルの概念図に焦点を当てていますが、シェルとカーネルの概念図を OS の構造だと勘違いする人がいるため、簡単にですが OS の構造についての解説をします。以下の図は、同心円で表現されていたシェルとカーネルの概念図に抜けている重要な層を追加して高水準(ユーザー寄り)から低水準(ハードウェア寄り)の順番で上から縦に並べたものです。必須ではないミドルウェアは省きましたが、入れる場合はアプリケーションとシステムライブラリの間だと思ってください。

シェルとカーネルの概念図とOSの構造の違い

この図で伝えたいことはシェルとカーネルの概念図には OS の構造として重要なものがこれだけ欠けているということです。シェルとカーネルの概念図はあくまでシェルとカーネルの関係を理解するためだけの図であり、OS の構造の全体を理解するためのものではありません。上記の図であっても OS の基本的な構造でしかなく、追加しようと思えばファイルシステムの層など細かい要素はいくつもあります。

カーネルとユーザーランド

上記の図の右側、その上部にユーザーランド(ユーザー空間)という枠を作っています。これはカーネル(カーネル空間)と区別するためのもので、シェルや多くの言語やアプリケーション、システムライブラリなどのほとんどのソフトウェアはユーザーランドに含まれています。カーネルはコンピュータ全体を制御するためのものです。コンピュータ全体を制御することが可能ということは、カーネルでのバグはシステム全体に影響することを意味しています。そのため極めて慎重なプログラミングが必要となります。影響が大きいため通常のアプリケーションの開発でカーネルを修正することはありません。アプリケーションの開発はカーネルに手を出さずにできるようになっています。

通常のアプリケーションはユーザーランドで動作します。ユーザーランドのアプリケーションからカーネルの機能を利用する時はシステムコールを呼び出します。シェルやシェルスクリプトから任意のシステムコールを直接呼び出すことはできないので、システムコールを直接呼び出したいときには何かしらのプログラミング言語で作られたアプリケーションが必要にあります。ユーザーランドで動くアプリケーションにはプロセスごとに個々に仮想的なメモリ空間が割り当てられ、他のアプリケーションに容易に干渉できなくなっています。アプリケーションのバグは基本的にそのアプリケーションのみの影響に留められ、他のアプリケーションに影響が及ばないようになっています。他のアプリケーションとデータをやり取りする場合にはプロセス間通信を使用します。パイプやネットワーク通信はプロセス間通信の手段の一つです。ファイルも一応プロセス間通信の手段の一つとして考えられているようです。プロセス間通信は一般的にユーザーランドとカーネルをまたがるデータ通信になるため遅い部類の処理になります。

ときどき勘違いしている人がいるようですが、カーネルで処理した方が速いという性質はありません。遅いのはユーザーランドとカーネルをまたがる処理やデータ通信です。それを避けるためにカーネルに処理を移すということはありえますが、別にユーザーランドだけで処理を行っても同じことです。仮にシステムコール(カーネルの機能)とシステムライブラリ(OS の機能だがカーネルの機能ではない)に同じことを行う関数があったとして、アプリケーションからの呼び出しであれば同じユーザーランドのシステムライブラリを利用するほうが速いということです。また各言語のライブラリも(実装による差はありますが)どちらも同じユーザーランドであるためシステムライブラリと本質的な速度の違いはありません。システムコールとは異なりシステムライブラリは使う必要はない(各言語のライブラリで実装できる)ため、Go のような移植性が高い言語では移植性が低くなる原因であるシステムライブラリ(OS の機能は OS によって微妙に異なる)に依存せずに、可能な限り言語のライブラリだけで実装し、必要な場合はシステムコールを直接呼び出すようになっています。

OS の構造と呼び出し階層

シェルはユーザーが直接使用するソフトウェアです。したがって最も高水準(ユーザー寄り)のソフトウェアとなります。シェルスクリプトはシェルを動かすためのスクリプトでユーザーの手作業の代わりに自動化したものです。したがってシェルと同等ですが少しユーザーに近い場所にあります。

シェルに次いで高水準のソフトウェアは、アプリケーション(UNIX コマンドなど)です。コマンドはシェルまたはシェルスクリプトから呼び出されます。TUI アプリや GUI アプリはシェルから起動しますが、起動中はユーザーが直接アプリケーションを操作します。アプリケーションはコンパイラ言語やスクリプト言語に関係なくさまざまな言語を使って作成されます。アプリケーションの層は最も厚い層で、多くのソフトウェアがこの層に含まれます。

この図からは省略していますがミドルウェア(ウェブサーバーやデータベースサーバー等)はアプリケーションと OS(カーネル)の間に入るものと言われているためアプリケーションの下に入れるのが適切でしょう。ミドルウェアとアプリケーションの技術的な違いはそんなに大きくないので個人的にはアプリケーションの下というよりも斜め下あたりだと考えています。

システムライブラリは OS が提供している共通関数のうちユーザーランドで実行されるものです。libc や glibc などが代表的なシステムライブラリの名前です。名前から推測できる通り元々は C 言語用のライブラリですが他の言語からも内部的に呼び出されることがあります。

カーネルはシステムコールというインターフェースを通してユーザーランドのソフトウェアと協調して動作します。また内部にハードウェアを制御するためのドライバの層を持っており、これが最も低水準のソフトウェアとなります。カーネル及びドライバは伝統的に C 言語で作られていましたが、最近は より生産性が高く堅牢な Rust を使おうという流れが進んでおり、Linux では近い将来 Rust で書かれたコードも使われるようになるでしょう。今後は低水準のプログラミングは C 言語だけではなく Rust も重要な言語となっていくと思われます。

第四章 間違ったシェルとカーネルの概念図が生まれた理由の考察

さて世の中には間違ったシェルとカーネルの概念図がたくさんあります。なぜこのようなものができてしまったのでしょうか? 調べてみるといろいろと面白いことがわかったのでそれらを考察としてまとめてみました。ここはおまけの考察なので興味がある人だけどうぞ。

おかしなシェルとカーネルの概念図

AT&T 公式の「UNIX を構成する三つの要素」

上記のようなアプリケーションがシェルの外側に書いてある図はあまりにも多くの所で使われており、歴史的にどれが最初かなんてわからないだろうと思っていたのですが、1982 年の AT&T ベル研究所の動画に初期の元ネタではないかと思われる映像を見つけることができました。これが最初かどうかはわかりませんが 1982 年は AT&T が UNIX を商用路線に切り替え、世の中で広く使われだした頃で比較的早い時期と言えます。

KERNELとSHELLとUTILITIES

この映像の UTILITIES というのはコマンドのことです。多くの人は ls のようなプログラムをコマンドと呼んでいますが、ユーティリティの方がより適切な呼び名で POSIX でも Utilities として扱われています。一番目の動画ではユーティリティは、エディタやコンパイラやドキュメント整形プログラムや自分自身で作成したプログラム、二番目の動画ではファイルの編集や番号のソートやプロットの作成や カーネルが直接提供していないその他のプログラムと言っています。GUI (X Window System) はまだ誕生してないので GUI アプリは考慮されていませんが、シェルやカーネルに含まれない全てのプログラムのことを指して UTILITIES と言っていることがわかります。

この動画ではシェルは「ユーザーとシステムとのインターフェース」と言っています。「ユーザーとカーネルとのインターフェース」とは言っていません。システムとは UNIX をインストールしたコンピュータシステム全体のことを指していると思われます。詳しく見たのは該当部分だけですが、私はこの図は UNIX を構成する基本的な要素が三つの層から成り立っていると言っているだけであると解釈しました。つまり外側の層が内側の層を呼び出す呼び出し階層ではないということです。おそらく「UNIX は中心にカーネルがあって UNIX を使うためのシェルがある。そして多くのアプリケーションがある(作ることができる)」というような事を言いたかっただけなのだと思います。

UNIX を構成する三つの要素

現在、GUI アプリやウェブアプリなどにとってはシェルは重要なものではありません。しかし UNIX が誕生した当時はエンドユーザーがコンピュータを使うといった場合、CLI 環境からの利用がコンピュータを使うということの全てであり、シェルは唯一のユーザーインターフェースでした。

「シェル」という技術用語の定義

包括的なシェルの定義はないかと調べた所、ハッカーの俗語をまとめた用語集ジャーゴンファイルハッカーズ大辞典 (Ascii books))に以下のような文章を見つけました。ジャーゴンファイルは元々は UNIX 誕生以前の 1975 年から続く AI や LISP 関係の用語集で 1990 年以降では UNIX 関連の用語集となったようです。

http://catb.org/~esr/jargon/html/S/shell.html より

[orig. Multics techspeak, widely propagated via Unix]

  1. [techspeak] The command interpreter used to pass commands to an operating system; so called because it is the part of the operating system that interfaces with the outside world.

  2. More generally, any interface program that mediates access to a special resource or server for convenience, efficiency, or security reasons; for this meaning, the usage is usually a shell around whatever. This sort of program is also called a wrapper.

  3. A skeleton program, created by hand or by another program (like, say, a parser generator), which provides the necessary incantations to set up some task and the control flow to drive it (the term driver is sometimes used synonymously). The user is meant to fill in whatever code is needed to get real work done. This usage is common in the AI and Microsoft Windows worlds, and confuses Unix hackers.

Historical note: Apparently, the original Multics shell (sense 1) was so called because it was a shell (sense 3); it ran user programs not by starting up separate processes, but by dynamically linking the programs into its own code, calling them as subroutines, and then dynamically de-linking them on return. The VMS command interpreter still does something very like this.

翻訳

[起源: Multics の技術用語で、Unix を経由して広く広まった]

  1. [技術用語] オペレーティングシステムにコマンドを渡すために使われるコマンドインタプリタ。それはオペレーティングシステムの一部で世界の外とのインターフェースであるためにそう呼ばれている。

  2. より一般的には、利便性や効率性やセキュリティ上の理由のために特別なリソースやサーバーへのアクセスを仲介するあらゆるインターフェースプログラム。この使い方は通常なにかの周りにあるシェルです。このようなプログラムはラッパーとも呼ばれています。

  3. 何かしらのタスクのセットアップために必要な呪文やそれを動かすための制御フローを提供する手書きまたは他のプログラム(例えばパーサージェネレータなど)によって作られたスケルトンプログラム(ドライバーという用語はしばしば同義語で使われることがある)。ユーザーは実際の作業を行うために必要なコードを記入することを意図している。 この使い方は AI と Microsoft Windows の世界では一般的で、Unix のハッカーを混乱させる。

歴史的メモ:どうやら、オリジナルの Multics シェル(意味 1)は、シェル(意味 3)だったことからそのように呼ばれたようです。それはユーザープログラムを別のプロセスを起動するのではなく、プログラムを自分自身のコードに動的にリンクしてサブルーチンとして呼び出し、戻り時に動的にリンクを解除していました。VMS のコマンドインタプリタは、今でもこれによく似たことを行っています。

まず 1. ですが、これが現在のシェルの意味で間違いないでしょう。

次に 2. ですが「セキュリティ上の理由のために特別なリソースを仲介する」という所から本当にシェルがカーネルを保護しているものという意味があったのか?と疑問になりましたが、私は Restricted shell の存在を思い出しました。よく読むとカーネルを守るとは書かれていません。Restricted shell はその名の通り「制限付きのシェル」で安全性の理由から行えることが制限されているシェルのことです。Restricted shell は 1981 年の System III Bourne シェルで実装されているようなので、このようなタイプの「シェル」という用法がこの時代にはあったことがわかります。

考えてみれば Remote Shell (1983) もシェルを名乗っていますし、SSH (1995) は Secure Shell の略です。最近ではクラウドでもブラウザベースでシェルを使う CloudShell という名前が使われています。これらは UNIX シェルではなくシェルっぽいなにかです。確かにラッパーとみなすこともできるでしょう。このようなシェルは一般的な意味のシェルとは異なるものですがシェルという名前であるため、ここからカーネルを守るシェルという勘違いが生まれた可能性が考えられます。

最後に 3. ですが、なかなか理解するのが難しかったのですが、これはコマンドライン引数を処理するコードの話をしているのだと考えています。歴史的メモにある Multics シェルの話がヒントになりました。Multics のシェル は UNIX のシェルとは異なり行を読み取る機能はリスナーと呼ばれる別のプログラムが担当しており、コマンドプロセッサのみの機能を指す用語のようです。

UNIX ではシェルがコマンドライン引数を処理しますが Windows ではアプリケーション側が引数の処理を行っています。例えばワイルドカード * の展開やクォートの処理でさえアプリケーション側の処理です。Windowes API には一行につながったコマンドライン文字列を処理するための CommandLineToArgvW が用意されています(ただしこの API は比較的新しいもののようです)。通常は各言語の処理系が裏で処理しているはずなので、引数を処理するコードを目にすることはないと思いますが、(私は記憶にないのですが)初期のスケルトンプログラム(自動生成されたテンプレートコードのこと)では、そのようなコードが生成されていたのではないかと考えています。

ただこのようなスケルトンプログラムを AI と Windows の世界でシェルと呼んでいたのか?については懐疑的で(この文章が書かれた昔の)Windows でシェルと言ったら一般的に DOS プロンプト (command.com) のことを指していたはずです(CONFIG.SYS の SHELL= を思い出しますね)。おそらく Multics のシェル(コマンドプロセッサ)相当の機能が昔の Windows (MS-DOS?) ではスケルトンプログラムだったというだけの話ではないかと思っています。AI の世界についてはもっとわかりませんが、ジャーゴンファイルがもともと AI と LISP の世界のハッカー用語集という所から出てきただけなのかもしれません。3. の正確な意味はよくわかりませんが、いずれにしろ現在この意味でシェルという用語を使っている人はほとんどいないでしょう。

ユーザーランドという用語がない時代のシェルの定義?

C 言語のコンパイラに cc コマンドがあります。ここで cc コマンドはシェルの機能の一つだと仮定しましょう。コンパイラがシェルの機能なわけがないだろと思うかもしれませんが、例えば echo コマンドはシェルにビルトインされたコマンドでシェルの機能の一つです。printf コマンドはどうでしょうか? ほとんどのシェルでシェルビルトインコマンドですが mksh という名前のシェルではビルトインコマンドではなく外部コマンドです。コマンド名からではビルトインコマンドなのか外部コマンドなのかわかりません。現在でもシェルのビルトインコマンドと外部コマンドの区別がついていない人は結構居ます。例えば sedgrepawk はシェルの機能ではないのですが、まるでシェルではこれらを使うのが常識であるかのように使っている人が多いです。これらにこだわらなくても他にもっと適切なものがあればそれらを使えば良いのですがね。例えば awk の代わりに perlruby とか。

シェルで使えるコマンドはアプリケーションをインストールすると増やすことができます。これをシェルの拡張機能(プラグイン)と考えてみてはどうでしょうか?そうすると cc コマンドはシェルの機能の一つと言えなくもないのではないでしょうか? もちろんこれはただの言葉遊びです。cc コマンドは明らかにシェルとは独立した別のプログラムです。もしシェルから実行できるコマンドすべてをシェルの機能の一つなどと言ったら、すべてのプログラムはシェルの機能の一つということになってしまうでしょう。しかしこれが UNIX が登場した 1980 年代だとしたらどうでしょうか? 世界はまだ UNIX もコンピュータもどういうものかよく分かっていない時代なのでコマンド = シェルの拡張機能という勘違いは十分ありえると思います。

ユーザーランドという言葉はジャーゴンファイルによると「少なくとも 1985 年にはこの言葉が使われていた」と書かれています(参考)。このことから 1980 年代の初めの頃にはユーザーランドという言葉はなかった、もしくは広く使われていなかった可能性があります。適切な用語がなかった時代であれば、シェル上で動くコマンドを含めてシェルと呼んでいた人がいても不思議ではありません。

コマンドはシェルの拡張機能?

この「適切ではないシェルという用語」には二つの種類が考えられます。一つは UNIX の標準コマンドだけをシェルに含めてそれ以外の誰かが作った CLI コマンドは含めないという考え方です。これは 1980 年前半の考え方です。もう一つは CLI コマンドすべてをシェルに含めて GUI アプリは含めないという考え方です。GUI (X Window System) の登場が 1984 年なのを踏まえて 1980 年代後半の考え方です。

上記の図は呼び出し階層になっていないことに注意してください。シェルの部分が OS に近い部分で、それ以外は OS とは離れた部分という発想です。繰り返しますがこれらはそのような考え方があったのではないかという私の仮説なので注意してください。

ユーザーとはユーザーアプリケーションの意味ではないか?

いろいろな図を見ていると、同心円の図のシェルの外にあるアプリケーション層に「ユーザー」を加えている図を見かけます。「アプリケーション・ユーザー」みたいな感じです。

「アプリケーションがシェルアウトでシェルを使う」「ユーザーがシェルを使う」という意味だとすれば理解できなくもないのですが、ソフトウェアと人間を同じ層に書くというのには違和感を覚えます。そこでふと気づいたのは、もしかしたら元々は「アプリケーション・ユーザー」ではなく「ユーザーアプリケーション」の意味だったのではないか?という仮説です。つまりこういうことです。

ユーザーアプリケーションがユーザーとアプリケーションに分離した?

「アプリケーション」を「ユーザーアプリケーション」に変えてしまうのもおかしいわけですが、シェルとカーネルだけが OS であるという発想の延長であれば、そのような考え方になるのもありえます。海外の図で「Application (User)」のような書き方をしている図がありましたが、もしこれが元々「User Application」と書いてあったとしたら「ユーザーアプリケーション」と「ユーザーとアプリケーション」の両方の意味が考えられるでしょう。このような勘違いから「ユーザー」が追加されてしまうと「UNIX を構成する三つの要素」は呼び出し階層のように見えてしまいます。

「翻訳」はインタプリタの誤訳ではないか?

「間違ったシェルとカーネルの概念」の話でシェルが「ユーザーの言葉をカーネルがわかるように『翻訳』する」という説明をよく見かけるという話をしました。概念としてわからなくもないのですが、翻訳するという言い方には少し違和感を感じました。そこで気づいたのがインタプリタ (interpreter) は通訳者と訳することができるということです。

シェルが持つ機能の一つはコマンドラインインタプリタです。この場合のインタプリタは通訳というよりも解釈といったほうが適切だと思いますが、無理やり日本語にすると「命令行通訳者」になるでしょう。また今もしばしば使われていますが、コンピュータ用語が一般的でなかった頃はコンパイラやインタプリタが行う作業を「機械語に『翻訳』する」という言い方がなされていました。

したがって元々はシェルの役割としてコマンドラインインタプリタと説明されていたものが、インタプリタの日本語訳として、通訳や翻訳と書き換えられ、シェルはユーザーの言葉をカーネルがわかるように翻訳するという言い方が広まったのではないかと考えられます。コマンドラインインタプリタは、その名の通りコマンドラインを解釈することしかしません。人間の言葉へのインタプリタ機能はないのです。

「詳解 UNIX プログラミング」掲載の図

こちらは「詳解 UNIX プログラミング」に掲載されている図です。余談ですがこの本はおすすめできる本ですが、少々値段が高すぎます。ただし Amazon で定期的に半額セールが行われるのでその時が狙い目です(高いときに買って後悔しないようにお知らせ)。

詳解UNIXプログラミング掲載の図

こちらもアプリケーションが一番外にありますが、シェルの部分が切り取られており、またライブラリルーティンがシェルと同じレイヤーにあるのが特徴です。ここからアプリケーションから直接システムコールを呼び出す、またはライブラリ経由でシステムコールを呼び出すことができるという図になっているのがわかります。アプリケーションからシェルを呼び出すというのはおそらくシェルアウトのことを意味しているのでしょう。

興味深いポイントの一つは、本文に「カーネルへのインターフェースはシステムコールである」と書かれてあることです。シェルとカーネルの概念図からはシェルがカーネルとのインターフェースであるかのように思えてしまいますが、技術的にはそうではないことが読み取れます。シェルはカーネルとのインターフェースではなく「UNIX システム環境へのインターフェース」なのです。

カーネルへのインタフェースは、「システムコール」(systemcall)と呼ばれるソフトウェアの層です(図1.1の影部分)。一般的な関数のライブラリ群はシステムコールインタフェースを用いて構築されますが、アプリケーションではどちらも自由に使えます。

やはりちゃんとした技術書だけあって納得できる内容になっています。一つだけ指摘するならばユーザーがシェルを使うパターン、そしてシェルからコマンドを呼び出す場合の話が抜けている所です。エンドユーザーの立場から UNIX を使う本ではなく、プログラマ向けの本だからこのようなものになったのだと考えています。ちなみに本文にはシェルはアプリケーションであると書かれています。

シェル(shell)は、他のアプリケーションを実行するためのインタフェースを提供する特別なアプリケーションです。

一つ面白いのは、広義の OS の説明が書いてあり、この定義は日本の情報処理技術者試験の「広義の OS」の定義とは異なっています。この本は翻訳本なので日本の広義の OS の定義を踏まえてはいないでしょう。技術的かつ現実的な定義だといえます。

広義には、オペレーティングシステムとは、カーネル、ならびに、コンピュータを有用にし個性を与える他のすべてのソフトウェア群からなります。他のすべてのソフトウェア群には、システムユーティリティ群、アプリケーション群、シェル群、一般的な関数のライブラリ群、などがあります。

この通り、OS は全てのソフトウェア群からなり、アプリケーションは OS の一部なのです。

私の感想ですが何か?

世の中にはシェルとカーネルの概念図としてさまざまなものが提示されています。その中にはシェルとアプリケーションの位置関係が逆になっているなど明らかに他の図と矛盾している図もあります。おそらくシェルとカーネルという名前の由来を説明しただけの図を元に、よく理解しないまま拡張していったからこうなったのでしょう。これらをそのまま放置していたら初学者は混乱するだけです。

シェルがカーネルの言葉をユーザーがわかるように翻訳しているなどという明らかに間違った説明はなくすべきですし、古く時代にあわなくなった図は改訂すべきです。しかしネットで誰でも情報発信でき、それがなかなか消えない時代では、すべての情報を更新するなんて不可能でしょう。次から次へと古くて間違った解説が新しく作られ続けています。こうやって新しい情報を追加するしかありません。

せめて大学や技術者認定機関は可能なら更新して欲しい所です。「いや、そういう意味で書いた図じゃないんだ」ということかもしれませんが、それならば勘違いされないようにその意味をちゃんと補足しましょう。どこからか引用したのであれば引用元を書きましょう。初学者にそんな説明をしてもわからないからと話をぼかす必要はありません。初学者は学んでいくうちに理解することができるからです。どういった意図で書いたかが明確になっていれば最初はその意味が理解できなくても、それが正確ではないことぐらいはわかるので混乱を招かずにすみます。

この記事によってシェルやカーネルや OS やシェルスクリプトに関する勘違いが少しでも減らせれば幸いです。

502
603
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
502
603

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?