プロセスpsコマンドがディレクトリの周りにスラッシュの代わりにスペースを表示するのはなぜですか?

プロセスpsコマンドがディレクトリの周りにスラッシュの代わりにスペースを表示するのはなぜですか?

実行中のプロセスを取得する前に失敗したプロセスモニターをデバッグしようとしています。私はこれが不思議なLinux動作のために起こったと確信しています。実行中のプロセスは/opt/my/path/directoryname/daemonnameですが、奇妙な理由でディレクトリ名の周りに/の代わりにスペースが表示されます。多くのデーモンが同じディレクトリで実行されており、正しいパスで表示されます。各サーバーの特定のデーモンだけです。

ps -efこのデーモンについては、以下を表示します。

/opt/my/path directoryname daemonname --arg1 VALUE1 --arg-two VALUE.TWO --arg-three ARG.UMENT.THREE.ERR --worker daemonname --pid-role daemonname

デーモンはPerlスクリプトによって開始されます。他のスクリプトと比較すると、ディレクトリパスは他のすべてのスクリプトとまったく同じ環境変数を使用して提供されるように見えますが、これらの他のスクリプトは正しいパスを生成します。たとえば、/opt/my/path/directoryname/daemonname関連するperl行は次のようになりますexec => catfile( $settings->config("/directories/executables"), $daemon_name ),。 config( "/directories/executables") が正しい値です。/opt/my/path/directoryname

スペースを含むディレクトリ名に関するすべての質問のために、この問題を検索するのは悪夢でした。これはそうではありません。すべてのパスにスペースを含めないでください。 psはスラッシュスペースを含むmkdirコマンドを表示します。私が見つけることができる最も近い場所でしたが、答えはありませんでした。

答え1

返されるフィールドの1つは、ps -fプロセス(またはその祖先の1つ)によって実行された最後のコマンドに渡された引数のリストです。通過できますps -o args

プロセスがファイルを実行すると、これはexecve()システムコールを介して行われます。

execve("/path/to/executable", [arg0, arg1..., 0], [env1, env2... 0])

arg0通常、これは実行可能ファイルの名前です。シェルが解釈されるとき:

cmd arg

コマンドラインでは、次のように呼び出しますexecve("/path/to/cmd", ["cmd", "arg", 0], ...)

/path/to/cmd arg

それは電話するexecve("/path/to/cmd", ["/path/to/cmd", "arg", 0], ...)

これらの文字列(cmd、、arg)はプロセススタックの一番下にあり、NULで区切られています。この過程でargv[0]argv[1]... を指すものです。

Linuxでは、ゾーンはサイトからのインポートに/proc/<pid>/cmdlineさらされます。psargs

技術的には、ps文字列のリストをスペースで連結して印刷し、最終フィールドがコマンドのargs実行に使用されるシェルコマンドラインのように見えます。

ここにps表示される場合:

/opt/my/path directoryname daemonname --arg1...

これらのスペースは、これらの文字列の実際のスペース、別々の文字列、またはNULで置き換えられた文字列のスペースを表すことができます。

手順を使用する場合標準strtok()機能あるいは、sargv[0]に分割するのと似たように/実行されている実行可能ファイルのベース名またはパスの他のディレクトリコンポーネントを見つけることもできます。

たとえば、次のように生成する場合:

#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
  strtok(argv[0], "/");
  while (strtok(NULL, "/"));
  pause();
}

コンパイルして実行します。

$ cc a.c
$ "$PWD/a.out" &
[1] 67758
$ ps -fp "$!"
UID          PID    PPID  C STIME TTY          TIME CMD
chazelas   67758   60043  0 19:19 pts/4    00:00:00 /home chazelas a.out

/sが空白に置き換えられていることがわかります。

実際にはNULに置き換えられており、次のように確認できます。

$ sed -n l "/proc/$!/cmdline"
/home\000chazelas\000a.out\000$

ただし、psこれらのNULを見て、それらをスペースで連結された3つの異なるパラメータの区切り文字として解釈します。

後でコメントでも指摘したように、dirname()/引数で s を 0 に置き換える別の標準関数です。

$ cat b.c
#include <libgen.h>
#include <unistd.h>
int main(int argc, char *argv[]) {dirname(dirname(argv[0])); pause();}
$ cc b.c
$ "$PWD/a.out" &
[1] 42128
$ ps -fp "$!"
UID          PID    PPID  C STIME TTY          TIME CMD
chazelas   42128   39744  0 20:18 pts/6    00:00:00 /home chazelas a.out

もしそうなら、これが正しい解釈であると仮定すると、そうではありません。奇妙なLinuxの動作これは、起動後にアプリケーション自体argv[0]を変更するだけです(/おそらくいくつかのパスコンポーネントを抽出するためにsをNULに置き換えることによって)。

Linuxでプロセスが現在実行されている実行可能ファイルを確認するためのより信頼性の高い方法は、たとえばユーティリティまたは組み込みの次のいずれかを使用してreadlink()実行することです。/proc/<pid>/exerealpathreadlinkstatzsh

$ zmodload zsh/stat
$ stat +link /proc/$!/exe
/home/chazelas/a.out
$ readlink "/proc/$!/exe"
/home/chazelas/a.out
$ realpath "/proc/$!/exe"
/home/chazelas/a.out

Linuxのprocpsの実装psはこれをps -o exe

/proc/<pid>/cmdlineプロセスが表示される内容を変更できるため、コマンドに渡される引数を決定する明確な方法はありません。

ただし、監査ログにはこの情報を含めることができます。

関連情報