syscallは、ユーザープログラムがLinuxカーネルと対話するメカニズムであり、straceはそれらを追跡するための強力なツールです。オペレーティングシステムがどのように機能するかをよりよく理解するには、それらがどのように機能するかを理解することが役立ちます。
オペレーティングシステムは、次の2つの動作モードに分けることができます。
- カーネルモードは、オペレーティングシステムカーネルによって使用される特権モードです。
- ユーザーモードは、ほとんどのユーザーアプリケーションが実行されるモードです。
ユーザーは通常、日常業務にコマンドラインユーティリティとグラフィカルインターフェイス(GUI)を使用します。同時に、システム呼び出しはバックグラウンドで目に見えない形で機能し、カーネルにアクセスして作業を行います。
システム呼び出しは、引数と戻り値が渡されるという意味で関数呼び出しと非常によく似ています。唯一の違いは、システム呼び出しはカーネルレベルで機能しますが、関数は機能しないことです。ユーザーモードからカーネルモードへの切り替えは、特別な割り込みメカニズムを使用して行われます。
これらの詳細のほとんどは、システムライブラリ(Linuxシステムではglibc)でユーザーに表示されません。システム呼び出しは本質的に普遍的ですが、それにもかかわらず、それらの実行のメカニズムは主にハードウェアに依存しています。
この記事では、を使用してシステム呼び出しを解析するいくつかの実用的な例について説明します
strace。例ではRedHat Enterprise Linuxを使用していますが、すべてのコマンドは他のLinuxディストリビューションで機能するはずです。
[root@sandbox ~]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.7 (Maipo)
[root@sandbox ~]#
[root@sandbox ~]# uname -r
3.10.0-1062.el7.x86_64
[root@sandbox ~]#
まず、システムに必要なツールがインストールされていることを確認します。
strace以下のコマンドでインストールされているか確認できます。バージョンを表示straceするには、-Vパラメーターを指定して実行します。
[root@sandbox ~]# rpm -qa | grep -i strace
strace-4.12-9.el7.x86_64
[root@sandbox ~]#
[root@sandbox ~]# strace -V
strace -- version 4.12
[root@sandbox ~]#
straceインストールされていない
場合は、以下を実行してインストールします。
yum install strace
たとえば
/tmp、次のコマンドを使用して、と2つのファイルにテストディレクトリを作成しますtouch。
[root@sandbox ~]# cd /tmp/
[root@sandbox tmp]#
[root@sandbox tmp]# mkdir testdir
[root@sandbox tmp]#
[root@sandbox tmp]# touch testdir/file1
[root@sandbox tmp]# touch testdir/file2
[root@sandbox tmp]#
(
/tmp誰もがアクセスできるため、ディレクトリのみを使用しますが、他の任意のディレクトリを使用できます。)
コマンドを使用して、ファイルが
lsディレクトリにtestdir作成されていることを確認します。
[root@sandbox tmp]# ls testdir/
file1 file2
[root@sandbox tmp]#
lsシステム呼び出しが内部で実行されていることに気付かずに
、おそらく毎日コマンドを使用します。ここで抽象化が役立ちます。このコマンドの仕組みは次のとおりです。
-> (glibc) ->
このコマンド
lsは、Linuxシステムライブラリ(glibc)から関数を呼び出します。これらのライブラリは、システム呼び出しを呼び出し、ほとんどの作業を実行します。
glibcライブラリから呼び出された関数を知りたい場合は、次のコマンドを使用してから次
ltraceのコマンドを実行しls testdir/ます。
ltrace ls testdir/
ltraceインストールされていない
場合は、以下をインストールします。
yum install ltrace
画面には多くの情報が表示されますが、心配しないでください。これについては後で説明します。出力からの重要なライブラリ関数のいくつかを次に示します
ltrace。
opendir("testdir/") = { 3 }
readdir({ 3 }) = { 101879119, "." }
readdir({ 3 }) = { 134, ".." }
readdir({ 3 }) = { 101879120, "file1" }
strlen("file1") = 5
memcpy(0x1665be0, "file1\0", 6) = 0x1665be0
readdir({ 3 }) = { 101879122, "file2" }
strlen("file2") = 5
memcpy(0x166dcb0, "file2\0", 6) = 0x166dcb0
readdir({ 3 }) = nil
closedir({ 3 })
この出力を調べることで、おそらく何が起こっているのかを理解できます。指定されたディレクトリは
testdir、ライブラリ関数を使用して開かopendirれ、その後readdir、ディレクトリの内容を読み取る関数が呼び出されます。最後に、closedir以前に開いたディレクトリを閉じる関数が呼び出されます。今のところ、strlenやなどの他の関数は無視してくださいmemcpy。
ご覧のとおり、呼び出されているライブラリ関数は簡単にわかりますが、この記事では、システムライブラリ関数によって呼び出されるシステム呼び出しに焦点を当てます。
システム呼び出しを表示するに
straceは、ls testdir以下に示すコマンドで使用します。また、一貫性のない情報がたくさん表示されます。
[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
brk(NULL) = 0x1f12000
<<< truncated strace output >>>
write(1, "file1 file2\n", 13file1 file2
) = 13
close(1) = 0
munmap(0x7fd002c8d000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
[root@sandbox tmp]#
実行の結果
strace、コマンドの実行中に実行されたシステム呼び出しのリストを受け取りますls。すべてのシステム呼び出しは、次のカテゴリに分類できます。
- プロセス管理
- ファイル管理
- ディレクトリとファイルシステムの管理
- その他
受信した情報を分析する便利な方法があります-オプションを使用して出力をファイルに書き込みます
-o。
[root@sandbox tmp]# strace -o trace.log ls testdir/
file1 file2
[root@sandbox tmp]#
今回は、画面にデータが表示されません。コマンドは
ls期待どおりに機能straceし、ファイルのリストを表示して、すべての出力をファイルに書き込みますtrace.log。単純なコマンドの場合、lsファイルにはほぼ100行が含まれています。
[root@sandbox tmp]# ls -l trace.log
-rw-r--r--. 1 root root 7809 Oct 12 13:52 trace.log
[root@sandbox tmp]#
[root@sandbox tmp]# wc -l trace.log
114 trace.log
[root@sandbox tmp]#
ファイルの最初の行を見てください
trace.log:
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
- 行の先頭には、実行されているシステム呼び出しの名前—execveがあります。
- 括弧内のテキストは、システム呼び出しに渡される引数です。
- =記号の後の数字(この場合は0)は、システム呼び出しによって返される値です。
さて、結果はそれほど怖くないようですよね?また、他の行にも同じロジックを適用できます。
呼び出した唯一のコマンドに注意してください-
ls testdir。コマンドlsで使用されるディレクトリの名前を知っているので、ファイルでgrepfortestdirを使用して、trace.log何が見つかるかを確認してみませんか?結果をよく見てください。
[root@sandbox tmp]# grep testdir trace.log
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[root@sandbox tmp]#
上記の分析に戻って、
execve次のシステム呼び出しが何をしているのかわかりますか?
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
すべてのシステム呼び出しとその機能を覚えておく必要はありません。すべてがドキュメントに記載されています。マンページは急いで助けてくれます!manコマンドを実行する前に、パッケージがインストールされていることを確認してください
man-pages。
[root@sandbox tmp]# rpm -qa | grep -i man-pages
man-pages-3.53-5.el7.noarch
[root@sandbox tmp]#
コマンド
manとsyscall名の間に「2」を追加する必要があることに注意してください。()manについて読むと、セクション2がシステム呼び出し用に予約されていることがわかります。同様に、ライブラリ関数に関する情報が必要な場合は、とライブラリ関数の名前の間に3を追加する必要があります。
以下はセクション番号です:manman manman
man
1. .
2. (, ).
3. ( ).
4. ( /dev).
システム呼び出しのドキュメントを表示するには、そのシステム呼び出しの名前でmanを実行します。
man 2 execve
ドキュメントによると、システム呼び出し
execveは、パラメーターで渡されるプログラムを実行します(この場合はls)。lsの追加パラメーターも渡されます。この例では、ですtestdir。したがって、このシステム呼び出しは、パラメータとしてで実行lsさtestdirれます。
'execve - execute program'
'DESCRIPTION
execve() executes the program pointed to by filename'
次のシステム呼び出しに
statはパラメータが渡されますtestdir:
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
ドキュメントを表示するには、を使用します
man 2 stat。statシステム呼び出しは、指定されたファイルに関する情報を返します。Linuxのすべては、ディレクトリを含むファイルであることを忘れないでください。
次に、システム呼び出し
openatが開きますtestdir。戻り値は3であることに注意してください。これは、後続のシステム呼び出しで使用されるファイル記述子です。
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
ファイルを開きます
trace.logシステム呼び出しに続く行に注意してくださいopenat。getdentsコマンドの実行に必要なほとんどの作業を実行するシステム呼び出しが表示されls testdirます。それではgrep getdents、ファイルに対して実行してみましょうtrace.log。
[root@sandbox tmp]# grep getdents trace.log
getdents(3, /* 4 entries */, 32768) = 112
getdents(3, /* 0 entries */, 32768) = 0
[root@sandbox tmp]#
ドキュメント(
man getdents)には、getdents実際に必要なディレクトリエントリを読み取ると記載されています。getdent3の引数は、システム呼び出しから以前に取得したファイル記述子であることに注意してくださいopenat。
ディレクトリの内容を受信したので、端末に情報を表示する方法が必要です。したがって、端末に出力するために使用される
grep別のシステム呼び出しwriteに対して行います。
[root@sandbox tmp]# grep write trace.log
write(1, "file1 file2\n", 13) = 13
[root@sandbox tmp]#
引数には、出力されるファイルの名前が表示されます:
file1およびfile2。最初の引数(1)については、Linuxでは、すべてのプロセスに対してデフォルトで3つのファイル記述子が開かれることに注意してください。
- 0-標準入力ストリーム
- 1-標準出力ストリーム
- 2-標準エラーストリーム
このように、システムコールが
writeかかるfile1とfile2、端末で標準出力には、数1を表し、
今、あなたはシステムコールがチームのためにほとんどの作業を行って何を知っています
ls testdir/。しかし、ファイル内の他の100以上のシステム呼び出しはtrace.logどうですか?
オペレーティングシステムは、プロセスを開始するために多くのサポートを行います。そのため、ファイルに表示される内容の多くは
trace.log、プロセスの初期化とクリーンアップです。 trace.logファイルをよく見て、コマンドの実行時に何が起こるかを理解してくださいls。
これで、任意のプログラムのシステム呼び出しを分析できます。 straceユーティリティには、多くの便利なコマンドラインオプションも用意されています。その一部を以下に説明します。
デフォルトでは
strace、システム呼び出しに関するすべての情報が表示されるわけではありません。ただし、-v verbose各システム呼び出しに関する追加情報を表示するオプションがあります。
strace -v ls testdir
パラメータを使用
-fして、実行中のプロセスによって作成された子プロセスを追跡することをお勧めします。
strace -f ls testdir
しかし、システム呼び出しの名前、それらが実行された回数、および実行に費やされた時間の割合だけが必要な場合はどうでしょうか。このオプション
-cを使用して、次の統計を取得できます。
strace -c ls testdir/
たとえば、特定のシステムコールを追跡し、
open他のコールを無視する場合-eは、システムコールの名前とともにオプションを使用できます。
[root@sandbox tmp]# strace -e open ls testdir
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1 file2
+++ exited with 0 +++
[root@sandbox tmp]#
複数のシステム呼び出しでフィルタリングする必要がある場合はどうなりますか?心配しないでください。同じオプション
-eを使用して、必要なシステム呼び出しをコンマで区切ることができます。たとえば、forwriteとgetdent:
[root@sandbox tmp]# strace -e write,getdents ls testdir
getdents(3, /* 4 entries */, 32768) = 112
getdents(3, /* 0 entries */, 32768) = 0
write(1, "file1 file2\n", 13file1 file2
) = 13
+++ exited with 0 +++
[root@sandbox tmp]#
これまでのところ、明示的なコマンドの実行のみを追跡しました。しかし、以前に実行されたコマンドはどうですか?悪魔を追跡したい場合はどうなりますか?このために、プロセスIDを渡すことができる
strace特別なオプション-pがあります。
デーモンは起動しません
catが、渡されたファイルの内容を引数として表示するコマンドを使用します。ただし、引数を指定しない場合、コマンドcatはユーザーからの入力を待つだけです。テキストを入力すると、入力したテキストが画面に表示されます。ユーザーがクリックCtrl+Cして終了するまで、以下同様です。1つの端末で
コマンド
catを実行します。
[root@sandbox tmp]# cat
別の端末で、次のコマンドを使用してプロセスID(PID)を見つけます
ps。
[root@sandbox ~]# ps -ef | grep cat
root 22443 20164 0 14:19 pts/0 00:00:00 cat
root 22482 20300 0 14:20 pts/1 00:00:00 grep --color=auto cat
[root@sandbox ~]#
次に
strace、で-p見つけたオプションとPIDから始めますps。開始後、strace接続したプロセスに関する情報とPIDが表示されます。今、straceコマンドによって作られたシステムコールを監視しますcat。最初に表示されるsyscallが読み取られ、スレッド0からの入力、つまり、コマンドが実行されている端末である標準入力からの入力を待機しますcat。
[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0,
コマンドを実行したままにしたターミナルに戻り、
catテキストを入力します。デモンストレーションのために、私は入りましたx0x0。cat入力しx0x0た内容を繰り返すだけで、画面が2回表示されることに注意してください。
[root@sandbox tmp]# cat
x0x0
x0x0
straceプロセスに接続
したターミナルに戻りますcat。これで、2つの新しいシステム呼び出しが表示されます。前の呼び出しreadは読み取り済みx0x0で、もう1つは書き込み用で、端末にwrite書きx0x0戻します。新しい呼び出しreadは、端末からの読み取りを待機しています。標準入力(0)と標準出力(1)が同じ端子上にあることに注意してください。
[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0, "x0x0\n", 65536) = 5
write(1, "x0x0\n", 5) = 5
read(0,
straceデーモン
の起動の利点を想像してみてください。バックグラウンドで実行されるすべてを確認できます。コマンドを完了するcatクリックすることで Ctrl+C..。これにより、セッションも終了しますstrace監視対象のプロセスが終了したため。
システム呼び出しのタイムスタンプを表示するには、次のオプションを使用します
-t。
[root@sandbox ~]#strace -t ls testdir/
14:24:47 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
14:24:47 brk(NULL) = 0x1f07000
14:24:47 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2530bc8000
14:24:47 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
システム呼び出しの間に費やされた時間を知りたい場合はどうなりますか?
-r各システム呼び出しを完了するのにかかる時間を示す便利なオプションがあります。かなり役に立ちましたね。
[root@sandbox ~]#strace -r ls testdir/
0.000000 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
0.000368 brk(NULL) = 0x1966000
0.000073 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb6b1155000
0.000047 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
結論
このユーティリティは
strace、Linuxでシステム呼び出しを学習するのに非常に便利です。その他のコマンドラインオプションについては、manおよびオンラインドキュメントを参照してください。
