2019独角兽企业重金招聘Python工程师标准>>>

这个系列博客主要为那些对 Go 基本知识已经有一定了解,又希望对其内部细节进行更深一步地探索的人准备的。今天这一篇主要分析 Go 源代码的基本结构以及 Go 编译器的某些内部细节。读完这篇博客后,你会得到下面三个问题的答案:

1. Go 源代码结构是什么样子的?
2. Go 编译器是如何工作的?
3. Go 语言中的结点树的基本结构是什么样的?

让我们开始吧

每当开始学习一门新编程语言的时候,你总是可以找到大量的 “hello world” 教程、新手指南或者关于语言的主要概念、语法甚至标准库的文档。然而,当你想找一些介绍得更加深入的资料,比如语言运行时分配的数据结构在内存中的布局,或者调用一个内置函数时到底生成了什么样的汇编代码,你就会发现这并非易事。显然,这些问题的答案都藏在源代码中。但是,以我的个人经验来看,你很可能花费数小时在源代码中摸索却最终一无所获。

我并不是打算装得自己什么都懂,也没有打算介绍得面面俱到。而是希望可以帮助你去探索 Go 语言的源代码。

在我们开始之前,我们需要自己有一份 Go 源代码的拷贝。要获得它的源代码非常容易,只需要执行如下代码:

Shell

1

git clone https://github.com/golang/go

请注意,这份代码的主分支是在不断改进中的,我在这个博客中使用的是 release-brach.go1.4 这个分支。

搞清楚项目结构

如果你看一下 Go 仓库的 /src 文件夹,你会看到很多文件夹。其中,大部分文件夹都是 Go 标准库的源文件。该项目使用标准命名规则,所以每一个包(pakage)都在一个独立的文件夹中,而且这个文件夹的名称与包名称相同。除了标准库以外,该目录中还有很多其它的东西。就我各人看法,其中最有用的文件中主要有:

文件夹 描述
/src/cmd/ 包含不同的命令行工具。
/src/cmd/go/ 该目录下包含一个 Go 工具的源代码文件。此工具用于下载编译 Go 的源文件,以及安装 Go 语言的包。在完成上述工作中,它会收集所有源文件并调用 Go 链接器与编译器。
/src/cmd/dist/ 此目录下也包含一个工具。此工具用于编译生成所有其它命令行工具。同时,它会由标准库生成所有的包。要想搞明白每个工具或者包到底用到了哪些库,你就需要分析这里的源代码。
/src/cmd/gc/ 包含 Go 编译器与系统架构无关的部分。
/src/cmd/ld/ 包含 Go 链接器与系统架构无关的部分。与系统架构相关的部分被放在以 l 开头的目录中。这些目录的命名规则与编译器部分的命名规则相同。
/src/cmd/5a/, 6a, 8a, and 9a 此目录下存放针对不同架构的 Go 语言汇编编译器。Go 汇编程序的语言并不能一一对应地映射到下层机器的汇编语言。不过,对于每种不同的架构都存在一个将 Go 汇编程序翻译为机器汇编程序的编译器。你可以这这里找到更多内容。
/src/lib9/, /src/libbio, /src/liblink 在编译器、链接器、以及运行时中用到的不同库。
/src/runtime/ 这部分包含了 Go 语言最重要的包,所有程序都默认导入这些包。其中包括所有的运行时功能,比如内存管理、垃圾回收、Go 协程(goroutine)等等。

Go 编译器内部机制

正如提到的那样,Go 编译器中与系统结构无关的部分被放在 /src/cmd/gc 目录下。其入口点在 lex.c 文件中。除了一些共同的部分,比如命令行参数解析,编译器还要完成如下的工作:

1. 初始化一些通用数据结构。

2. 遍历提供的所有 Go 源代码文件,并针对每个文件调用 yyparse 方法。该方法会完成真正的语法分析。Go 编译器使用 Bison 作为程序分析生成器。语法描述存储在文件 go.y 中(后面我会提供详细的说明)。最终,这一步会生成一个完整的分析树,其中每个结点表示编译后程序的一个元素。

3. 递规地遍历生成的树,并做出一定修改,例如为那些应当隐式定义的节点指定类型信息、重写在运行时包中传递给函数的某些语言元素——如类型转换,以及其它一些工作。

