web-dev-qa-db-ja.com

リダイレクト/パイピングを使用する場合、bashは実際にstdin / stdout / stderrをどのように変更しますか

残念ながら、私が見つけたものはすべてリダイレクトの構文、またはリダイレクトがどのように機能するかについての浅い情報に基づいているため、これを理解することができませんでした。

私が知りたいのは、パイプまたはリダイレクトを使用したときに、bashが実際にstdin/stdout/stderrをどのように変更するかです。たとえば、次のように実行します。

_ls -la > diroutput.log
_

stdoutlsを_diroutput.log_にどのように変更しますか?

私はそれがこのように機能すると思います:

  • Bashはfork(2)を実行して、それ自体のコピーを作成します
  • フォークされたbashプロセスは、freopen(3)のようなものを使用して、それをstdoutから_diroutput.log_に設定します。
  • フォークされたbashプロセスは、execve(2)または同様のexec関数を実行して、それ自体をlsに置き換えます。これにより、bashによるstdoutセットアップが使用されます。

しかし、それは私の知識に基づく推測です。

3

_strace -f_を使用して、Cで小さな概念実証を書くことでそれを理解することができました。

私が思ったように、bashはexecveを呼び出す前に、子プロセスでファイル記述子を操作するだけのようです。

_ls -la > diroutput.log_の仕組みは次のとおりです(大まかに):

  • bashはfork(2)を呼び出します
  • フォークされたbashプロセスは、出力リダイレクトを確認し、open(2)を使用してファイル_diroutput.log_を開きます。
  • フォークされたbashプロセスは、dup2(2)syscallを使用してstdoutファイル記述子を置き換えます
  • bashはexecve(2)を呼び出して、実行可能イメージをlsに置き換えます。これにより、すでに設定されているstdoutが継承されます。

関連するシステムコールは次のようになります(strace output):

_6924  open("diroutput.log", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
6924  dup2(3, 1)                        = 1 
6924  close(3)                          = 0 
6924  execve("/bin/ls", ["ls", "-la"], [/* 77 vars */]) = 0
_
5