编译原理之美
宫文学
北京原点代码 CEO
46197 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
编译原理 · 期中考试周 (1讲)
编译原理之美
15
15
1.0x
00:00/00:00
登录|注册

21 | 运行时机制:突破现象看本质,透过语法看运行时

程序运行的过程
程序运行的环境
程序运行的环境和过程

该思维导图由 AI 生成,仅供参考

编译器的任务,是要生成能够在计算机上运行的代码,但要生成代码,我们必须对程序的运行环境和运行机制有比较透彻的了解。
你要知道,大型的、复杂一点儿的系统,比如像淘宝一样的电商系统、搜索引擎系统等等,都存在一些技术任务,是需要你深入了解底层机制才能解决的。比如淘宝的基础技术团队就曾经贡献过,Java 虚拟机即时编译功能中的一个补丁。
这反映出掌握底层技术能力的重要性,所以,如果你想进阶成为这个层次的工程师,不能只学学上层的语法,而是要把计算机语言从上层的语法到底层的运行机制都了解透彻。
本节课,我会对计算机程序如何运行,做一个解密,话题分成两个部分:
1. 了解程序运行的环境,包括 CPU、内存和操作系统,探知它们跟程序到底有什么关系。
2. 了解程序运行的过程。比如,一个程序是怎么跑起来的,代码是怎样执行和跳转的,又是如何管理内存的。
首先,我们先来了解一下程序运行的环境。

程序运行的环境

程序运行的过程中,主要是跟两个硬件(CPU 和内存)以及一个软件(操作系统)打交道。
本质上,我们的程序只关心 CPU 和内存这两个硬件。你可能说:“不对啊,计算机还有其他硬件,比如显示器和硬盘啊。”但对我们的程序来说,操作这些硬件,也只是执行某些特定的驱动代码,跟执行其他代码并没有什么差异。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

