/proc/ 的内容/cmdline 文件依赖于脚本 shebang?

/proc/ 的内容/cmdline 文件依赖于脚本 shebang?

我有一个bash名为 的脚本test.sh,以startproc.如果我#!/usr/bin/env bash在 中使用 shebang test.sh,该/proc/<PID>/cmdline文件如下所示:

SERVER:~ # cat /proc/29481/cmdline
bash/root/user/test.shSERVER:~ # 
SERVER:~ # 

现在,当我将 shebang 行更改为 时#!/bin/bash/proc/<PID>/cmdline文件如下:

SERVER:~ # cat /proc/29729/cmdline
/bin/bash/root/user/test.shSERVER:~ # 
SERVER:~ # 

是什么导致了这种行为?文件的内容/proc/<PID>/cmdline取决于脚本shebang吗?问题是,如果使用前一个选项checkprockillproc或者startproc无法检测到该test.sh服务。我使用 openSUSE 11.4 和sysvinit-tools-2.88-37.47.1.x86_64.

答案1

是的,cmdline具体取决于exec*进行的系统调用,并且完全限定的路径可能env(1)与其路径搜索中出现的路径不同。

bash-4.1$ cat aaa
#!/bin/bash
xxd /proc/$$/cmdline
bash-4.1$ cat bbb
#!/usr/bin/env bash
xxd /proc/$$/cmdline
bash-4.1$ ./aaa
0000000: 2f62 696e 2f62 6173 6800 2e2f 6161 6100  /bin/bash../aaa.
bash-4.1$ ./bbb
0000000: 6261 7368 002e 2f62 6262 00              bash../bbb.
bash-4.1$ 

strace显示细节:

bash-4.1$ strace ./aaa 2>&1 | grep exec
execve("./aaa", ["./aaa"], [/* 57 vars */]) = 0
bash-4.1$ strace ./bbb 2>&1 | grep exec
execve("./bbb", ["./bbb"], [/* 57 vars */]) = 0
execve("/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/bin/bash", ["bash", "./bbb"], [/* 57 vars */]) = 0
bash-4.1$ 

如果这是一个仅限 Linux 的脚本,那么 的位置bash可能不会改变(例外:在其他地方有一个包含bash, 的某种软件库)。因此,使用完全限定路径可能是最有意义的,因为这可以避免env(1)在路径搜索中出现混乱,并获得与 init 工具的兼容性。

答案2

进程的命令行由argv参数的元素组成execve系统调用。此参数是一个从 0 开始编号的数组,其中编号 1 到 n 的元素是调用命令时传递的参数,元素 0 由发出调用的 shell 或其他程序选择execve。按照惯例,元素 0 是用于指定命令的字符串。

Shebang 行由内核处理。内核插入一个参数 0,它是#!魔术前缀后指定的路径。因此,如果您使用中的/root/user/test.sh一个参数运行,则使用两个参数 0= 、 1=进行调用。当以 开头时,内核会看到 shebang并将参数列表重写为 0= 、 1= 、 2= 。foostartprocstartprocexecve/root/user/test.shfoo/root/user/test.sh/bin/bash/root/user/test.sh/bin/bash/root/user/test.shfoo

如果 shebang 行是#!/bin/env bash,则内核会在参数列表中插入两项:程序和参数。 (Linux 此处仅限于单个参数。)因此,在本例中,调用将转换为 0= /bin/env、 1= bash、 2= /root/user/test.sh、 3= foo。该env程序完成其工作并发出一个新的execve系统调用 0= bash(遵守参数 0 是用于指定命令的路径的约定)、 1= /root/user/test.sh、 2= foo

在所有情况下,/root/user/test.sh都将是该过程的第一个参数。

相关内容