解释器文件
解释器文件,是一种文本文件,例如 PHP 写的解释器文件 demo.php
。
#!/usr/bin/php
<?php
echo 'Hello World!'
chmod u+x demo.php
赋予可执行权限,即可直接以 ./demo.php
执行。
解释器,是可执行文件 ELF,例如 /usr/bin/php
。
bash 进程启动过程
bash 进程是如何创建的?
-
直接在 centos、ubuntu等终端直接登录,由 login 服务开启 bash 进程。
-
通过网络方式登录到 shell 终端,由 sshd 服务开启 bash 进程。
sshd 服务默认监听 22 端口。
进程观察的几个命令:
-
pstree 查看 Linux 进程间的关系,显示进程树。
pstree -ap
:├─sshd,9408 │ └─sshd,444836 │ └─bash,444875 │ └─pstree,444893 -ap
ctrl+c 产生一个中断信号,退出当前终端正在执行的进程。
ctrl+z 把当前台进程丢到后台去暂停。
-
strace,跟踪系统调用和信号。
通过
strace -f -s 65500 -p 9408 -o ssh.log
追踪 pid = 9408 的sshd
进程的系统调用,来分析 bash 进程是如何创建的。strace: Process 9408 attached strace: Process 447115 attached ... strace: Process 447151 attached ^Cstrace: Process 9408 detached strace: Process 447115 detached strace: Process 447143 detached
ssh.log 部分内容:
9408 select(95, [3], NULL, NULL, NULL) = 1 (in [3]) 9408 accept(3, {sa_family=AF_INET, sin_port=htons(4143), sin_addr=inet_addr("1xx.1x9.xx3.1xx")}, [128->16]) = 4 // accept 用来接收一个 socket 客户端连接 ... 9408 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1fc4482210) = 447115 // clone = fork 用来创建一个子进程 447115 execve("/usr/sbin/sshd", ["/usr/sbin/sshd", "-D", "-R"], 0x55d325ea09c0 /* 7 vars */) = 0 // execve 用来执行一个程序 447115 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD <unfinished ...> 447143 set_robust_list(0x7fa0e70b0220, 24 <unfinished ...> 447115 <... clone resumed>, child_tidptr=0x7fa0e70b0210) = 447143 447143 <... set_robust_list resumed>) = 0 // 创建子进程 447143 ... 447143 execve("/bin/bash", ["-bash"], 0x563bf3853680 /* 25 vars */) = 0 // 执行 bash 程序
a. 通过 accept 接收 ssh 客户端连接;
b. 通过 clone 创建一个子进程;
c. 通过 execve 执行 sshd 程序;
d. 创建一个子进程;
e. 执行 bash 程序。
解释器文件在 bash 中运行的过程
通过 strace -f -s 65500 -o demo_with_php.log php demo3.php
追踪 php demo3.php
形式的运行过程:
demo_with_php.log
部分内容:
// system call
505 execve("/usr/bin/php", ["php", "demo3.php"], 0x7ffdaed67dc0 /* 53 vars */) = 0
...
// 打开解释器文件
505 openat(AT_FDCWD, "demo3.php", O_RDONLY) = 3
...
// 读取解释器文件
505 read(3, "#!/usr/bin/php\n<?php\n\necho \"Hello World!\";\n", 4096) = 43
// 解释执行
505 close(3) = 0
505 write(1, "Hello World!", 12) = 12
505 close(0) = 0
通过 strace -f -s 65500 -o demo.log php demo3.php
追踪 php demo3.php
形式的运行过程:
demo.log
部分内容:
// execve 函数在载入文件的时候,一般是当做 ELF 可执行文件载入,读取 ELF Header。
// 如果不是 ELF 文件,就当做解释器文件读取文件的第一行,得到解释器的完整路径。
596 execve("./demo3.php", ["./demo3.php"], 0x7ffd6a87e5f8 /* 53 vars */) = 0
...
// 打开解释器文件
596 openat(AT_FDCWD, "./demo3.php", O_RDONLY) = 3
...
// 解释执行
596 read(3, "#!/usr/bin/php\n<?php\n\necho \"Hello World!\";\n", 4096) = 43
596 close(3) = 0
596 write(1, "Hello World!", 12) = 12
596 close(0)