程序的运行机制是计算机领域中的重要知识,对于想要深入了解底层机制的工程师来说尤为重要。本文从程序运行的环境和过程两个方面展开讲解。首先,程序运行的环境主要涉及CPU、内存和操作系统,其中CPU的寄存器和高速缓存对程序执行机制和优化有着密切关系。其次,程序运行的过程包括程序与硬件和操作系统的互动,以及程序需要遵守的约定。文章通过介绍CPU和内存的关系、内存的管理和使用策略,以及程序与操作系统的微妙关系,深入解析了程序运行的环境和过程。对于想要深入了解程序运行机制的读者来说,本文提供了宝贵的知识和视角。 文章首先介绍了程序运行的细节,包括程序加载到内存、活动记录和栈桢的概念,以及栈桢的设计和作用。接着从全局角度看整个运行过程,讲解了代码区存储代码、指令执行过程和栈桢的释放。最后,总结了程序运行的环境和过程,强调了CPU、内存和操作系统的重要性,以及程序执行的顺序和栈桢的动态创建和释放。 总的来说,本文通过深入浅出的方式介绍了程序运行的环境和过程,为读者提供了全面的知识和视角。读者可以从中了解程序与CPU、内存和操作系统的交互,以及栈桢的设计和作用,从而更好地理解程序的运行机制。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编译原理之美》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(17)

  • 最新
  • 精选
  • 置顶
    宫老师讲的真好!一直以来,都有一种强烈的信念!要学编译原理,要学这种在日新月异的信息技术领域里“亘古不变”的技术原理,以不变应万变。上半年学习了极客时间出的“深入浅出计算机组成原理”,在这一节正好排上用场,对程序运行时机制有了更深入的理解。

    作者回复: 对的。 计算机的组成原理、编译器的基本架构等等内容,其实半个世纪也没有太大的变化,是比较稳定的。并且,真正做一些深入的工作的时候,这些知识仍然非常有价值。

    2019-10-25
    10
  • Gopher
    写的真好,一下子就听懂了( ̄∀ ̄) 内存布局: 指令数据,分而治之; 自下而上,由静至动; 栉比鳞次,序从中来。

    作者回复: 你不光代码写得好,文采也很好。 新东方的三驾马车之一的王强说到,好的代码就像诗歌一样优美。写完代码要站在远处欣赏一下 :-)

    2019-10-09
    12
  • 刘強
    文章里说操作系统还会自动维护栈。 但我觉得栈的维护是有程序或者编译器来维护的。操作系统只是给程序(进程)分配了栈的起始地址而已,剩下的进栈和出栈操作,都是预先编译好的push和pop指令来完成的。不知理解的对不对。

    作者回复: 栈这个东西,如果深入看一下,其实涉及得还挺多的。 操作系统的介入,主要原因是内存管理。因为你的程序所使用的内存,并不是物理内存,都是虚拟出来的。在你使用栈的时候,操作系统要帮你把逻辑的内存映射到物理的内存上去。只不过这个过程对程序是透明的。是CPU和操作系统之间协作完成的。 具体细节是:当你push一个新的变量到栈里的时候,如果超出了当前可用的物理内存,CPU会产生一个page fault(缺页错误),操作系统这个时候介入,调度一页新的物理内存过来。 你可以查看Intel的手册,看看push指令的说明,里面有介绍。关于page fault,你可以参考https://en.wikipedia.org/wiki/Page_fault 当然,如果你的程序直接运行在裸机上,没有使用操作系统,那就没有操作系统什么事了。直接在裸机上编程叫做bare metal programming,在有些领域很有用。 总结起来,栈的管理,与CPU、内存和你的代码都有关。

    2019-11-05
    2
    8
  • wolfie
    “控制链接”和“上一帧的%rbp的值”两者分列返回地址的两边,它们是怎样的关系?

    作者回复: 控制链接通常是指向上一级作用域的函数对应的栈桢,用于查找上一级作用域中变量的值,它不一定是前一个栈桢。 比如,有两个函数,foo和bar,他们都是在全局作用域中声明的,而在foo里调用了bar。那么,bar的控制链接是指向全局作用域(的栈桢),可以访问全局变量,而不是指向foo(的栈桢)。

    2020-12-13
    2
    5
  • sunbird
    一节一节,看的根本停不下来。看过才发现,自己根本不会计算机。比MOOC上那些顶尖名牌大学的教授讲的好太多了!!! 有几个问题还是想的不太明白,麻烦宫老师在***有时间的时候***帮忙解惑一下,不胜感激。 1.这个栈是操作系统维护的栈吗? 2.这个栈和数据结构中的栈有什么区别? 3.这个栈是每个程序一个,还是所有的程序共用一个? 4.这个栈是和进程绑定的吗? 5.多线程的时候,每个线程都有自己的方法栈,和这个栈是一个吗?如果不是,他们之间有什么区别? 6.栈为什么是高地址向低地址的延伸?栈顶在高地址还是低地址?栈寄存器指向的是栈顶吗?

    作者回复: 谬赞了! 回答一下你的问题: 1.是的,栈是操作系统维护的,所以会降低程序维护内存的负担。但缺点是函数退出后栈里的内存就自动收回,这点是跟堆的区别。 2.数据结构中的栈是一个普遍的概念,可以用在很多地方。内存管理是栈的一个具体应用。 3.在现在操作系统中,是每个线程一个栈。如果一个进程里面有多个线程,那就有多个栈。 另外,如果程序是支持协程的,有些实现机制也会给协程提供单独的栈,用来维护协程的状态。但协程的栈一般是由程序的运行时或库来支持的,而不是由操作系统来维护的。 4.同上,栈是跟线程绑定的。 5.同上,每个线程有自己单独的栈。 6.栈从高地址到低地址延伸,这个时候栈顶是低地址。对于这个问题,没有什么特别的道理,只是一个设计决定而已。并且,并不是所有的架构都这样设计,有的设计恰好是反向的。栈寄存器的设计也是跟具体架构相关的。对于比较新的x86架构来说,我们一般只需要一个指向栈顶的栈寄存器即可。但,由于调用约定通常要向下兼容,所以生成的程序通常也要用到一个寄存器指向栈底。 对于上述问题,我想额外指出几点: 1.上述具体实现,跟CPU架构和操作系统二者都有关系,不是死的。编译器在实现的时候,要生成针对具体架构的代码。不要认为这些都是一成不变的。 2.在《编译原理实战课》中,对于运行时有更多的介绍,比如栈和线程的关系,协程的机制等等。你可以参考。

    2020-11-21
    5
  • 吴小智
    太赞了,老师一文道破计算机专业本科生四年需要学的 70% 专业领域的知识,底层知识扎实很重要。

    作者回复: 谬赞了。 不过学习编译原理,确实会用到计算机学科的多方面的知识,如形式语言、数据结构和算法、计算机组成、操作系统等。这也不奇怪,因为你要让一门语言跑起来,就是要涉及方方面面。

    2020-01-08
    5
  • 曾经瘦过
    使用的java 语言。java是运行在jvm虚拟机里面的,是便以为jvm所需的机器码 基本的过程和这个是差不多的。看了这一篇专栏之后 发现基础知识的用处真的很多,操作系统 组成原理 用处真多。

    作者回复: 对呀。既然学计算机嘛,就搞到根本上去,心里会比较踏实。而且说实话,基础原理并不多,也不易变。反倒上层各种类库、框架,层出不穷,天天更新。这两头哪边学起来更辛苦,真不一定!

    2019-10-09
    4
  • westfall
    像js这种非编译性语言又是怎么跑起来的呢?

    作者回复: 通常,是要通过一个解释器来执行。而解释器执行的一般是中间代码,比如java和python的字节码。 还有一种情况,就是把js快速编译成机器码,然后执行。因为是快速编译,所以不够优化,代码体积也比较大,但好处是编译时间很短,可以马上跑起来。如果一段代码经常被执行,就意味着这个热代码,那么就进行优化编译,产生更好的代码。这就是早期JavaScript引擎做编译的流程。但最新的v8引擎,采用的是一个解释器(ignition)加一个优化编译器(turbofan)的结构,看上去跟JVM很像,都是一个解释器加一个优化编译器。 我在第二季《编译原理实战课》的运行环境这一讲,增加了栈机和寄存器机这两种虚拟机的介绍。另外,也有对v8编译器的分析。你可以去看看。

    2020-05-21
    3
  • 阿辉
    老师说的真好,通过老师的讲解,运行时的机制主要是操作作系统系统级别,那编译器起的主要作用是?我们通常所说的runtime到底指的是什么?和vm,engine之类的有啥区别啊?

    作者回复: 当我们谈论一门语言的时候,通常三个组成部分是必不可少的:编译器、运行时和标准库。此外,一般还要加上一些工具,比如模块管理工具、调试工具等等。 运行时,顾名思义,就是你编写的程序在运行的时候所需要的那些软件。这个定义比较泛,所以在Windows系统中,把一些标准库也称作运行时,因为确实是在Windows上运行软件所必须的。 而虚拟机,则是比较复杂的那种运行时,典型代表是HotSpot虚拟机和V8虚拟机,它们为程序的运行、内存管理、并发等都提供了支持,程序可以无缝地在解释执行和运行本地代码之间切换、适时地启动自动编译机制等。 你可以看出,这种级别的运行时和操作系统的功能有很多相似之处。比如,要做好内存的管理,其实你需要对操作系统的内存管理也了解得比较深,并使它们二者良好的配合。并发也是如此。

    2021-04-15
    2
  • 微秒
    老师你好,想问下你在文中说的静态数据期的地址在编译时就确定,这里的地址是虚拟地址,还是实际内存地址。

    作者回复: 应用程序能够看到的地址,都是操作系统虚拟出来的,除非你直接运行在裸机上。

    2020-03-16
    2
收起评论
显示
设置
留言
17
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部