私はLinuxがどのように動作するかについての精神的なモデルをゆっくりと作り、Linuxがどのように動作するかについてのモデルをできるだけ単純化しようとしています。カーネルが起動して実行する唯一のソフトウェアである端末を初期化するとします。端末が画面に表示され、いくつかのテキストをレンダリングでき、もちろんキーボードから入力を受け取ることができると仮定します。また、実行可能ファイルの名前を入力し、実行可能ファイルがメモリ内のどこにあるかを知っているとします。今ターミナルはこのプログラムをどのように実行しますか?私の精神モデルでは、私は次のことを思いついた。
端末はプログラムなので、システムコールを行うことができます。そのため、fork()システムコールを使用してカーネルに新しいプロセスを作成します。その後、どういうわけかそのプロセスに私のプログラムコードを実行させます。これで、プログラムの実行中にどのようにprintf()が端末にリアルタイムでテキストを表示できますか?
答え1
あなたの理解は非常に正確です。シェルはclone()
システムコールを使用して新しいプロセスを作成します。マンページでは、これが以下とどのように異なるかを説明しますfork()
。
fork(2)とは異なり、clone()を使用すると、子プロセスは呼び出しプロセスとメモリスペース、ファイル記述子テーブル、およびシグナルハンドラテーブルなどの実行コンテキストの一部を共有できます。 (このマニュアルページでは、「呼び出しプロセス」は通常「親プロセス」に対応します。
次に、execve()
システムコールを使用して、現在のサブプロセスイメージを新しいプロセスイメージに置き換えます。このシステムコールは、プロセスがプログラムのコードを実行するようにします。
プロセスが分岐すると、親プロセスのファイル記述子がコピーされます。fork(2)
マニュアルページから:
子プロセスは、親プロセスのオープンファイル記述子セットのコピーを継承します。
子の各ファイル記述子は、親の対応するファイル記述子と同じオープンファイル記述(open(2)を参照)を参照します。これは、2つの記述子が開かれたファイル状態フラグ、現在のファイルオフセット、および信号駆動I / O属性を共有することを意味します(fcntl(2)のF_SETOWNおよびF_SETSIGの説明を参照)。
これが、プログラムが標準出力に書き込むときに端末にテキストが表示される理由です。strace
Linuxのプログラムを使用して、このプロセスが進行していることを確認できます。以下は、strace
Linuxのbashプロセスで実行され、/bin/echo foo
シェルで実行される主な抜粋です。
21:32:20 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3f419f19d0) = 32036
Process 32036 attached
[pid 32017] 21:32:20 wait4(-1, <unfinished ...>
[pid 32036] 21:32:20 execve("/bin/echo", ["/bin/echo", "foo"], ["XDG_VTNR=8", "KDE_MULTIHEAD=false", "XDG_SESSION_ID=5512", "SSH_AGENT_PID=30259", "DM_CONTROL=/var/run/xdmctl", "TERM=xterm", "SHELL=/bin/bash", "XDM_MANAGED=method=classic", "XDG_SESSION_COOKIE=5c78dafb330601d94d7556bb52a6a2a6-1450467466.154128-547622992", "HISTSIZE=50000", "KONSOLE_DBUS_SERVICE=:1.160", "GTK2_RC_FILES=/etc/gtk-2.0/gtkrc:/home/jordan/.gtkrc-2.0:/home/jordan/.kde/share/config/gtkrc-2.0", "KONSOLE_PROFILE_NAME=Shell", "GTK_RC_FILES=/etc/gtk/gtkrc:/home/jordan/.gtkrc:/home/jordan/.kde/share/config/gtkrc", "GS_LIB=/home/jordan/.fonts", "WINDOWID=92274714", "SHELL_SESSION_ID=5b72a0038b0c4000a9299cae82f340a2", "KDE_FULL_SESSION=true", "USER=jordan", "SSH_AUTH_SOCK=/tmp/ssh-JEjo6RVmNhvR/agent.30205", "SESSION_MANAGER=local/tesla:@/tmp/.ICE-unix/30329,unix/tesla:/tmp/.ICE-unix/30329", "PATH=/home/jordan/.gem/ruby/1.9.1/bin:/home/jordan/.gem/ruby/1.9.1/bin:/home/jordan/bin:/home/jordan/local/packer:/home/jordan/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/local/sbin:/usr/sbin:/home/jordan/.rvm/bin:/home/jordan/prog/go/bin:/home/jordan/.rvm/bin:/home/jordan/prog/go/bin", "DESKTOP_SESSION=kde-plasma", "PWD=/home/jordan/games", "WORKING=/home/jordan/prog/greenspan", "KONSOLE_DBUS_WINDOW=/Windows/1", "EDITOR=emacs -nw", "LANG=en_US.UTF-8", "KDE_SESSION_UID=1000", "PS1=\\[\\033[01;32m\\]\\u@\\h\\[\\033[01;34m\\] \\w\\[\\033[1;31m\\]$(__git_ps1)\\[\\033[01;34m\\] \\$\\[\\033[00m\\] ", "KONSOLE_DBUS_SESSION=/Sessions/1", "SHLVL=2", "XDG_SEAT=seat0", "COLORFGBG=15;0", "HOME=/home/jordan", "LANGUAGE=", "KDE_SESSION_VERSION=4", "GOROOT=/home/jordan/local/go", "XCURSOR_THEME=oxy-zion", "LOGNAME=jordan", "DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-onouV6Cc66,guid=bcdceeabe7aa00a28d55899f5674608a", "XDG_DATA_DIRS=/usr/share:/usr/share:/usr/local/share", "GOPATH=/home/jordan/prog/go", "PROMPT_COMMAND=history -a", "WINDOWPATH=8", "DISPLAY=:0", "XDG_RUNTIME_DIR=/run/user/1000", "PROFILEHOME=", "QT_PLUGIN_PATH=/home/jordan/.kde/lib/kde4/plugins/:/usr/lib/kde4/plugins/", "XDG_CURRENT_DESKTOP=KDE", "HISTTIMEFORMAT=%F %T: ", "_=/bin/echo"]) = 0
[pid 32036] 21:32:20 write(1, "foo\n", 4) = 4
[pid 32036] 21:32:20 exit_group(0) = ?
[pid 32036] 21:32:20 +++ exited with 0 +++
21:32:20 <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WSTOPPED|WCONTINUED, NULL) = 32036