GNU/Linux >> Linux の 問題 >  >> Linux

straceを使用したLinuxでのシステムコールの理解

システムコールは、プログラムがカーネルにサービスを要求するプログラム的な方法であり、 strace は、ユーザープロセスとLinuxカーネルの間の薄層を追跡できる強力なツールです。

オペレーティングシステムがどのように機能するかを理解するには、まずシステムコールがどのように機能するかを理解する必要があります。オペレーティングシステムの主な機能の1つは、ユーザープログラムに抽象化を提供することです。

オペレーティングシステムは、大きく2つのモードに分けることができます。

  • カーネルモード: オペレーティングシステムカーネルで使用される特権的で強力なモード
  • ユーザーモード: ほとんどのユーザーアプリケーションが実行される場所

ユーザーは主に、コマンドラインユーティリティとグラフィカルユーザーインターフェイス(GUI)を使用して、日常のタスクを実行します。システムコールはバックグラウンドでサイレントに動作し、カーネルとインターフェイスして作業を実行します。

その他のLinuxリソース

  • Linuxコマンドのチートシート
  • 高度なLinuxコマンドのチートシート
  • 無料のオンラインコース:RHELの技術概要
  • Linuxネットワーキングのチートシート
  • SELinuxチートシート
  • Linuxの一般的なコマンドのチートシート
  • Linuxコンテナとは何ですか?
  • 最新のLinux記事

システムコールは関数呼び出しと非常によく似ています。つまり、引数と戻り値を受け入れて処理します。唯一の違いは、システムコールはカーネルに入りますが、関数呼び出しは入りません。ユーザースペースからカーネルスペースへの切り替えは、特別なトラップメカニズムを使用して行われます。

このほとんどは、システムライブラリ(別名 glibc )を使用してユーザーから隠されています。 Linuxシステムの場合)。システムコールは本質的に一般的なものですが、システムコールを発行するメカニズムはマシンに大きく依存します。

この記事では、いくつかの一般的なコマンドを使用し、 strace を使用して各コマンドによって行われたシステムコールを分析することにより、いくつかの実用的な例を探ります。 。これらの例ではRedHatEnterprise Linuxを使用していますが、コマンドは他のLinuxディストリビューションでも同じように機能するはずです:

 [root @ sandbox〜]#cat / etc / redhat-release 
Red Hat Enterprise Linux Serverリリース7.7(Maipo)
[root @ sandbox〜]#
[root @ sandbox 〜]#uname -r
3.10.0-1062.el7.x86_64
[root @ sandbox〜]#

まず、必要なツールがシステムにインストールされていることを確認します。 straceかどうかを確認できます 以下のRPMコマンドを使用してインストールされます。そうである場合は、 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つのファイルを作成します 使用するコマンド:

 [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を使用します その下でシステムコールが機能していることに気付かずに毎日コマンドを実行します。ここでは抽象化が行われています。このコマンドの仕組みは次のとおりです。

 Command-line utility -> Invokes functions from system libraries (glibc) -> Invokes system calls 

ls コマンドは、システムライブラリ(別名 glibc )から関数を内部的に呼び出します )Linuxの場合。これらのライブラリは、ほとんどの作業を行うシステムコールを呼び出します。

glibcから呼び出された関数を知りたい場合 ライブラリの場合は、 ltraceを使用します コマンドの後に通常のlstestdir / コマンド:

 ltrace ls testdir/ 

ltraceの場合 がインストールされていない場合は、次のように入力してインストールします:

 yum install ltrace 

大量の出力が画面にダンプされます。心配する必要はありません。従うだけです。 ltraceの出力からの重要なライブラリ関数の一部 この例に関連するコマンドは次のとおりです。

 opendir( "testdir /")={3} 
readdir({3})119 " }
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})

上記の出力を見ると、おそらく何が起こっているのかを理解できます。 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
<<<切り捨てられたstrace出力>>>
write(1、 "file1 file2 \ n"、13file1 file2
)=13 />近い(1)=0
と、munmap(0x7fd002c8d000、4096)=0
近い(2)=0
exit_group(0)=?
+++ EXITED 0と+++
[root @ sandbox tmp]#

straceを実行した後の画面の出力 コマンドは、 lsを実行するために行われたシステムコールでした。 指図。各システムコールは、オペレーティングシステムの特定の目的を果たし、次のセクションに大まかに分類できます。

  • プロセス管理システムコール
  • ファイル管理システムコール
  • ディレクトリおよびファイルシステム管理システムコール
  • その他のシステムコール

