文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Go编译时写入数据的原理

2024-12-02 23:28

关注

时间:某天凌晨

事情:线上Go服务突然间 crash

紧急处理:重启Go服务

故障排查:查询日志,找出可能出现的堆栈信息以及追溯源码

问题:线上同时存在多个版本,如何知道当前 crash 的程序属于哪个版本?

添加版本信息的两种方案

方案1,手动添加版本信息:

  1. package main 
  2.  
  3. import ( 
  4.     "flag" 
  5.     "fmt" 
  6. // 下面三个变量,每次发版都要修改 
  7. var version = "v0.0.1" // 程序版本号 
  8. var gitTag = "v0.0.1" // git tag 号 
  9. var dateTime = "2021-08-14 10:00:00" // 编译生成时间 
  10.  
  11. func main() { 
  12.     debugVerInfo := flag.Bool("ver"false"show app version info"
  13.     flag.Parse() 
  14.  
  15.     if *debugVerInfo { 
  16.         fmt.Println("version is:", version) 
  17.         fmt.Println("dateTime is:", dateTime) 
  18.         fmt.Println("gitTag is:", gitTag) 
  19.         return 
  20.     } 
  21.  
  22.     fmt.Println("do other thing"

由于手动在代码中添加版本信息,所以在排查时可以查看到对应信息。

  1. ➜  code ✗ ./client -ver   
  2. version is: v0.0.1 
  3. dateTime is: 2021-08-14 10:00:00 
  4. gitTag is: v0.0.1 

分析:

在很多公司甚至开源项目都会采用该方式,在代码中显式地添加版本等信息。

  1. 假设不经常发版或者发版周期比较长,则完全没问题
  2. 假设发版频繁,很大概率会出现版本信息的遗漏、错误
  3. 假设版本信息忘记更改,则查询出来的信息就是错的

针对以上情况,提出一个问题:Go是编译型语言,版本等信息是否可以在编译时,自动地打包到二进制文件中?

方案2,自动打包版本信息:

  1. package main 
  2.  
  3. import ( 
  4.     "flag" 
  5.     "fmt" 
  6.  
  7. var version = "v0.0.0"// 此处暂时只填写大的版本号 
  8. var gitTag string 
  9. var dateTime string 
  10.  
  11. func main() { 
  12.     debugVerInfo := flag.Bool("ver"false"show app version info"
  13.     flag.Parse() 
  14.  
  15.     if *debugVerInfo { 
  16.         fmt.Println("version is:", version) 
  17.         fmt.Println("dateTime is:", dateTime) 
  18.         fmt.Println("gitTag is:", gitTag) 
  19.         return 
  20.     } 
  21.  
  22.     fmt.Println("do other thing"

 在编译时,打包版本等信息到Go的二进制文件中:

  1. go build -ldflags \ 
  2.     "-X main.version=v0.0.1 -X main.dateTime=`date +%Y-%m-%d,%H:%M:%S` -X main.gitTag=`git tag`" \ 
  3.   -o client 

build 通过 -ldflags 的 -X 参数可以在编译时将值写入变量

变量格式:包名称.变量名称=值

查看版本信息

  1. ➜  code ✗ ./client -ver   
  2. version is: v0.0.1 
  3. dateTime is: 2021-08-14 10:00:00 
  4. gitTag is: v0.0.1 

优点:

无需代码中显式添加版本等信息

避免手动添加版本信息时,遗漏或者错误等情况发生

可使用持续集成工具自动把版本等信息打包到二进制文件中

原理

二进制文件在加载到内存中之后,整个内存空间会被划分为若干段。除了代码区、数据区、堆、栈,还有有一个段为符号表。

在编译时,把版本等信息打包到符号表中,供程序运行时使用。

  1. [root@localhost demo]# readelf -s client | grep main 
  2.     ...... 
  3.   1686: 00000000005608b0    16 OBJECT  GLOBAL DEFAULT   10 main.version 
  4.   1687: 00000000005608a0    16 OBJECT  GLOBAL DEFAULT   10 main.gitTag 
  5.   1688: 0000000000560890    16 OBJECT  GLOBAL DEFAULT   10 main.dateTime 
  6.     ...... 
  7.   2320: 00000000004eb2e8     7 OBJECT  GLOBAL DEFAULT    2 main.version.str 
  8.   2321: 00000000004ebba0    20 OBJECT  GLOBAL DEFAULT    2 main.dateTime.str 
  9.   2322: 00000000004eb2e0     7 OBJECT  GLOBAL DEFAULT    2 main.gitTag.str 

使用 readelf -s命令查看编译好的Go二进制文件符号表信息,可以明显看到在编译时写入的三个变量。

其中,main.version、main.gitTag、main.dateTime 大小都为16,是指 在Go中的string类型结构体大小。

  1. (gdb) ptype version 
  2. type = struct string { 
  3.     uint8 *str; 
  4.     int len; 
  5.  
  6. (gdb) ptype dateTime 
  7. type = struct string { 
  8.     uint8 *str; 
  9.     int len; 
  10.  
  11. (gdb) ptype gitTag 
  12. type = struct string { 
  13.     uint8 *str; 
  14.     int len; 

不知细心的你是否发现,在符号表显示的变量具体值 main.version.str、main.dateTime.str、main.gitTag.str长度都比实际多一个字节。

虽然目前Go实现了自举,但是编译Go编译器的编译器还是用C语言写的

C语言字符串(字节数组)是非安全类型,使用尾零来标识字符串结束。其中,尾零也占用一个字节。

尾零是 ASCII 第一个元素 0, 即:NUL

  1. (gdb) p version 
  2. $1 = "v0.0.1" 
  3.  
  4. (gdb) p dateTime 
  5. $2 = "2021-08-13,23:26:44" 
  6.  
  7. (gdb) p gitTag 
  8. $3 = "v0.0.1" 

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