コマンドの前にbash stat()とaccess()が多すぎます。普通ですか?

コマンドの前にbash stat()とaccess()が多すぎます。普通ですか?

実行するように指示されたシェルで実行すると、strace実際のバイナリが実行される前に広範な統計を示す次の出力が得られます。bashmkdirmkdir

BASH$> strace -f sh -c "bash -c \"mkdir /tmp\" 2>&1 | nl | grep -e "execve\|stat\|access" 
[.....]
  2766  [pid 17371] stat(".", {st_mode=S_IFDIR|0750, st_size=17262, ...}) = 0
  2767  [pid 17371] stat("/usr/local/sbin/mkdir", 0x7ffd87aad0a0) = -1 ENOENT      2767 (No such file or directory)
  2768  [pid 17371] stat("/usr/local/bin/mkdir", 0x7ffd87aad0a0) = -1 ENOENT (No such file or directory)
  2769  [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2770  [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2771  [pid 17371] access("/usr/bin/mkdir", X_OK) = 0
  2772  [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2773  [pid 17371] access("/usr/bin/mkdir", R_OK) = 0
  2774  [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2775  [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2776  [pid 17371] access("/usr/bin/mkdir", X_OK) = 0
  2777  [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2778  [pid 17371] access("/usr/bin/mkdir", R_OK) = 0
  2779  [pid 17371] execve("/usr/bin/mkdir", ["mkdir", "/tmp"], 0x557ec7e15920 /* 5 vars */) = 0

/usr/bin/mkdir stat()私の質問は:頻繁に編集されるのが普通ですか(それではなぜですか?)出力ラインには番号が付けられています。特にこの行が実行され2776たらどういう意味があるのか​​知りたいです2771。また、私はbashがすぐに情報を提供する必要があるため、2770すべてのシステムコールを最初から最後まで保存するという印象を受けました。私は何を見逃していますか?execvestat

以降の説明を調べて、代替dashシェルがどのように機能するかを確認し、次のstat()内容も表示されます。

DASH$> strace -f sh -c "dash -c \"mkdir /tmp\" 2>&1 | nl | grep -e "execve\|stat\|access" 
[....]
  2792  [pid 17372] stat("/usr/local/sbin/mkdir", 0x7ffc66010b50) = -1 ENOENT (No such file or directory)
  2793  [pid 17372] stat("/usr/local/bin/mkdir", 0x7ffc66010b50) = -1 ENOENT (No such file or directory)
  2794  [pid 17372] stat("/usr/sbin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
  2795  [pid 17372] execve("/usr/sbin/mkdir", ["mkdir", "/run"], 0x55d8d3453bb8 /* 6 vars */) = 0

私は27922793が2768 PATH`行に似ていることを知っています。2767are because of searching the executable in the various directories in the current

これがセール中であれば、一統計dashだけしてbash10回だけしてください。もう一度質問:これは普通ですか?

修正する:Bash統計には、より多くのgeteuid()getguid()およびgetuid()混合項目があります。getgid()

BASH$>strace -f sh -c "bash -c \"mkdir /tmp\"" 2>&1 | grep -e "execve\|stat\|access\|geteuid\|getegid\|getuid\|getgid" 
[....]
[pid 24534] stat("/usr/local/bin/mkdir", 0x7fffda480f30) = -1 ENOENT (No such file or directory)
[pid 24534] stat("/usr/local/sbin/mkdir", 0x7fffda480f30) = -1 ENOENT (No such file or directory)
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid()                   = 1000
[pid 24534] getegid()                   = 1000
[pid 24534] getuid()                    = 1000
[pid 24534] getgid()                    = 1000
[pid 24534] access("/usr/bin/mkdir", X_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid()                   = 1000
[pid 24534] getegid()                   = 1000
[pid 24534] getuid()                    = 1000
[pid 24534] getgid()                    = 1000
[pid 24534] access("/usr/bin/mkdir", R_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid()                   = 1000
[pid 24534] getegid()                   = 1000
[pid 24534] getuid()                    = 1000
[pid 24534] getgid()                    = 1000
[pid 24534] access("/usr/bin/mkdir", X_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid()                   = 1000
[pid 24534] getegid()                   = 1000
[pid 24534] getuid()                    = 1000
[pid 24534] getgid()                    = 1000
[pid 24534] access("/usr/bin/mkdir", R_OK) = 0
[pid 24534] execve("/usr/bin/mkdir", ["mkdir", "/tmp"], 0x55adcd4dc040 /* 55 vars */) = 0

それでは、これが「ここで何が起こっているのか」についての手がかりを提供できますか?悪用を防ぐためにいくつかのテストが実施されていますかsetuid

**更新2:** geteuid()getguid()およびgetuid()アクセスの組み合わせは、ライブラリ機能の使用の特徴であるようですgetgid()。使用するたびにbashが実行されるため、all、およびを使用します。glibcint eaccess(const char *pathname, int mode);eaccessgeteuidgetguidgetuidgetgidaccessfindcmd.c関数はfile_statusこのようにeaccessを2回実行します。

#if defined (HAVE_EACCESS)
  /* Use eaccess(2) if we have it to take things like ACLs and other
     file access mechanisms into account.  eaccess uses the effective
     user and group IDs, not the real ones.  We could use sh_eaccess,
     but we don't want any special treatment for /dev/fd. */
  if (eaccess (name, X_OK) == 0)
    r |= FS_EXECABLE;
  if (eaccess (name, R_OK) == 0)
    r |= FS_READABLE;

これらの各eaccessは、4つのシステムコールに接続できます。

答え1

ループを見てください。findcmd.c:find_user_command_in_path()

stat()(からfile_status())パスの各要素を2回:find_in_path_element()at行を介して1回640is_directory()atラインを通って一度645話

おっしゃった通りfile_status()いわゆるeaccess()

これは最適化できますが、パスがハッシュされ、これらのすべての検索と統計はコマンドが最初に使用されたときにのみ発生するため、大きな問題ではないことに注意してください。

答え2

見ているバッシュソースコード、正解は:

はい通話は正常であり、次のようないくつかの要因が原因で発生します。

  1. bashfile_status呼び出しを含む関数を実行すると、インクルードペアが実行され、statほとんどのGNU / Linux設定で実行されます。それぞれ違うeaccessから電話glibc
  2. glibceaccess走る再び statその後、メアリーgeteuid、、、そして最後に(有用な情報を記録しないので、glibcはシステムコールをまったく保存したくないかもしれません(コンテキストスイッチは重要ではありません!)getegidgetuidgetgidaccessstat
  3. bashディレクトリを繰り返すことができるファイルがPATH実際に実行可能であり、それを実行したいユーザーが読み取ることができることを確認したいと思います。 (試験)
  4. bashhash連続呼び出しの検索パスを減らすためにテーブルを実行します(2番目のテストfile_status)。

これにより、重複しているように見えるシステムコールが一括生成されます。 PATHの各カンテートは6/11システムコールを実行し、コマンドが以前にハッシュテーブルにあると確認された場合は、別の6/11システムコールが実行されるため、コマンドがまだ有効であることを確認します。

file_statusBashの関数は、findcmd.cLinuxコンピュータ用にコンパイルするときに次のようになります(例:ifdefs評価)。

int
file_status (name)
     const char *name;
{
  struct stat finfo;
  int r;

  /* Determine whether this file exists or not. */
  if (stat (name, &finfo) < 0)
    return (0);

  /* If the file is a directory, then it is not "executable" in the
     sense of the shell. */
  if (S_ISDIR (finfo.st_mode))
    return (FS_EXISTS|FS_DIRECTORY);

  r = FS_EXISTS;

  /* Use eaccess(2) if we have it to take things like ACLs and other
     file access mechanisms into account.  eaccess uses the effective
     user and group IDs, not the real ones.  We could use sh_eaccess,
     but we don't want any special treatment for /dev/fd. */
  if (exec_name_should_ignore (name) == 0 && eaccess (name, X_OK) == 0)
    r |= FS_EXECABLE;
  if (eaccess (name, R_OK) == 0)
    r |= FS_READABLE;

  return r;
}

stat()最初は1回実行され、順番にeaccess()2回実行されます(glibc関数は次のように実行されます。)

  1. stat
  2. geteuid
  3. getguid
  4. getuid
  5. getgid
  6. access )

したがって、この部分は元のbash出力を担当します。

[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid()                   = 1000
[pid 24534] getegid()                   = 1000
[pid 24534] getuid()                    = 1000
[pid 24534] getgid()                    = 1000
[pid 24534] access("/usr/bin/mkdir", X_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid()                   = 1000
[pid 24534] getegid()                   = 1000
[pid 24534] getuid()                    = 1000
[pid 24534] getgid()                    = 1000
[pid 24534] access("/usr/bin/mkdir", R_OK) = 0

関連情報