代码改变世界

【专栏】重学Go语言文章导航

2017-11-17 09:42  李永京  阅读(2176)  评论(1编辑  收藏  举报

大纲

专栏

重学Go语言:基础篇

重学Go语言:进阶篇

开篇:学习基础技能树意义

00 学习基础技能树意义

  • 到底什么是基础
  • 为什么选择Go作为基础语言
  • 安装学习环境
  • 演示:反汇编、函数内联优化

第一部分:编译(编译、链接、可执行文件结构、符号表)

01 编译

  • gcc编译过程
  • go build编译过程

02 链接

  • 链接器
  • 合并方式
  • 静态链接和动态链接的区别

03 可执行文件结构

  • 通用可执行文件结构(COFF)(readelf -h)
  • COFF用段(section)存储不同类型数据(readelf -S)
  • 常用段
  • 演示:使用readelf、xxd、objdump、gdb查看可执行文件结构信息
  • 演示:objcopy -add-section;strip -remove-section;readelf -p

04 符号

  • 使用nm查看符号
  • 使用readelf -s输出符号信息
  • 删除符号表对反汇编的影响
  • 使用strip删除符号和调试信息
  • 使用UPX压缩并保护可执行文件

第二部分:内存(虚拟内存、进程内存模型、常量、变量)

05 虚拟内存

  • 实模式(8086,boot)与保护模式
  • 虚拟存储器(VM),VA/PA,MMU/TLB,PT/PTE用途
  • 缺页异常(Page Faults),换入换出(swap in/out),颠簸(thrashing)
  • 存储器层次
  • 机会主义内存分配
  • dstat、pidstat使用示例

06 处理器

  • 对称多处理器(SMP),NUMA的问题
  • 物理核心、逻辑核心
  • 缓存:L1(d,i)、L2、L3
  • 超线程(HT),可能因共享cache造成性能问题
  • lscpu使用示例

07 进程内存模型

  • 可执行文件和进程的差异
  • 进程内存模型
  • 使用readelf -l查看段映射
  • 使用gdb查看运行期内存模型

08 常量和变量

  • 常量和变量的区别
  • 常量展开
  • 常量陷阱演示

第三部分:指令(初级汇编指令、控制流)

09 初级汇编指令

  • 寄存器
  • 几类寄存器
  • 内存寻址
  • 常用汇编指令
  • 操作数长度
  • MS-DOS debug
  • 简读汇编代码

第四部分:执行(函数执行、调用堆栈、参数及返回值、闭包)

10 调用堆栈

  • 调用堆栈call stack
  • 堆栈帧stack frame
  • 函数调用,现场保护和恢复
  • 用GDB查看调用堆栈,输出堆栈桢信息
  • IP寄存器的用途
  • 相关汇编指令

11 参数传递

  • C参数复制,返回值
  • Go参数复制,返回值
  • 优化模式对参数传递的影响

12 if和switch对比

  • 是否存在性能差异
  • 使用场景
  • 反汇编对比

13 死代码

  • 示例
  • 使用代码覆盖测试检查
  • 查看编译器能否优化掉死代码

14 匿名函数

  • 匿名函数符号名
  • 匿名函数调用方式
  • 作为返回值的匿名函数
  • 直接调用匿名函数

15 闭包

  • 何为闭包
  • 闭包通过指针引用环境变量
  • 闭包导致环境变量生命周期延长和堆分配
  • 闭包怎么调用的
  • 闭包与数据竞争

16 递归调用

  • 什么是递归
  • Go与C栈大小差异
  • 为什么会引起堆栈溢出(stack overflow)
  • 什么是尾调用
  • 什么是尾递归优化
  • 为什么go的编译器对尾递归调用不做优化处理

17 延迟调用

  • 延迟调用的用途(作用域、IDisposable)
  • defer与finally的对比(总能执行)
  • 正确理解defer实现和执行机制,确保合理使用
  • 利用匿名函数重构作用域
  • 性能问题

18 错误处理

  • 错误分类
  • 错误(异常)是一种“值”,属于正常逻辑返回(exception,error)
  • 使用实例或类型判断错误类别,而非“魔法数字”。(编译器检查、重构、常量陷阱)

19 应用

  • 阻止GDB调试
  • 阻止反汇编、代码混淆
  • monkey patch
  • 缓冲区溢出攻击

第五部分:数据(基础数据类型、常用数据结构)

20 字符串

  • 开篇
  • Go字符串和C char*的差异
  • 为什么实现为不可变类型
  • 拼接字符串实现方式
  • 转换性能优化

21 数组

  • 数组值类型和指针差异
  • 数组指针和指针数组的差别
  • 切片为什么不是动态数组或数组指针
  • 用new或make创建引用类型的差别
  • 切片和数组的性能差异

22 基于数组实现数据结构

  • 开篇
  • 栈(Stack)
  • 队列(Queue)
  • 缓冲区(Pool)
  • 链表(Linked List)

23 哈希表

  • 开篇
  • 哈希表基本实现方式
  • 用数组改进链表法性能
  • 字典的性能调优
  • 字典的数据竞争问题

24 结构体

  • 匿名字段与继承
  • 名称遮蔽(成员访问优先级)
  • 结构体内存布局

第六部分:对象(面向对象理论、方法、接口)

25 方法

  • 方法与函数的差异
  • 方法调用方式,如何传递receiver、self、this
  • 方法可被内联吗
  • 匿名字段方法,是继承调用?还是语法糖?
  • 匿名字段方法调用

26 方法集

  • 什么是方法集
  • 方法集区分基础类型T和指针类型*T
  • 匿名嵌入对方法集的影响
  • 方法集调用

27 方法表达式

  • 开篇
  • Method Expression和Method Value
  • 方法表达式实现方式

28 接口

  • 什么是接口
  • 什么是Duck Type
  • 接口实现方式,方法集与接口 和 接口内部结构
  • 接口调用与直接调用的性能差异
  • 总结

第七部分:并发(进程、线程、协程、通信、同步)

29 进程、线程、协程

  • 进程、线程的区别
  • 系统线程(内核线程、内核态)和用户线程的区别
  • CPU时间片分配方式
  • 协程基本原理,优点和缺点
  • 上下文切换(context switch),以及对性能的影响

第八部分:系统(内存管理、垃圾回收、并发调度)

30 内存管理

  • 自主实现内存管理
  • 内存管理面临的问题
  • Go基于tcmalloc实现的内存分配器工作原理
  • 如何释放物理内存

31 垃圾回收

  • 常用方式:引用计数、代龄、标记清理
  • 垃圾回收何时启动?如何避免内存膨胀,避免影响性能?
  • Go三色标记+写屏障模式如何实现并发标记和并发清理?
  • 控制器和辅助回收的作用

32 并发调度

  • G、M、P模型
  • 如何创建Goroutine?
  • 如何启动并发任务?
  • 调度器如何执行?
  • M/P对应关系

33 连续栈

  • 什么是分段栈
  • 分段栈的问题是什么
  • 连续栈如何实现
  • 连续栈回收
  • 连续栈扩张问题演示

34 系统监控

  • 系统监控的用途
  • 强制垃圾回收
  • 释放物理内存
  • 抢占调度
  • 处理系统调用
  • I/O事件

35 通道

  • 通道基本原理
  • 同步通道和异步通道的区别
  • Goroutine Leak
  • 架构设计

36 同步

  • 常见同步方式
  • Mutex、RWMutex、Cond、Semaphore、SpinLock、Atomic区别
  • 单核和多核指令是否原子
  • HLOCK pin引线(电位拉低、锁总线)如何实现原子操作
  • CAS(Compare-and-swap)
  • 用原子操作实现自旋锁

第九部分:组织(代码组织方式、访问权限)

第十部分:动态(反射、动态语言运行模式)

第十一部分:测试(单元测试、性能测试、代码覆盖率、数据竞争、性能调优)

第十二部分:工具(GDB调试器、常用工具、Go工具链)