百摩网
当前位置: 首页 生活百科

go源码分析(语言源码阅读有感)

时间:2023-07-07 作者: 小编 阅读量: 4 栏目名: 生活百科

以下文章来源于Golang技术分享,作者机器铃砍菜刀Golang技术分享专注于Go语言的知识分享小菜刀读Go源码时,发现一个高频注释语句“XXXmustnotbecopiedAfterfirstuse“。例如sync包下的Pool、Cond、WaitGroup、Mutex、Map和atomoic.Vaule、strings.Builder等,都有该句注释。为什么注释文档中要强调nocopy?安全!

以下文章来源于Golang技术分享 ,作者机器铃砍菜刀

Golang技术分享

专注于Go语言的知识分享

小菜刀读Go源码时,发现一个高频注释语句“XXX must not be copied After first use“。例如sync包下的Pool、Cond、WaitGroup、Mutex、Map和atomoic.Vaule、strings.Builder等,都有该句注释。

为什么注释文档中要强调no copy?

安全!

如果结构体对象包含指针字段,当该对象被拷贝时,会使得两个对象中的指针字段变得不再安全。

typeSstruct{f1intf2*s}typesstruct{namestring}funcmain(){mOld:=S{f1:0,f2:&s{name:"mike"},}mNew:=mOld//拷贝mNew.f1=1mNew.f2.name="jane"fmt.Println(mOld.f1,mOld.f2)//输出:0&{jane}}

如上,结构体对象S中存在两个field,分别是f1和f2,其中f2是指向s类型的指针。当mNew复制了mOld之后,mNew对两个字段进行了改变,可以看到f1字段的更改,不会对mOld造成影响。但是,nNew中f2字段的修改也会把mOld中的f2字段修改掉,这引发了安全问题。

Go是如何保证no copy的?

1. runtime checking

  • strings.Builder中copy检查

funcmain(){varastrings.Buildera.Write([]byte("a"))b:=ab.Write([]byte("b"))}//运行报错:panic: strings: illegal use of non-zero Builder copied by value

报错信息,来源于strings.Builder的copycheck。

typeBuilderstruct{addr*Builder//ofreceiver,todetectcopiesbyvaluebuf[]byte}func(b*Builder)Write(p[]byte)(int,error){b.copyCheck()b.buf=append(b.buf,p...)returnlen(p),nil}func(b*Builder)copyCheck(){ifb.addr==nil{b.addr=(*Builder)(noescape(unsafe.Pointer(b)))}elseifb.addr!=b{panic("strings:illegaluseofnon-zeroBuildercopiedbyvalue")}}

在Builder中,addr是一个指向自身的指针。当对上文中的a复制给b时,a和b本身是不同的对象。因此,b.addr实际还是指向a的指针,这就会触发条件b.addr!=b,造成panic。

  • sync.Cond中copy检查

在源码中,拥有copy检查机制的还有sync.Cond。

typeCondstruct{noCopynoCopyLLockernotifynotifyListcheckercopyChecker}func(c*Cond)Wait(){c.checker.check()...}typecopyCheckeruintptrfunc(c*copyChecker)check(){ifuintptr(*c)!=uintptr(unsafe.Pointer(c))&&!atomic.CompareAndSwapUintptr((*uintptr)(c),0,uintptr(unsafe.Pointer(c)))&&uintptr(*c)!=uintptr(unsafe.Pointer(c)){panic("sync.Condiscopied")}}

这里的check函数初看不易明白。因此,定义一个相似的结构体对象,来探究这里的check函数究竟是如何做copy检查的。

typecondstruct{checkercopyChecker}typecopyCheckeruintptrfunc(c*copyChecker)check(){fmt.Printf("Before:c:%12v,*c:v,uintptr(*c):v,uintptr(unsafe.Pointer(c)):v\n",c,*c,uintptr(*c),uintptr(unsafe.Pointer(c)))swapped:=atomic.CompareAndSwapUintptr((*uintptr)(c),0,uintptr(unsafe.Pointer(c)))fmt.Printf("After:c:v,*c:v,uintptr(*c):v,uintptr(unsafe.Pointer(c)):v,swapped:v\n",c,*c,uintptr(*c),uintptr(unsafe.Pointer(c)),swapped)}funcmain(){varaconda.checker.check()b:=ab.checker.check()}//输出Before:c:0xc0000b4008,*c:0,uintptr(*c):0,uintptr(unsafe.Pointer(c)):824634458120After:c:0xc0000b4008,*c:824634458120,uintptr(*c):824634458120,uintptr(unsafe.Pointer(c)):824634458120,swapped:trueBefore:c:0xc0000b4040,*c:824634458120,uintptr(*c):824634458120,uintptr(unsafe.Pointer(c)):824634458176After:c:0xc0000b4040,*c:824634458120,uintptr(*c):824634458120,uintptr(unsafe.Pointer(c)):824634458176,swapped:false

这下,sync.Cond的copy检查就很清晰了。当a被b copy之后,uintptr(*c)和uintptr(unsafe.Pointer(c))的值是不同的,通过uint对象的原子比较方法CompareAndSwapUintptr将返回false,它证明了对象a被copy过,从而调用panic保护sync.Cond不被复制。

2. go vet checking

上述两个例子都是在程序编译后,runtime检查的。但是,正如文中开篇所述,sync包下的其他的对象如Pool、WaitGroup、Mutex、Map等,它们其实也需要copy检查机制,但是在源码中,却没有提供运行时检查。那该如何保证我们的代码中这些对象在使用中未被copy,从而避免潜在的安全问题呢?

Go在源代码src/sync/cond.go中的一段注释给了我们答案。

//noCopymaybeembeddedintostructswhichmustnotbecopied//afterthefirstuse.////Seehttps://golang.org/issues/8005#issuecomment-190753527//fordetails.typenoCopystruct{}//Lockisano-opusedby-copylockscheckerfrom`govet`.func(*noCopy)Lock(){}func(*noCopy)Unlock(){}

很明显,runtime时的copy检查虽然很重要,但是,该操作会影响程序的执行性能。Go官方目前只提供了strings.Builder和sync.Cond的runtime拷贝检查机制,对于其他需要nocopy对象类型来说,使用go vet工具来做静态编译检查。

具体实施来说,就是该对象,或对象中存在filed,它拥有Lock()和Unlock()方法,即实现sync.Locker接口。之后,可以通过go vet功能,来检查代码中该对象是否有被copy。

例如sync.Pool和sync.WaitGroup就内嵌了noCopy属性,sync.Mutex实现了sync.Locker接口,sync.Map内嵌了sync.Mutex。

  • 静态检查

// wg.gopackage mainimport "sync"func main() {var sm sync.Mutexsm.Lock()sm.Unlock()sm2 := smsm2.Lock()}

如上,sm在first use后,被copy给sm2。注意:该代码运行时,不会报错,但是却存在安全隐患。

$govetwg.go#command-line-arguments./wg.go:9:9:assignmentcopieslockvaluetosm2:sync.Mutex

通过以上命令,即可检查出sync.Mutex有被copy。因此,举一反三,如果在我们自己的项目开发中,定义某对象不能被copy,那么就可以参考Go源码中,嵌入noCopy结构体,最终通过go vet进行copy检查。

typenoCopystruct{}func(*noCopy)Lock(){}func(*noCopy)Unlock(){}typeMyTypestruct{noCopynoCopy...}

更多关于Go关于no copy的讨论请参考官方Github issue,地址:https://github.com/golang/go/issues/8005

    推荐阅读
  • 盆底肌肉锻炼方法男(男性盆底肌训练方法)

    盆底肌肉锻炼方法男锻炼盆底肌肉的方法很简单,即提肛,经常收缩、放松肛门,在开会、办公时就可以锻炼。更准确的方法是在小便时中断排尿,体会下身正在发力的肌肉群的位置,并牢牢记住那种紧绷的感受。锻炼时,应迅速收缩、放松盆底肌肉,每次持续10秒,连续做3组,每组间隔10秒,每天都要练。然后不规则收紧并放松肌肉,重复3组。最后尽可能久地收缩肌肉,以2分钟为上限。需要提醒的是,锻炼时切忌紧绷腹、臀及腿部肌肉。

  • 眼线笔防晕小技巧(防止眼线晕妆的小技巧)

    用海绵沾取定妆产品,仔细按压在眼部的每一块皮肤上,同时也会吸去皮肤上多余的油分。随时清洁如果怕内眼线晕染,建议随身携带棉签,沾湿后即可擦去晕染开的“黑眼圈”。这里需注意的是,很多人为了偷懒,直接用棉棒在眼周擦拭,这样对眼周皮肤带来没必要的拉扯,应用手或乳液沾湿棉棒进行清洁。妆前让眼部变水润。上妆前的半个小时先敷一下补水眼膜,让眼睛得到滋润。这样眼部肌肤在滋润水分充足的情况下才不容易出油晕妆。

  • 抗核抗体谱17项是检查什么的(抗核抗体谱17项是检查什么的,孕检要做)

    在17项抗核抗体谱中,不同抗体针对的自身免疫性疾病并不完全相同。抗SS-B抗体,与干燥综合征有关。有些抗体与疾病活动和器官受累密切相关。一般检查体内的组蛋白和非组蛋白,也有体内的抗体和DNA检测。同时需要根据患者的临床症状作出正确的诊断。而免疫性疾病目前还不能完全治愈,临床表现多样,很容易被误诊或漏诊。此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。

  • 哈弗汽车首保换什么机油(首保机油来了解一下)

    哈弗汽车首保换什么机油机油的话一般有很多牌子,建议还是选择合成机油,去当地的修理厂或者4s店更换机油比较靠谱。建议之后每5000公里或半年更换一次,喜跑高速与急加速的及自驾游爱好者,可换进口大牌子全合成机油,此油能对高速及恶劣条件下的发动机起全面的保护作用,是首选。

  • 桃浓缩汁是什么(桃浓缩汁指的是啥)

    桃浓缩汁是什么浓缩果汁是在水果榨成原汁后再采用低温真空浓缩的方法,蒸发掉一部分水分做成的,在配制100%果汁时须在浓缩果汁原料中还原进去,果汁在浓缩过程中失去的天然水分等量的水,制成具有原水果果肉的色泽、风味和可溶性固形物含量的制品。纤维素又被营养学家称为继蛋白质、碳水化合物、脂肪、无机盐、维生素和微量元素六大营养素之后的第七营养素。

  • 不死鸟的养殖方法(不死鸟的养殖方法是什么)

    接下来我们就一起去研究一下吧!不死鸟的养殖方法适当摘心:对生根后的小苗要及时摘心,促进分枝。在非常闷热的环境下,不利于它的长势,而且它也会容易得一些病害。所以,尽量在夏季多多通风,并调节温度。另外,冬季也得防寒,最低也别低于两度。

  • 正宗石头石锅鱼制作方法(价值2万的石锅鱼配方及做法解密)

    原料:草鱼一条约1800克,姜片5克,葱节5克,香菇20克,红枣10克,枸杞5克,西红柿50克,小葱段10克。

  • 养殖海蟹(黄河滩上养海蟹)

    团队共有十多人,宁波大学海洋学院青年教师王欢成为这次实验的操刀者。2018年10月底,团队赶到兰考进行验收,却失望而归。2019年,宁波大学研发出苗种淡化技术,并申请了发明专利。今年7月,基于这项发现的盐碱水关键离子调控技术申请了发明专利。2020年,当地农民终于迎来了青蟹大丰收。按照亩产量来计算,河南盐碱地青蟹与浙江青蟹产区的中等水准已基本持平。

  • 35岁华裔女子惨遭澳籍丈夫杀害(旧金山99岁华裔妇女遇袭案)

    基德被下令登记为性侵罪犯后,未来十年至终身都可能要向当局报告其居住地点。该案发生于2018年除夕中午。警方于2019年1月1日公布案情,并公开监视系统在北平园拍到基德的照片。2019年陪审团只裁定基德的两项虐待老人的轻罪成立,其他各项重罪均不成立。在轻罪之下,基德无须在狱中服刑,被捕后的一年多基德无力申请保释而被关押在监狱候审,已超过他应有的刑罚。康莱接纳检方的要求将基德登记为性侵罪犯,并实时让基德恢复自由身。

  • 120w快充加持8gb+1亿像素三摄(8512G恢复现货6.92英寸大屏)

    起步价调低400,8512G已经恢复现货,可以入手了原本ZTE中兴Axon30的起步价接近两千五,但如今调低400后只需要两千出头。严格来说,它是第二款中兴旗下的屏下摄像旗舰。这次ZTE中兴Axon30主打大屏视觉体验,因为主屏尺寸高达6.92英寸,在玩游戏或者看视频追剧的时候很有沉浸式体验。整体来看,ZTE中兴Axon30都是专业的旗舰机实力,毕竟这次起步价调低了,而且8512G大存储版本又恢复了现货,当真可以入手了!