软件那些事儿

82. 钻钻牛角尖:为什么开发浏览器引擎这么难?


Listen Later

这一期的内容继续讲浏览器的工作原理,以及一些吐槽,或者这期的内容继续吐槽,顺便讲一点浏览器的工作原理。因为有些听众希望听到干货,不想听吐槽,对这些听众我想说的是,进错剧场了,我这个节目主要是吐槽,没有啥干货,并且,我始终认为,如果想学编程,希望通过睡前听不到半小时的音频,这是缘木求鱼。编程这个东西,是通过编译器以及无数次痛苦的调试才能掌握的技术,我不认为有什么灵丹妙药,可以让人通过每天半小时,或者每天7分钟能学会的。每天7分钟,让你成为编程高手,这就是扯淡啊!我觉得别说每天7分钟,我觉得0基础程序员的话,每天7小时,也得一年才能入门。


说实在的,我倒是看到过不少音频节目是这样宣传的,比如说,每天5分钟,让你实现财务自由,但是,如果要想听每天5分钟,得先付出199元钱,然后才能财务自由。然后,很多人就付了199元,结果让人家主播财务自由了。大家可能觉得我吃不到葡萄说葡萄酸,讲真,你们猜对了!我真的想不懂,真的有人觉得每天听5分钟,就能财务自由,这是不太可能的。我觉得任何有价值的技术,包括忽悠这门技术,绝对不会是每天5分钟,听半年就学会的,一定需要不停的锻炼才可以掌握。编程也是这样,可能相对于其它的一些技巧,编程算不上特别复杂的技术,但是,大家也不要认为通过听电台就能学会编程。如果想学编程,听我这个电台是不合适的,所以,大家也不要总是留言说要干货,我这里没有干货,只有吐槽。不要幻想通过听广播学编程,学编程只有一条路,就是打开你的电脑,打开编译器,要么阅读别人的源代码,要么调试自己的项目。其它的途径,都是效果很差的。


开始吐槽了,当浏览器拿到了HTML文档,需要解析,解析的最终结果是一棵二叉树,二叉树的每一个节点都是一个文档的结构。这棵树的名字叫解析树或者语法树。这两种叫法我都看到过,但是说的都是一回事儿,在数据结构的教材上,叫语法树的时候比较多。那什么是语法呢?这个语法和我们英语课上学的语法是不同的。我要开始讲编译原理的知识了。而且我没法用通俗易懂的语言讲编译原理的知识。我慢慢的发现,如果要讲清楚,或者说我自己思考清楚一件事情,唯一的方法就是从头讲起,比如说,我们很难让一个没有学过电路知识的人明白,一个电路是怎么工作的,如果真的要讲清楚,只有一个办法,就是从晶体管那个地方开始讲,否则,肯定是不清楚的。但是,如果从晶体管开始讲,那时间可就太长了,得讲好几天。还有一种办法,虽然讲不清楚,但是可以让听的人或者看的人,觉得自己清楚了,这种方法就是基本上所有的科普读物所采用的方法,类比的方法。这种方法其实只是好像清楚了,实际上根本没清楚。


我可以举个例子,比如说,现在的手机都是32G存储或者128G存储,但是,手机里还有个存储器,也就是内存,是2G或者4G。一方面是一个32G或者128G的存储器,另一方面是一个2G或者4G的内存,如果想对一个没有搞过计算机的人讲清楚这两个东西,是非常困难的,甚至,可能就是徒劳的。因此,我听过一个节目,就是试图讲清楚这两个东西是不同的。最后,不得不用了类比的方法,这也是几乎所有的科普书籍所采用的方法,类比。他把手机内存比做超市里的货架,把外存比作超市的仓库,然后说这两个是不同的,为什么不同呢?讲不清楚么,最后,不懂的人觉得手机里有一个超市的货架,还有一个超市的仓库,至于到底有什么不同,也就不去深究了。毕竟,手机里有有两个东西,一个是货架,一个仓库,这代表着两种存储器。这算不算是个方法呢?说实在的,我觉得也算是个方法,因为我也很喜欢看科普的读物,对个外行来说,知道一点总比啥也不知道强。但是,类比实际上对理解真正的原理没太多的帮助。


我得再狡辩一下,我对科普并没有任何批评的意思,因为面向大众的科普,比如说,我如果想了解黑洞,想了解微观可逆性,只能看面向我这种档次的人的科普,也就是大众科普。大众科普其实就是在非常浅显的面向大量的读者;如果读博士了,就是在很深的层次上面向很少的人,博士生的论文,肯定不会出现很业余的类比,人家都是直接上数学公式。


