shebangから理解するUNIXの仕組み
unixでシェルスクリプトを書くとき、冒頭に#!/bin/sh
と書くことは当たり前なのだけど、
時々コマンドなんだっけ、と忘れることがあった。
その当たり前をマジックとして理解せずに置いておいていけはいけないなと思い、
仕組みについて調べて理解してみた。
Fork-Exec
まずはFork-Execについて知っている必要がある。
fork -wikipediaによると以下の通り。
Fork-Execは、UNIXで一般的に使われる手法であり、新たなプログラムをプロセスとして実行する。
fork()は親プロセスを2つの同一内容のプロセスに(フォークの先のように)分岐させるシステムコールである。
fork()によって子プロセスが親プロセスのコピーとして生成され、子プロセスがexec()システムコールを呼び出すことで(子プロセス)自身の内容を置き換える。
要は、UNIXでコマンドを実行されると実行したいコマンドはforkにより 子プロセスが生成され、その子プロセスにてexec()にて実行される。
exec()
exec() -wikipediaによると execveをシステムコールとするとのこと。
現代のたいていのUnixにおける実装では、最も汎用的なexecveをシステムコールとし(すなわちexecve(2))、
他はそれを呼ぶライブラリ関数(たとえばexec(3))としている。
なので、execve()を調べてみる。
evecve()
EXECVEによると、 指定されたプログラムはバイナリ形式、または以下の形式の行で始まるスクリプトでなければならないとある。
execve() は、filename によって指定されたプログラムを実行する。 filename は、バイナリ実行形式か、 以下の形式の行で始まるスクリプトでなければならない。
#! interpreter [optional-arg]
つまり、execve()にてファイルを実行するための形式として、ファイルの先頭にシェバンを指定しなければならないことがわかった。
まとめ
シェバンと呼ばれ、スクリプトの最初で指定している#!/bin/sh
は
- UNIXでコマンドが実行される仕組みはFork-Execであり
- Fork-Execではコマンドはexec()で実行される
- exec()はシステムコールでexecve()を呼び出す
- execve()で実行できるfileはバイナリ形式またはファイルの先頭に
#! interpreter [optional-arg]
を指定しなければならない
ということが理解できた。仕組み理解するの大事。