PHP底层原理和源码分析初探

一、引言:

1、解释性语言:PHP,swoole,JAVA(有争议:先被编译为.class字节码后被JVM解释执行,都是具两种特性。因为虽然java也需要编译,编译成.class文件,但是并不是机器可以识别的语言,而是字节码,最终还是需要 jvm的解释,所以一般被归为解释性语言),JS,python,shell

2、编译型语言:C,C++, Golang,汇编

汇编语言:

机器语言汇编语言都是低级语言,它们都是面向机器的。汇编语言直接操作硬件,对CPU内的寄存器、运算器进行控制。

汇编语言由机器语言发展而来。开始人们是直接用机器语言编程的,后来有人编出了汇编程序,就可以直接用比较易用的汇编语言写程序。汇编程序负责把汇编语言写的程序转换成机器语言。有些编译器会现将高级语言的源代码编译成目标机器的汇编码,然后再经过汇编步骤才生成机器码。

汇编语言(汇编代码)-》汇编程序/汇编器(汇编语言转码即可,什么程序实现都行)-》汇编码-》二进制机器码。

我们编写的源代码是人类语言,我们自己能够轻松理解;但是对于计算机硬件(CPU),源代码就是天书,根本无法执行,计算机只能识别某些特定的二进制指令,在程序真正运行之前必须将源代码转换成二进制指令。

所谓的二进制指令,也就是机器码,是 CPU 能够识别的硬件层面的“代码”,简陋的硬件(比如古老的单片机)只能使用几十个指令,强大的硬件(PC 和智能手机)能使用成百上千个指令。

然而,究竟在什么时候将源代码转换成二进制指令呢?不同的编程语言有不同的规定:

有的编程语言要求必须提前将所有源代码一次性转换成二进制指令,也就是生成一个可执行程序(Windows 下的 .exe),比如C语言、C++、Golang、Pascal(Delphi)、汇编等,这种编程语言称为编译型语言,使用的转换工具称为编译器。

有的编程语言可以一边执行一边转换,需要哪些源代码就转换哪些源代码,不会生成可执行程序,比如 PythonJavaScriptPHP、Shell、MATLAB 等,这种编程语言称为解释型语言,使用的转换工具称为解释器。

Java 和 C# 是一种比较奇葩的存在,它们是半编译半解释型的语言,源代码需要先转换成一种中间文件(字节码文件),然后再将中间文件拿到虚拟机中执行。Java 引领了这种风潮,它的初衷是在跨平台的同时兼顾执行效率;C# 是后来的跟随者,但是 C# 一直止步于 Windows 平台,在其它平台鲜有作为。

是不是高级语言一定会先转变成汇编语言?

不是主要看编译器是如何设计的,有的编译器直接编译成机器语言,有的编译器直接编译成汇编语言再汇编成机器语言

大多解释语言就不会转成汇编语言,例如Java就生成*.class字节码,然后解释执行。

二、C语言的编译:

程序员编写的C语言代码,首先要经过C语言编译器,生成汇编代码,这个过程称为编译阶断,当C语言编译器生成汇编代码后,再调用汇编器来将汇编代码编译成汇编指令。(如何写编译器:直接用指令码写出第一个汇编语言编译器,然后就可以用汇编语言写新的编译器,其实很多语言都可以写汇编编译器。比如第一个C语言编译器可能是用汇编写的,但是以后的C编译器都可以用C语言来写--也就是编译到最终还是执行的汇编代码)

这是一种站在巨人肩人的作法,最早的C++编程语言也是这样的实现方法,只不过那时候叫Cfront程序,Cfront程序的作用是将C++代码转换成C语言代码,类似于一个文本处理器,然后再调用C语言编译器,将C源码编译成汇编代码,然后再调用汇编器将汇编代码编译成机器码。

这个过程,在Windows平台上不容易操作,但是在Linux平台上很容易看到。以gcc这款c语言编译器为例,它实际上是四个小程序。

cp: c语言预处理程序,有它负责进行预处理操作。

cc: C语言编译器,它负责将C源码编译成汇编代码。

as: 汇编器,它负责将汇编代码编译成机器码,一般使用gcc test.c这样的命令编译C语言时,会生成一个a.out的程序,它实际上指的就是as ouput,即汇编器输出文件。

link: 链接器,它负责将汇编器输入的机器码和库打包成一个操作系统可以运行的可执行文件,在Linux上的可执行文件格式是ELF格式,这个格式的实现是有链接器来完成的。

高级语言都会先转译成C再编译吗?

历史上是有一些语言会选择转译到c,再交给c编译器完成到二进制的编译(事实上现在也有),但这一直不是也不可能是主流做法。稍了解过编译相关知识就可以明白编译器(或者转译器)跑完AST生成后,是直接生成二进制或是转译成其他语言的工作量不是在一个数量级的,除非特殊原因没必要多此一举。