4. 语法解析树处理完成后,再执行真正的编译,将结点翻译成汇编代码。

5. 在磁盘上创建目标文件,并将翻译生成的汇编代码以及一些额外的数据结构,如符号表等,写入目标文件中。

深入 Go 语言语法

现在让我们再进一步。 go.y 文件中包含了语言的语法规则,所以这个文件是一个探索 Go 编译器的很好突破口,也是我们理解语言语法规则的关键。这个文件主要包括如下几部分:

1

2

3

4

5

6

xfndcl:

LFUNC fndcl fnbody

fndcl:

sym '(' oarg_type_list_ocomma ')' fnres

| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres

这个声明中定义了 xfndcl 以及 fundcl 两个结点。 fundcl 结点可以有以下两种形式。第一种对应于如下的语法结构:

1

somefunction(x int, y int) int

其第二种形式对应于下面这种语法结构:

1

(t *SomeType) somefunction(x int, y int) int.

xfndcl 结点中包含存储于常量 LFUNC 中的关键字 func,以及其后的 fndcl 与 fnbody 结点。

Bison(或者说 Yacc)语法一个十份重要的特征是,它允许将任意 C 代码放在结点定义之后。每当在源代码文件中找到匹配该结点定义的部分的时候,相应的 C 代码就会执行。这里,我们把最终结果结点定义为 $ $,其子结点分别为 $1,$2……

通过一个例子更加容易理解。注意下面这段简化后的代码:

1

2

3

4

5

6

7

8

9

10

11

12

fndcl:

sym '(' oarg_type_list_ocomma ')' fnres

{

t = nod(OTFUNC, N, N);

t->list = $3;

t->rlist = $5;

$$ = nod(ODCLFUNC, N, N);

$$->nname = newname($1);

$$->nname->ntype = t;

declare($$->nname, PFUNC);

}

| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres

首先,我们创建了一个新结点,该结点包含函数声明的类型信息。同时,此结点的参数列表引用了结点 $3,结果列表引用了结点 $5。随后创建了结果结点 $ $。在结果结点中存储了函数的名称和以及其类型结点。 正如你所看到的那样,在 go.y 文件中的定义与结点结构之间可能没有直接的对应关系。

如何理解结点

是时候看一下结点到底是什么东西了。首先,结点是一个结构体(你可以在这里找到其定义)。这个结构体包含了大量的属性,这是因为它需要各种不同类型的结点类型,而不同类别的结点又有着不同的属性。下面列出了一些我认为比较重要一些属性:

结点结构体域 描述
op 结点操作符。每个结点都有这个域。它将不同类型的结点区分开来。在前面的例子中,该域分别是 OTFUNC(操作类型函数)与 ODCLFUNC(操作声明函数)。
type 该域引用一个包含类型信息的结构体(有些结点没有类型信息,例如,像 if、switch、for 之类的控制流语句)。
val 在表示常量的结点中,该域存储常量值。

到目前为止,你已经明白了结点树的基本结构了,你可以去运用一下这些知识。在接下来的博文中,我们会用一个简单的 Go 应用作为实例来分析 Go 编译器到底是如何编译代码的。

转载于:https://my.oschina.net/u/3626804/blog/1823797

