🐚

シェルってなにしてるの

2023/08/22に公開4

はじめに

先日シェルについてのオンライン勉強会に参加しました。
https://studyco.connpass.com/event/291129/
スライドも非常にわかりやすく、動画もアップされているので、ぜひ見てみて下さい。

何度勉強してもついつい忘れてしますシェルについて、忘れないうちにアウトプットしておきたいと思います。
私の解釈が混ざっているので、詳しく知りたい方は上のURLへどうぞ

前提

記事内で実行しているコマンドは全てawsのEC2(Amazon Linux2023)のbashで実行したものです。

シェルってなあに

みなさん「シェル」使ってますか?
使ってますよね?

sh, bash, zsh, csh,etc...
いろんなシェルがありますね。

この「シェル」ってなんでしょうか?
シェルはユーザーが命令したことをカーネルに伝えてくれるやつ。です。

でも面倒じゃないですか?
なぜユーザーの命令がそのままカーネルではなく、わざわざシェルを通す必要があるのでしょうか?
それはカーネルがユーザーが入力した文字を理解できないからです。

ということで、「カーネル」を理解しましょう。

カーネルってなあに

カーネルはOSのコア(核)です。
これではあまりピンとこないと思います。

ではカーネルは何をやっているのでしょう?
主にプロセスやメモリ、ファイルシステムなどの管理をしています。

プロセスやメモリなど、ソフト側もハード側も管理していますね。
つまり、カーネルはソフトウェアとハードウェアの間の管理を行ってくれているということです。

これはどういったところで使われているのでしょうか?

カーネルの仕事

例えば、今みなさんが開いているブラウザです。
複数のタブを開いている方も多いのではないでしょうか?
1コアのCPUの場合、1つのプロセス(タブ)しか動かすことができません。

しかし、実際にタブを移動すると別のタブが見れますね?
これはタブが移動した際にカーネルが複数のプロセスを高速で切り替えることで、あたかも複数のプロセスが同時に動作しているように見せかけています。

カーネルは頑張り屋さんですね〜

ちなみに、今回は1コアのCPUで例えましたが、CPUのコア数が増えると、それだけ並行して処理を行うことができます。

なんとなくカーネルがやっていることがイメージできたましたか?

非常に重要なカーネルですが、カーネルはユーザーの言語を理解することができません。
これは、役割分担やセキュリティ面による理由からです。

そのため、我々ユーザーの命令をカーネルに伝えるのがシェルという訳です。

カーネルとシェルが起動するまで

では、このカーネルやシェルはどこで動いているのでしょうか?
これらはメモリ上で動作しています。

どのようにしてPCの起動からカーネル、そしてシェルが動作するのか見てみましょう。
まずPCを起動すると、最初にCPUがBIOSを起動します。

BIOSは簡単にいうと、ハードウェアのチェックや初期化を行なっています。
次にBIOSがストレージにあるブートローダを実行します。

ブートローダはカーネルをメモリに読み込ませます。

これでカーネルがメモリ上で動けるようになります。

そしてカーネルがinitプロセスを起動します。
initは「初期化」です。
なので、initプロセスは初期状態のプロセス、つまり一番大元の親プロセスということになります。

このinitプロセスを親として、シェルや他の各プログラムが子プロセスとして生まれていきます。
これでカーネルやシェルがどのように起動しているのかが分かりました。

シェルの仕事

ここまででシェルとカーネルの関係性が見えてきました。
ではここからはシェルがどのようにユーザーの入力したプログラムを実行するのか見ていきます。

そもそもシェルがなければユーザーの入力したコマンドは実行されません。
それどころか、シェルがなければコンピュータにログインすることもできません。

つまり、シェルはユーザーとカーネルを繋ぐインタフェースということです。

コマンド

私たちは普段シェルに対して命令としてコマンドを入力すると思います。
lsとかcatとか...

このコマンドには2つの種類があります。

  • 内部コマンド
  • 外部コマンド

それぞれ違いを見ていきます。

内部コマンド