画面にダンプされた情報を分析する簡単な方法は、 straceを使用して出力をファイルに記録することです。 の便利な-o 国旗。 -oの後に適切なファイル名を追加します フラグを立ててコマンドを再実行します:

 [root @ sandbox tmp]#strace -o trace.log ls testdir / 
file1 file2
[root @ sandbox tmp]#

今回は、出力が画面にダンプされません— ls コマンドは、ファイル名を表示し、すべての出力をファイル trace.log に記録することで、期待どおりに機能しました。 。このファイルには、単純な ls のためだけに、ほぼ100行のコンテンツが含まれています。 コマンド:

 [root @ sandbox tmp]#ls -l trace.log
-rw-r--r--。 1ルートルート780910月12日13:52trace.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 この場合)は、 execveによって返される値です。 システムコール。

出力は今ではそれほど威圧的ではないようですよね?また、同じロジックを適用して他の行を理解することもできます。

ここで、呼び出した単一のコマンド、つまり ls testdirに焦点を絞ります。 。コマンドlsで使用されるディレクトリ名を知っています 、だからなぜ grep testdirの場合 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 を実行する前に、次のパッケージがインストールされていることを確認してください コマンド:

 [root @ sandbox tmp]#rpm -qa | grep -i man-pages 
man-pages-3.53-5.el7.noarch
[root @ sandbox tmp]#

2を追加する必要があることを忘れないでください の間 コマンドとシステムコール名。 を読んだ場合 man manを使用したのmanページ 、セクション2がシステムコール用に予約されていることがわかります。同様に、ライブラリ関数に関する情報が必要な場合は、 3を追加する必要があります の間 およびライブラリ関数名。

マニュアルのセクション番号とそれに含まれるページの種類は次のとおりです。

1。実行可能プログラムまたはシェルコマンド
2。システムコール(カーネルが提供する関数)
3。ライブラリ呼び出し(プログラムライブラリ内の関数)
4。特別なファイル(通常は/ devにあります)

次のを実行します システムコール名を指定してコマンドを実行すると、そのシステムコールのドキュメントが表示されます。

 man 2 execve 

execveによる マニュアルページでは、引数で渡されるプログラム(この場合は ls )を実行します。 )。 lsに提供できる追加の引数があります 、 testdirなど この例では。したがって、このシステムコールは lsを実行するだけです。 testdirを使用 引数として:

'execve-プログラムの実行'

'DESCRIPTION
execve()は、ファイル名が指すプログラムを実行します'

statという名前の次のシステムコール 、 testdirを使用します 引数:

 stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0 

man 2 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エントリ*/、32768)=112
getdents(3、/ *0エントリ*/、32768 )=0
[root @ sandbox tmp]#

getdents マニュアルページでは、ディレクトリエントリを取得と説明しています。 、それはあなたがやりたいことです。 getdentsの引数に注意してください 3です 、これは openatからのファイル記述子です 上記のシステムコール。