Go语言内幕(1):主要概念与项目结构相关推荐

  1. cocos2d-x 学习笔记(2)cocos2d-x重要概念,项目结构及 CCDirector 导演控件

    cocos2d-x 学习笔记(2)cocos2d-x重要概念及项目结构 在cocos2d引擎中,有几个概念,分别是导演,场景,布景和人物角色. 导演(CCDirector)在cocos2d-x引擎中, ...

  2. 学会了C语言你可以独立开发这些项目。。。。

    C语言可以做什么? 从最简单的.最熟悉的说起吧,毕竟我们在学校学习的时候,老师几乎都会让我们去开发: 一.C语言可以实现一些常见的应用 以下几个几乎是我们学习C语言到一定阶段之后必开发的一个小项目了, ...

  3. c语言21个入门练手项目,初学C语言没有项目练手怎么行,这17个小项目收下不谢...

    C语言是咱们大多数人的编程入门语言,对其也再熟悉不过了,不过不少初学者在学习的过程当中不免会出现迷茫,好比:不知道C语言能够开发哪些项目,能够应用在哪些实际的开发中--,这些迷茫也致使了咱们在学习的过 ...

  4. 数据仓库 — 01_项目需求分析与技术选型(数仓概念、项目需求及架构设计、数据生成模块格式要求)

    文章目录 1 数据仓库的概念 2 项目需求分析 3 项目框架 3.1 技术选型 3.2 系统数据流程设计 3.3 框架版本选型 3.4 服务器选型 3.5 集群资源规划设计 3.5.1 集群规模计算 ...

  5. c语言不同指令意识,C语言必须理清的概念1

    当今的社会的人或多或少都有点惰性和急功近利,在一开始学习编程的时候不喜欢阅读那些枯燥的文字,喜欢直接去阅读代码,渐渐地,发现一个问题,那就是编程时经常会犯一些低级错误,总结一下,这些错误源自于自己对这 ...

  6. C语言基础-函数的概念

    c语言基础-函数的概念 一.函数的定义: 将常用的整体实现某个功能的代码块封装起来,用到的时候可以直接调用 函数也是模块化编程的一种体现 二.函数的定义格式: 函数类型 函数名(形参类型 形参名,形参 ...

  7. java语言保留结构和联合_Java 语言中取消了联合概念,保留了结构概念。( )_学小易找答案...

    [单选题]Graves病最严重的临床表现是 [判断题]Java 语言中取消了联合概念,保留了结构概念.( ) [单选题]下列关于子类继承父类的成员的描述中,错误的是 . [多选题]冯.诺依曼机确立计算 ...

  8. 大数据项目之电商数仓、数据仓库概念、项目需求及架构设计

    文章目录 1.数据仓库概念 2. 项目需求及架构设计 2.1 项目需求分析 2.1.1 采集平台 2.1.2 离线需求 2.1.3 实时需求 2.1.4 思考题 2.2 项目框架 2.2.1 技术选型 ...

  9. 基于C语言编程的职工工资管理系统项目的设计与开发

    文章目录 基于C语言编程的职工工资管理系统项目的设计与开发 一.需求分析 二.项目环境 2.1.项目创建过程 2.2.向项目添加头文件与源文件 三.职工工资管理系统模块功能 3.1.系统总体设计框图 ...

  10. 2022-07-25:xiu是用rust语言编写的流媒体服务器软件项目。k8s安装xiu,drone文件如何写?

    2022-07-25:xiu是用rust语言编写的流媒体服务器软件项目.k8s安装xiu,drone文件如何写? 答案2022-07-25: 云原生环境不可能完全一样,只能做参考. 我采用的是dron ...

最新文章

  1. Ubuntu 安装LAMP ...
  2. js添加多个子节点_DOM节点
  3. Highcharts隐藏网格线
  4. Java 之 合成模式
  5. rocketMq发送事务消息
  6. 从进程组、会话、终端的概念深入理解守护进程
  7. 怎么抽象mysql数据库_一个用于mysql的数据库抽象层函数库
  8. 05-01 docker 介绍
  9. TurboFan-Sea of Nodes概念讲解
  10. Windows系统下的socket编程
  11. java flex blazeds_使用BlazeDS实现Java和Flex通信
  12. EasyCHM chm文件制作过程及软件附软件下载地址
  13. Python游戏——Pong
  14. 如何提高BT的下载速度?
  15. 南阳oj 题目127 星际之门(一)
  16. Linux内核配置文件
  17. scala 惰性函数
  18. 美女--男人的Vitamin C
  19. 按键,触摸屏流程分析
  20. 怪物之心无法触发_异度之刃2解锁稀有异刃力男支线怪物之心BOSS位置攻略

热门文章

  1. mod java 求余_java中求余%与取模floorMod的区别
  2. 242.有效的字母异位词
  3. Juggling Life and Learning
  4. mysql多线程复制binlog_MySQL 不同复制模式下,如何忽略binlog事件
  5. 凸优化第二章凸集 2.1仿射集合和凸集
  6. hadoop2.7.3+hbase1.2.5配合起来使用的一个小问题,备注一下
  7. h3c交换机查看电源和风扇模块序列号
  8. java web xml配置详解_Java Servlet web xml 配置详解
  9. js 根据某属性取出数组中对应的对象
  10. how to set up github blog