所以,我打算以不用类比的方法开始讲编译原理的知识。即使这样可能会有点枯燥无味。因为我的观点是这样的,计算机这个东西,总共发展了就这么多年,不用类比的方法,我觉得大部分人还是能理解计算机的美丽的,尤其是程序员。就像我们看到一个美女,就知道她是美女。根本不用类比的方法,说她像西施或者像仙女,比如说,汤唯就是汤唯,高圆圆就是高圆圆,不用类比,我也觉得她们是美女。编译原理就是编译原理,不用类比,我觉得也可以领略编译原理的美。


首先,讲词法分析器,这个玩意也叫词法扫描器。当我们拿到一个HTML文档的时候,尤其是可以把语法高亮显示的时候,我们一眼就可以看出,不同的颜色,代编不同的含义。比如说有HTML的各种标签,显示的颜色就和其他的文字的颜色不同。然后,我上一期节目提到的那几个引擎webkit开始进入语法分析阶段,在术语上把单词叫做token,也有翻译为令牌,记号等等,在这里我把这个token翻译为单词,因为HTML文件分解后的东西,的确非常像一个一个的单词。然后,用相对简单的规则,就把从网络上下载下来的文本文件,分解为一串一串的单词流,这样,为了下一步的词法分析做准备。很可惜,这一段话仍然是白话,对计算机来说,这段话就是废话,他根本不懂。为了让计算机能理解上面我说的这段话,需要用计算机才能理解的语言。比如说,正则表达式就是一个比较理想的选择。


如果是程序员,我就默认大家知道啥是正则表达式,程序员经常用正则表达式匹配字符串等等,我也默认大家已经是个正则表达式高手了。尤其是大家可以在脑子里默念一下正则表达式的规则,两个正则表达式的并,两个正则表达式的连接以及一个正则表达式的克林闭包,这三个表达式在心里想几秒钟,其实用这三种正则表达式的运算,就可以把文件给分离的差不多了。尤其是能完全用正则表达式表达的语言,比如说C#就是,这种语言也被称为正则语言。但是,很可惜,我们这个音频里讲的是怎么处理HTML和CSS,恰好,HTML和JSON都不是正则语言,那怎么搞呢?我当然是搞不定的!但是,因为这个世界上有不少默默无闻的科学家,数学家,他们能搞定。既然讲到了数学家,我个人心里还是挺佩服数学家的,因此,我本科在理学院中混,算是学了4年数学吧,当然,这句话说的有点儿心虚,因为我逃了好多课,然后就找不到工作了,幸亏学数学的时候,老师未雨绸缪,知道天天让我们学微分几何,偏微分方程或者拓扑学,是找不到工作的,所以,我们系允许去计算机那里学点编程,以防止毕业以后因为找不到工作,给饿死了,所以我学了一些C++编程,然后我就当了程序员,来填饱肚子。


因为我们所熟知的一些语言,是有递归的性质,比如数学运算也有递归的性质,尤其是我们小学三四年级所进行的加减乘除四则运算,有好几个括号的那种运算,实际上是可以通过递归的方法来算出来。可惜的是,我们三四年级的时候,根本不懂这个。在递归的语言中,我们没法用DFA来解释,因为,DFA只有有限个状态,是没办法追溯无限递归的。比如说,我们写网页的时候,可以在一个列表里包含另一个列表,在另一个列表里又包含另一个列表,理论上,只要足够闲的蛋疼,我们可以无穷的包含下去,这个时候,就是我前面所说的,使用正则表达式,正则语言还有DFA工具,都搞不定的状态。幸亏,有一些数学家,又搞出来了一个语言,他的名字叫上下文无关语言。英文叫做Context-Free Grammar。依靠这个工具,我们才可以愉快的上网看网页,当然了,绝大部分的网友根本不用关心上下文无关语言到底是个什么东西,这也是科学的神奇之处。我们不用懂空调的工作原理,只需要按几个按钮,就可以使用空调了。


开源的伟大之处在于,并不是所有的东西都要从零开始,因为有了各种解析器了,毕竟创建一个解析器并不容易,尤其是能兼顾效率和精确的解析器更是难上加难。webkit使用了两种非常著名的解析器生成器,第一个叫Flex用来创建词法分析器,还有Bison用来创建解析器。他们也有其他的名字,比如叫Lex或者Yacc,不过,大家很少能用到这两个东西,除非真的去做浏览器或者做编译器。Flex处理的是包含编辑的正则表达式定义的文件,Bison处理的则是BNF格式的语法规则。接下来再说说HTML解析器,很可惜的是,常规的解析器都不能解析HTML,为什么呢?


