シェルはプロセスを直接実行しない
カーネル以外のプロセスは、新しくプロセスを実行できない。そのため、シェルでプロセスを実行するためには、シェルからカーネルにプロセスの実行を依頼しなければならない。
カーネルへの依頼にはシステムコールを使う
カーネルになにかの処理を依頼するためには、システムコールを使う。システムコールは、ユーザプログラムがカーネルに処理を依頼するためのインターフェースである。
シェルからカーネルへのプロセスの実行依頼
シェルからカーネルへのプロセスの実行依頼は、fork
システムコールとexecve
システムコールを使って行う。なお、プログラミング言語からは、これらのシステムコールに対応するライブラリの関数を呼び出すことになる。
fork
システムコールを呼び出すと、カーネルは呼び出し元のプロセスのコピーを作成し、新しいプロセスを生成する。この時点で生成されるプロセスは呼び出し元のコピーであるため、呼び出し元のプロセスと同じプログラムを実行する。
pid = os.fork()
fork
で生成したプロセスに新しいプログラムを実行させるには、execve
システムコールを呼び出す。execve
システムコールでは、プログラムのパス名と引数を指定する。これを呼び出すと、プロセスのプログラムが指定したプログラムに置き換わる。
if pid == 0: # Child process
os.execve('/bin/ls', ['/bin/ls', '-l'], os.environ)
プロセスの入出力のリダイレクト
fork
で複製された処理の標準入力・標準出力・標準エラー出力は、ファイルディスクリプタとして親プロセスから子プロセスへコピーされる。これにより、親プロセスと子プロセスは独立して処理できる。
子プロセスのファイルディスクリプタをリダイレクト先に変更することで、標準入力・標準出力・標準エラー出力をリダイレクトできる。
ファイルディスクリプタの変更は、dup2
システムコールを使って行う。dup2
システムコールは、指定したファイルディスクリプタを、指定したファイルディスクリプタ(の番号)へコピーする。
# 標準入力をリダイレクト
# stdin_fd: リダイレクト先のファイルディスクリプタ
# sys.stdin.fileno(): 標準入力のファイルディスクリプタ
os.dup2(stdin_fd, sys.stdin.fileno())
プロセスのパイプ
あるプロセスの出力を別のプロセスの入力につなぐことをパイプという。
パイプは、pipe
システムコールを使って作成する。pipe
システムコールを使うと、読み込み用と書き込み用の2つのファイルディスクリプタが生成される。
pipe_read, pipe_write = os.pipe()
これらのファイルディスクリプタをパイプ前の出力とパイプ後の入力にリダイレクトすることで、パイプを実現できる。