よく使うものでいえばcd,echo,pwdなどがあります。
これらはシェル自体のプログラムの中に組み込まれています。

そのため、cdコマンドを実行すると、シェルが自分自身のプログラムを実行します。

外部コマンド

先ほどの内部コマンドはシェル内部のコマンドでした。
ということは外部コマンドはシェルの外側のコマンドということです。

代表的な外部コマンドはls, cat, cp, mv...などがあります。

外部コマンドではlsという命令に対してlsの処理が書かれたプログラムファイルを実行しているのです。
よく意味が分かりませんね。

順番に見ていきましょう。
ファイルの一覧を表示するlsコマンドを実行します。

しかし、lsという文字列からシェルが勝手にファイルの一覧を表示してくれている訳ではありません。
これはシェル内部にlsというプログラムが存在しないからです。

ではどうするのか?
シェルはファイル一覧を表示するというプログラムが書かれたファイルを実行する必要があります。
つまり、lsを実行するとシェルはlsの処理が書かれた外部のプログラムファイルを探しにいきます。

そして、見つけたlsの処理が書かれたファイルを実行します。

この時シェルが探しに行く場所のことを「PATH」といいます。
PATHというとecho $PATHと入力すると出力される謎の文字列のことです。

$ echo $PATH
/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin

これは謎の文字列ではなく、各コマンドの実行内容がプログラムされたファイルの場所を表すものだったのです。

ということは、このPATHの中に自分が実行したいコマンドのプログラムが存在しないとコマンドが実行できないということです。
これがよく言う「PATHが通ってない」というやつです。

外部コマンドの実行

各コマンドは一つのプログラムという話をしました。
そして、プログラムを実行するにはプロセスとして実行する必要があるとも言いました。

ということは、コマンドを実行するには新たにプロセスを実行する必要があります。
しかし、シェルではプロセスを実行できません。

そこでシェルはカーネルに対して、新しいプロセス実行して!とお願いします。
これを「システムコール」と呼びます。

呼び出されたカーネルはプロセスの実行や、メモリの割り当て、必要なファイル情報を呼び出すためにファイルシステムへのアクセスなどを行い、その結果をシェルへ返します。

こうして、lsコマンドでファイルの一覧を取得することができます。

内部コマンドと外部コマンドの見分け方

最後に内部と外部のコマンドの見分け方を紹介しておきます。
各コマンドが内部コマンドか外部コマンドかは、type <コマンド>で簡単に調べることができます。

$ type cat
cat is /usr/bin/cat

$ type echo
echo is a shell builtin

catではパスが出力されたのに対して、echoではshell builtinと返ってきました。

catはパスが表示されたので外部コマンドです。
echoはshell builtinつまり、シェルに組み込まれた内部コマンドということです。

まとめ

簡単にまとめると、シェルとカーネルはOSの一部で、
シェルはユーザーとカーネルの間を取り持ってよしなにしてくれるやつ。
カーネルはソフト(シェルなど)とハードウェアの間を取り持ってよしなにしてくれるやつ。
シェルのコマンドがどうやって実行されているかを知ると、コマンド関連のエラーの時に早く解決できるかもね!
ということです。

かなり長々と書いてしまいました。
普段カーネルなんて意識しながら生きていないので、ああ、そういえば本にそんなことあ書いてあったな〜と思い出しながら勉強することができました。

私の記事ではあまり深くは触れていませんが、勉強会の動画ではもう少し深いところまでお話しされているので、興味がある方はぜひ!

参考資料

https://studyco.connpass.com/event/291129/

GitHubで編集を提案

Discussion

oshima_123oshima_123

勉強会で発表していた大嶋と申します!
丁寧な記事を書いていただけて、とても嬉しいです!
イラストも分かりやすく、読みやすかったです!

勉強会の中で私の説明で伝えきれなかった点もあるかと思うので、少しだけ補足させていただきます!

なぜユーザーの命令がそのままカーネルではなく、わざわざシェルを通す必要があるのでしょうか?
それはカーネルがユーザーが入力した文字を理解できないからです。