为什么呢?HTML和XML差不多样子,解析XML的解析器多如牛毛,为啥解析HTML的解析器就寥若晨星呢?那是因为XML是一种非常严格的语言,HTML是一种非常宽容的语言。简单来说,XML不允许存在一点错误,HTML则是差不多就行。HTML可以随便写写,比如说,HTML可以随便省略一些标记,也可以省略开始或者结束的标记,反正HTML简直就是最宽容的语言,写错了问题不大,只要是能显示出来,前端的同学也懒得去修改,即使知道这玩意有错误。这样的后果就是,HTML的解析器能容忍错误,不能说一个错误,浏览器给崩溃了。一个错误导致崩溃,大部分编程语言都是这样的,比如说闪退啥的,很可能是一个小错误。但是由于HTML处理的网页是各种各样水平的程序员写的,网页里面的错误肯定是五花八门,如果一个错误就让浏览器崩溃,那就不用上网了。所以,这也就是HTML的难点所在,得和错误共存。稍微总结一下开发一个浏览器引擎的难点,正是因为这些难点,所以才导致世界上可能有几百种浏览器,但是核心的引擎,就那么三四种:


1. HTML语言宽容的本质。所以,我们程序员真的要感谢那么多人默默的付出,才让我们写的那些垃圾代码能显示出来,即使我们写的HTML像一坨屎,浏览器的引擎也默默的帮我们处理成我们想要的样子。

2. 浏览器能处理很久远的网页,即使一些无效的HTML标签,他也知道什么时候该忽略掉。尤其是浏览器大战的年代,各家浏览器搞创新,每家都有一些独特的创新,可以显示在自己的浏览器上,那些创新尽量不让他们正确的显示在别人家的浏览器上。嗯,人类这个物种,有时候挺恶心的。

3. 解析的过程需要不停的反复。为什么这么说呢,举例来说,如果在HTML中,有可以写入的选项,这个时候,因为用户要不停的写入内容,因此,这个浏览器要反复的解析,写入一点,解析一点。这也是非常棘手的一个问题。


这三个难点是表面上就能看出来的,具体还有什么更多的难点,我想,也许只有开发引擎的程序员能体会了。现在已经是HTML5的年代了,HTML5规范则详细的描述了解析算法。HTML5的解析算法主要分为两个阶段,第一个阶段叫标记化阶段,第二个阶段则是构建树的阶段。如果大家有兴趣的话,可以google搜索关键字HTML 5 Parsing,就可以找到链接。或者,你可以输入HTML在百度搜索引擎里,你可以找到相关的培训班,然后交钱参加培训班以后,可以去问老师HTML5的解析算法是什么。这一点,不得不服百度呢,我一搜HTML,先给我搞了四个网站开发培训班的广告。这一点,百度的人性化特别好。


如果你不想搜索的话,可以关注我的微信公众号——软件那些事儿——我把HTML 5 parsing的链接放在这里,还是挺有趣的。


https://www.w3.org/TR/2011/WD-html5-20110113/parsing.html


我简要的把这个链接的意思概括一下,不过呢,还是建议大家去读一下,还算有趣,单词量也不大。技术文章的单词量不大,这一点让我这个英语渣感觉有点幸福。竟然抱着一本字典,能读懂。


第一个阶段叫标记化阶段,讲输入的内容,也就是HTML文件,解析成多个HTML的关键字,也就是标记。这些标记包括HTML的起始标记,也就是这个东西,结束标示,还有属性的名称,以及属性的值,这些都是在第一个阶段完成的。 


第二个阶段叫构建树的过程,由第一个阶段生成的那些标记,传递给构造器,然后反复执行,直到第一个阶段生成的标记都完成了,这是个循环的过程。最后,生成一刻HTML解析树。这个流程呢,我把解析图放在这里,这个图也是在w3那个链接里的,如果有人去读一下原文,就不用看这个图了,这个图解释了整个过程。HTML的解析流程。



HTML解析流程


好吧,这一期就到这里,下一期继续讲浏览器的工作原理。

...more
View all episodesView all episodes
Download on the App Store

软件那些事儿By 刘延栋

  • 4.9
  • 4.9
  • 4.9
  • 4.9
  • 4.9

4.9

51 ratings


More shows like 软件那些事儿

View all
一席 by 一席

一席

104 Listeners

枫言枫语 by 枫影JustinYan、自力hzlzh

枫言枫语

24 Listeners

津津乐道 by DAO

津津乐道

119 Listeners

疯投圈 by 黄海、Rio

疯投圈

109 Listeners

声东击西 by ETW Studio

声东击西

314 Listeners

科技乱炖 by DAO

科技乱炖

24 Listeners

忽左忽右 by JustPod

忽左忽右

440 Listeners

What's Next|科技早知道 by 声动活泼

What's Next|科技早知道

176 Listeners

硅谷101 by 硅谷101

硅谷101

184 Listeners

硅谷101|中国版 by 泓君Jane

硅谷101|中国版

53 Listeners

商业就是这样 by 商业就是这样

商业就是这样

272 Listeners

半拿铁 | 商业沉浮录 by 潇磊&刘飞

半拿铁 | 商业沉浮录

291 Listeners

脑放电波 by 托马斯白

脑放电波

10 Listeners

硬地骇客 by skoowoo

硬地骇客

17 Listeners

42章经 by KaiQu

42章经

9 Listeners