Go语言内幕(1):主要概念与项目结构
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):主要概念与项目结构相关推荐
- cocos2d-x 学习笔记(2)cocos2d-x重要概念,项目结构及 CCDirector 导演控件
cocos2d-x 学习笔记(2)cocos2d-x重要概念及项目结构 在cocos2d引擎中,有几个概念,分别是导演,场景,布景和人物角色. 导演(CCDirector)在cocos2d-x引擎中, ...
- 学会了C语言你可以独立开发这些项目。。。。
C语言可以做什么? 从最简单的.最熟悉的说起吧,毕竟我们在学校学习的时候,老师几乎都会让我们去开发: 一.C语言可以实现一些常见的应用 以下几个几乎是我们学习C语言到一定阶段之后必开发的一个小项目了, ...
- c语言21个入门练手项目,初学C语言没有项目练手怎么行,这17个小项目收下不谢...
C语言是咱们大多数人的编程入门语言,对其也再熟悉不过了,不过不少初学者在学习的过程当中不免会出现迷茫,好比:不知道C语言能够开发哪些项目,能够应用在哪些实际的开发中--,这些迷茫也致使了咱们在学习的过 ...
- 数据仓库 — 01_项目需求分析与技术选型(数仓概念、项目需求及架构设计、数据生成模块格式要求)
文章目录 1 数据仓库的概念 2 项目需求分析 3 项目框架 3.1 技术选型 3.2 系统数据流程设计 3.3 框架版本选型 3.4 服务器选型 3.5 集群资源规划设计 3.5.1 集群规模计算 ...
- c语言不同指令意识,C语言必须理清的概念1
当今的社会的人或多或少都有点惰性和急功近利,在一开始学习编程的时候不喜欢阅读那些枯燥的文字,喜欢直接去阅读代码,渐渐地,发现一个问题,那就是编程时经常会犯一些低级错误,总结一下,这些错误源自于自己对这 ...
- C语言基础-函数的概念
c语言基础-函数的概念 一.函数的定义: 将常用的整体实现某个功能的代码块封装起来,用到的时候可以直接调用 函数也是模块化编程的一种体现 二.函数的定义格式: 函数类型 函数名(形参类型 形参名,形参 ...
- java语言保留结构和联合_Java 语言中取消了联合概念,保留了结构概念。( )_学小易找答案...
[单选题]Graves病最严重的临床表现是 [判断题]Java 语言中取消了联合概念,保留了结构概念.( ) [单选题]下列关于子类继承父类的成员的描述中,错误的是 . [多选题]冯.诺依曼机确立计算 ...
- 大数据项目之电商数仓、数据仓库概念、项目需求及架构设计
文章目录 1.数据仓库概念 2. 项目需求及架构设计 2.1 项目需求分析 2.1.1 采集平台 2.1.2 离线需求 2.1.3 实时需求 2.1.4 思考题 2.2 项目框架 2.2.1 技术选型 ...
- 基于C语言编程的职工工资管理系统项目的设计与开发
文章目录 基于C语言编程的职工工资管理系统项目的设计与开发 一.需求分析 二.项目环境 2.1.项目创建过程 2.2.向项目添加头文件与源文件 三.职工工资管理系统模块功能 3.1.系统总体设计框图 ...
- 2022-07-25:xiu是用rust语言编写的流媒体服务器软件项目。k8s安装xiu,drone文件如何写?
2022-07-25:xiu是用rust语言编写的流媒体服务器软件项目.k8s安装xiu,drone文件如何写? 答案2022-07-25: 云原生环境不可能完全一样,只能做参考. 我采用的是dron ...
最新文章
- Ubuntu 安装LAMP ...
- js添加多个子节点_DOM节点
- Highcharts隐藏网格线
- Java 之 合成模式
- rocketMq发送事务消息
- 从进程组、会话、终端的概念深入理解守护进程
- 怎么抽象mysql数据库_一个用于mysql的数据库抽象层函数库
- 05-01 docker 介绍
- TurboFan-Sea of Nodes概念讲解
- Windows系统下的socket编程
- java flex blazeds_使用BlazeDS实现Java和Flex通信
- EasyCHM chm文件制作过程及软件附软件下载地址
- Python游戏——Pong
- 如何提高BT的下载速度?
- 南阳oj 题目127 星际之门(一)
- Linux内核配置文件
- scala 惰性函数
- 美女--男人的Vitamin C
- 按键,触摸屏流程分析
- 怪物之心无法触发_异度之刃2解锁稀有异刃力男支线怪物之心BOSS位置攻略
热门文章
- mod java 求余_java中求余%与取模floorMod的区别
- 242.有效的字母异位词
- Juggling Life and Learning
- mysql多线程复制binlog_MySQL 不同复制模式下,如何忽略binlog事件
- 凸优化第二章凸集 2.1仿射集合和凸集
- hadoop2.7.3+hbase1.2.5配合起来使用的一个小问题,备注一下
- h3c交换机查看电源和风扇模块序列号
- java web xml配置详解_Java Servlet web xml 配置详解
- js 根据某属性取出数组中对应的对象
- how to set up github blog