ディレクトリリストができたので、ターミナルに表示する方法が必要です。つまり、 grep 別のシステムコールの場合は、書き込み 、ログへの書き込みに使用されます:

 [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-標準エラー

したがって、書き込み システムコールはfile1を表示しています およびfile2 1で識別される端末である標準ディスプレイ上 。

これで、 ls testdir /のほとんどの作業を実行したシステムコールがわかりました。 指図。しかし、 trace.logにある他の100以上のシステムコールについてはどうでしょうか。 ファイル?オペレーティングシステムは、プロセスを実行するために多くのハウスキーピングを実行する必要があるため、ログファイルに表示されるものの多くは、プロセスの初期化とクリーンアップです。 trace.log全体を読む ファイルを作成し、 lsを作成するために何が起こっているのかを理解してください コマンドワーク。

特定のコマンドのシステムコールを分析する方法がわかったので、この知識を他のコマンドに使用して、実行されているシステムコールを理解できます。 strace 便利なコマンドラインフラグが多数用意されており、簡単に使用できます。そのうちのいくつかを以下に説明します。

デフォルトでは、 strace すべてのシステムコール情報が含まれているわけではありません。ただし、便利な-v冗長があります 各システムコールに関する追加情報を提供できるオプション:

 strace -v ls testdir 

常に-fを使用することをお勧めします straceを実行するときのオプション 指図。 straceを許可します 現在トレースされているプロセスによって作成された子プロセスをトレースするには:

 strace -f ls testdir 

システムコールの名前、実行回数、および各システムコールに費やされた時間の割合だけが必要だとします。 -cを使用できます それらの統計を取得するためのフラグ:

 strace -c ls testdir/ 

オープンに焦点を当てるなど、特定のシステムコールに集中したいとします。 システムコールと残りを無視します。 -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
+++は0で終了しました+++
[root @ sandbox tmp]#

複数のシステムコールに集中したい場合はどうなりますか?心配ありません。同じ-eを使用できます。 2つのシステムコールの間にコンマが付いたコマンドラインフラグ。たとえば、書き込みを表示するには およびgetdents システムコール:

 [root @ sandbox tmp]#strace -e write、getdents ls testdir 
getdents(3、/ *4エントリ*/、32768)=112
getdents(3、/*0エントリ* /、32768)=0
write(1、 "file1 file2 \ n"、13file1 file2
)=13
+++ 0 +++
[root@で終了サンドボックスtmp]#

これまでの例では、明示的に実行されたコマンドをトレースしました。しかし、すでに実行され実行中のコマンドについてはどうでしょうか。たとえば、長時間実行されるプロセスであるデーモンをトレースする場合はどうなりますか?このために、 strace 特別な-pを提供します プロセスIDを提供できるフラグ。

straceを実行する代わりに デーモンで、の例を見てください コマンド。通常、引数としてファイル名を指定すると、ファイルの内容が表示されます。引数が指定されていない場合、 コマンドは、ユーザーがテキストを入力するのを端末で待機するだけです。テキストが入力されると、ユーザーがCtrl+Cを押して終了するまで指定されたテキストが繰り返されます。

を実行します 1つの端末からのコマンド。プロンプトが表示され、そこで待つだけです(を覚えておいてください) まだ実行中であり、終了していません):

 [root@sandbox tmp]# cat 

別の端末から、 ps を使用してプロセス識別子(PID)を見つけます コマンド:

 [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 によって行われたシステムコールをトレースしています 指図。最初に表示されるシステムコールは読み取りです 、0からの入力を待機している、または catが存在する端末である標準入力 コマンドが実行されました:

 [root @ sandbox〜]#strace -p 22443 
strace:プロセス22443がアタッチされました
read(0、

ここで、を離れたターミナルに戻ります。 コマンドを実行し、テキストを入力します。 x0x0を入力しました デモ用。 に注目してください 入力した内容を繰り返しただけです。したがって、 x0x0 2回表示されます。最初のものを入力し、2番目のものはによって繰り返された出力でした コマンド:

 [root @ sandbox tmp]#cat 
x0x0
x0x0

straceがあるターミナルに戻ります に取り付けられました 処理する。これで、2つの追加のシステムコールが表示されます。以前の読み取り システムコール。x0x0と表示されます。 ターミナルで、もう1つは書き込み x0x0と書いた ターミナルに戻り、もう一度新しい読み取り 、端末からの読み取りを待機しています。標準入力( 0 )に注意してください )および標準出力( 1 )両方が同じ端末にあります:

 [root @ sandbox〜]#strace -p 22443 
strace:プロセス22443がアタッチされました
read(0、 "x0x0 \ n"、65536)=5
write(1、 " x0x0 \ n "、5)=5
read(0、

straceを実行するときにこれがどれほど役立つか想像してみてください デーモンに対して、バックグラウンドで実行するすべてを確認します。 を殺す Ctrl+Cを押してコマンドを実行します。これにより、 straceも強制終了されます プロセスが実行されなくなったため、セッション。

すべてのシステムコールに対するタイムスタンプを表示する場合は、 -tを使用するだけです。 straceのオプション :

 [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_ANONY -1、0)=0x7f2530bc8000
14:24:47 access( "/ etc / ld.so.preload"、R_OK)=-1 ENOENT(そのようなファイルまたはディレクトリはありません)
14:24: 47 open( "/ etc / ld.so.cache"、O_RDONLY | O_CLOEXEC)=3

システムコールの間に費やされた時間を知りたい場合はどうなりますか? strace 便利な-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.000119 open( "/ etc / ld.so.cache"、O_RDONLY | O_CLOEXEC)=3
結論

strace ユーティリティは、Linuxでのシステムコールを理解するのに非常に便利です。その他のコマンドラインフラグについては、manページとオンラインドキュメントを参照してください。


Linux
  1. procps-ngを使用してターミナルでLinuxシステムを監視します

  2. noatimeでLinuxシステムのパフォーマンスを向上させる

  3. LinuxでのCronを使用したシステムタスクのスケジューリング

  1. Linuxシャットダウンコマンド(例付き)

  2. Linuxのセキュリティとユーザビリティのバランス

  3. ハウツー:Linux でのディレクトリを使用した C プログラミング

  1. VirtualBoxを搭載したオペレーティングシステムでLinuxをお試しください

  2. Linuxセキュリティツールを使用した侵入テスト

  3. Linux の cat に比べて、システム コールを使用した cat 関数が遅いのはなぜですか?