我们有一个看起来像这样的脚本:
#!/usr/bin/env node --unhandled-rejections=strict
console.log("Hi!");
Run Code Online (Sandbox Code Playgroud)
这在我的笔记本电脑 (OSX) 上运行良好,但当我们在 Linux 上运行它时,它就挂起了。使用 strace 我们可以看到它不断加载 libc 并执行
execve("./foo.sh", ["./foo.sh"], ["YARN_VERSION=1.22.4", "HOSTNAME=307d861c7c1a", "PWD=/", "HOME=/root", "NODE_VERSION=12.18.1", "TERM=xterm", "SHLVL=1", "PATH=/usr/local/sbin:/usr/local/"..., "_=/usr/bin/strace", "node --unhandled-rejections=stri"...]) = 0
Run Code Online (Sandbox Code Playgroud)
我们可以在这里看到 env 的参数被解释为环境变量。我们还知道“node --unhandled-rejections=strict”作为单个参数传入。
我们可以通过创建两个脚本来看到 OSX 中的差异。b1.sh:
args=("$@")
echo \"${args[0]}\" \"${args[1]}\" \"${args[2]}\"
Run Code Online (Sandbox Code Playgroud)
和 b2.sh
#!/usr/bin/env /tmp/b1.sh foo=bar
Run Code Online (Sandbox Code Playgroud)
当我们在 OSX 上运行 b2.sh 时,我们得到
"foo=bar" "./b2.sh" ""
Run Code Online (Sandbox Code Playgroud)
在 Linux 上它也会挂起。
很明显,在 Linux 和 OSX 中,传递给 env 的参数是不同的。在 OSX 中,“/tmp/b1.sh”和“foo=bar”是单独的参数。在 Linux 中,它们作为相同的参数传递。
但是为什么这会导致 env 一遍又一遍地执行相同的代码呢?
您可以使用-Sofenv来拆分node --unhandled-rejections=strict:
~/tmp$ uname -a
Linux ubuntu 5.4.0-37-generic #41-Ubuntu SMP Wed Jun 3 18:57:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
~/tmp$ cat ./test.sh
#!/usr/bin/env -S node --unhandled-rejections=strict
console.log("Hi!");
~/tmp$ ./test.sh
Hi!
~/tmp$
Run Code Online (Sandbox Code Playgroud)
现在解释一下您帖子中发生的事情。
当您运行时./foo.sh,相当于运行以下命令:
/usr/bin/env "node --unhandled-rejections=strict" ./foo.sh
Run Code Online (Sandbox Code Playgroud)
这是摘录自man env:
env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
Run Code Online (Sandbox Code Playgroud)
因此,env由于等号 (=),将“node --unhandled-rejections=strict”视为 NAME=VALUE"node --unhandled-rejections=strict"
然后env尝试./foo.sh作为命令运行,现在进入无限循环。
Bas*_*tch -1
阅读exec(3)、execve(2)、syscalls(2)、env(1)和strace(1)的文档;另请参见凭证(7)和path_resolution(7)。
hash-bang 行只能包含一行和一个空格。但最近的内核(例如 Debian/Sid 的 Linux 5.6)可能更加灵活。
你可能想开始你的脚本
#!/usr/bin/node --unhandled-rejections=strict
Run Code Online (Sandbox Code Playgroud)
另请阅读高级 Linux 编程和系统调用(2)
“为什么它会一遍又一遍地循环调用 execve”
您可以下载其源代码并研究它,和/或在kernelnewbies.org上询问,甚至可以修补您的内核源代码以适应您的需要。
我的观点是,修补内核源代码并不是一个好主意。更好的方法可能是根据您的需要编写自己的 C 程序调用,并使用GCCexecve等编译您的 C 代码(可能通过您的GCC 插件进行增强;然后另请阅读此草稿报告)。请务必阅读有关GCC 调用的内容。
当我们在 OSX 上运行 b2.sh 时,我们得到
但Linux不是OSX。您可能想了解有关POSIX规范的更多信息并阅读其中的一些(并查看这些幻灯片)
您可以用 C 语言编写自己的 Linux内核模块,提供系统调用的变体execve,但我不建议这样做。更喜欢在用户态编码,而不是在内核态。您的 C 程序和用户空间 可执行文件可能使用dlopen(3)和dlsym(3)来加载插件。
| 归档时间: |
|
| 查看次数: |
386 次 |
| 最近记录: |