三、PHP的编译:

就PHP语言来说,它也是一组符合一定规则的约定的指令。 php的解释器是用c写的,解释器相当于弱编译器,但是php本身并不基于某种底层语言。 在编程人员将自己的想法以PHP语言实现后,通过PHP的虚拟机(确切的来说应该是引擎Zend)将这些PHP指令转变成C语言指令(编译过程)opcode(类似JAVA的.class是字节码,由虚拟机JVM执行),而C语言又会转变成汇编语言, 最后汇编语言将根据处理器的规则转变成机器码执行。PHP跨平台不是解释器解释而是zend虚拟机屏蔽了操作系统的区别,和java的JVM虚拟机有点像。但和Java不同的是.class是保留住的可以打包运行,而PHP编译完opcode程序执行完就丢弃。

我们分别来脑补下PHP请求运行的整个流程:

nginx -》 httpd-》fastCGI-》PHP-FPM(前两统称SAPI)-》PHP (PHPAPI + EXT)-》Zend引擎-》调用C函数编译成opcode -》并由Zend虚拟机执行这些指令

apache -> httpd -> php5_module(sapi) -> PHP->...

zend编译器与zend虚拟机

从一种语言到另一种语言的转化称之为编译,这两种语言分别可以称之为源语言和目标语言。 这种编译过程通过发生在目标语言比源语言更低级(或者说更底层)。 语言转化的编译过程是由编译器来完成, 编码器通常被分为一系列的过程:词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成等。 前面几个阶段(词法分析、语法分析和语义分析)的作用是分析源程序,我们可以称之为编译器的前端。 后面的几个阶段(中间代码生成、代码优化和目标代码生成)的作用是构造目标程序,我们可以称之为编译器的后端。

 一种语言被称为编译类语言,一般是由于在程序执行之前有一个翻译的过程, 其中关键点是有一个形式上完全不同的等价程序生成(如C的.exe和Java的.class文件,但JAVA不属于编译型语言)。 而PHP之所以被称为解释类语言,就是因为并没有这样的一个程序生成, 它生成的是中间代码Opcode,这只是PHP的一种内部数据结构。其实严格地说PHP是一种编译类语言,因为综上所述PHP是有编译过程的,先编译成中间代码opcode后执行,只不过每次执行完程序编译完的Opcode被销毁。但现在已经可以借助一些第三方工具加速器来缓存住opcode,类似JAVA的.class文件。

四、PHP底层源码分析:

这里推荐PHP底层原理的参考文:

深入理解PHP代码的执行的过程

PHP底层工作原理

PHP底层源码分析的视频(强推):PHP5.3底层运行原理(底层源码分析C)

推荐书籍:PHP 7底层设计与源码实现

视频分析了PHP的底层原理C的实现,小马把大概的提纲列一下当脑图用,有助于复盘。总结一下就是一切皆结构体,哈希表作为花名册发挥着重大作用。

提纲

以下把一些精华笔记也放上供回忆复盘用。

变量赋值的实现(结构体)
变量写时即赋值(结构体分裂)
引用传值(写时不分裂),is_ref_gc:1代表引用传值。
强制分裂的情况
数组赋值
只是小单元是引用的,会出现问题。也算是BUG。原理如上图。
注意指针拨动。下面的写导致分裂,数组引用的怪现象。
数组引用的怪现象
每个函数有自己的符号表(变量花名册)
一个函数只有一份op_array,静态变量在op_array中,所以是全局共享的
常量也可以定义对象(从源码发现)
对象的实现,值是一个指针
为什么不是4.5,其实是一个指针
并没有改变ZVAL值所以并不分裂。
内存分层
内存分层:都是向堆申请内存,不分全局,局部还是常量区,都由哈希表来指向区分。(局部哈希表,常量哈希表等),与JAVA不同,其是对内存分块的,划分好的。


五、如何写一个PHP拓展:

1、如何写一个PHP拓展

写拓展要先熟悉底层源码才好上手。参考如下:

2、Linux 安装PHP扩展过程

假设安装extend_test拓展:
1、生成扩展:

在extend_test拓展文件夹下,运行/home/php/bin/phpize(实际为phpize所在路径)

然后运行./configure --with-php-config=/home/php/bin/php-config(实际为php-config所在路径)

2、编译安装

make           

make install

3、配置PHP文件,将拓展配置进去即可。


swoole可以理解为在C的上面套了一层PHP语法的壳,使PHP可以使用上协程这些东西。swoole其实是PHP的一个新拓展,只不这个拓展有点强大。

就到这吧,总结自用笔记,不到之处欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351