惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
博客园 - Franky
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿
博客园 - 司徒正美
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
宝玉的分享
宝玉的分享
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
博客园_首页
S
SegmentFault 最新的问题
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
GbyAI
GbyAI
H
Help Net Security
MongoDB | Blog
MongoDB | Blog

WangDeer

非机动车交通规则测验 微软 50 周年壁纸是在 macOS上设计的? 「转」西二旗折叠:蜂巢经济学启示录 难得空明 Ubuntu Clean ApidocJs apigroup Support Chinese int(10)里面的10是什么意思? 5-3 会话 2021-03-25 「PHP架构师」面试准备 Laravel 上传 Docx 文件,表单验证不通过 4-4 发送信号 4-3 信号集 4-2 编写中断信号处理程序 2022-03-10 面试复盘 4-1 什么是中断信号(软中断) 3-7 进程查看 3-6 SUID特权进程 3-5多进程编写 3-4进程调度 3-3进程exec 3-2进程退出和回收 3-1进程标识与fork 2-2命令行参数与环境参数表 2-1程序与进程 1-1课程介绍 终极复盘 第八章 传承篇 第七章 自由篇 第六章 品牌篇 第五章 作品篇
2-3解释器文件
王二 · 2022-02-18 · via WangDeer

解释器文件

解释器文件,是一种文本文件,例如 PHP 写的解释器文件 demo.php

#!/usr/bin/php
<?php

echo 'Hello World!'

chmod u+x demo.php 赋予可执行权限,即可直接以 ./demo.php 执行。

解释器,是可执行文件 ELF,例如 /usr/bin/php

bash 进程启动过程

bash 进程是如何创建的?

  1. 直接在 centos、ubuntu等终端直接登录,由 login 服务开启 bash 进程。

  2. 通过网络方式登录到 shell 终端,由 sshd 服务开启 bash 进程。

    sshd 服务默认监听 22 端口。

进程观察的几个命令:

  1. pstree 查看 Linux 进程间的关系,显示进程树。

    pstree -ap

        ├─sshd,9408
        │   └─sshd,444836    
        │       └─bash,444875
        │           └─pstree,444893 -ap
    

    ctrl+c 产生一个中断信号,退出当前终端正在执行的进程。

    ctrl+z 把当前台进程丢到后台去暂停。

  2. 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) 

解释器文件在 bash 中运行的过程