とのことですが、どちらかというと、「カーネル自体にはインタラクティブに人間が操作する機能がないから」ということになります!
シェルはなくてもいいんですが、もしもシェルがないと、そのコンピュータに対して人間が操作する方法がなくなります。
「コンピュータの操作方法を提供するプログラムがシェル」ということになります!
(実際にはシェル以外にも色々なプログラムが協力して、人間が操作できるようにしてくれています)

そして、たしかに「カーネルがユーザーが入力した文字を理解できない」「シェルがユーザの入力を翻訳する」といった説明を見かけることはあるのですが、こういった説明はあまり正確ではなかったりします。
例えばシェルに /usr/bin/cat hello.txt というコマンドを入力したとき、シェルはカーネルこの文字列をほぼそのまま渡すような動きになるので、「カーネルがユーザーが入力した文字を理解できない」「シェルがユーザの入力を翻訳する」というのはちょっとあやしい表現だったりします!
(ただ、コマンドによってはシェルが一度解釈した結果をカーネルに渡すというのも事実なので、「カーネルがユーザーが入力した文字を理解できない」「シェルがユーザの入力を翻訳する」といった表現は完全に間違いと言い切れない部分もあります)

また、

これはタブが移動した際にカーネルが複数のプロセスを高速で切り替えることで、あたかも複数のプロセスが同時に動作しているように見せかけています。

とのことですが、ブラウザのタブを表示していることは、そのプロセスを実行していることとはちょっと別の話だったりします
というのも、ブラウザのタブを表示中だからといってそのプロセスを実行中とは限らず、表示していないブラウザのタブや表示していないアプリケーションのプロセスが実行中の可能性もあります

プロセスの切り替わりのタイミングも、ユーザが操作したら、というよりも、すごく短い時間で勝手に切り替わって交互に少しずつ動く、ということになります。
(ユーザーが何か入力したタイミングでもプロセスが切り替わったりはしますが、そのあとすぐまた別のプロセスが動きます)

なので、「タブの切り替えでプロセスが切り替わる」よりも、「Excelで計算中に、他のアプリも動かせる」などのほうが適切な例になります!

少しだけ気になった点を書かせていただきましたが、全体的に丁寧でとても分かりやすい記事でした!
ありがとうございます!

よなよな

先日は勉強会を開催して頂きありがとうございました!
とても勉強になりました!
記事への補足もありがとうございます、まだまだ勉強不足なのでレビューを頂けると本当に助かります!

シェルの役割について少し誤解していました。
シェルは、カーネルが機能として持っていない「インタラクティブな操作」を提供しているんですね!

シェルに /usr/bin/cat hello.txt というコマンドを入力したとき、シェルはカーネルこの文字列をほぼそのまま渡すような動きになる

そうなんですね!ということは、この場合はカーネルが/usr/bin/catのプログラムを呼び出している。という認識になるのでしょうか...

プロセスの切り替わりのタイミングも、ユーザが操作したら、というよりも、すごく短い時間で勝手に切り替わって交互に少しずつ動く、ということになります。

プロセスは勝手に切り替わるんですね、ユーザーの入力がトリガーになってるものだと思い込んでました!
Excelの例えがとても分かりやすいです、ありがとうございます!

機を見て記事の方も訂正させてもらいます!
ありがとうございます!

oshima_123oshima_123

そうなんですね!ということは、この場合はカーネルが/usr/bin/catのプログラムを呼び出している。という認識になるのでしょうか...

はい!そういうことになります!
シェルはプログラムのファイルを実行することはできないので、プログラムのファイルの実行はカーネルに依頼します。

また、勉強会での話はすべて「Linuxの場合」なので、他のOSだとまたちょっと違うケースもありえたりします!

補足の内容が参考になったようで良かったです!

よなよな

シェルはプログラムのファイルを実行することはできないので、プログラムのファイルの実行はカーネルに依頼します。

ありがとうございます、勉強になります!

他のOSだとまた違うんですね、ややこしい...

ありがとうございます🙇