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

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

    推荐阅读
  • 吹空调对颈椎有影响吗 吹空调对颈椎有影响吗吗

    天气燥热,心情烦躁,诱发颈椎病研究表明,多愁善感、脾气暴躁的人易患神经衰弱,神经衰弱会影响骨关节及肌肉休息,长此以往,颈肩部容易疼痛。因此专家建议即使在室内时,大家不妨多安排一些就地取材的“运动”,比如仰卧起坐、擦擦窗户、远眺等,充分运动颈部、背部肌肉,保持颈部血液畅通。可以做米字操,也可以做扩胸运动等等,这都有利于我们颈肩关节的放松。肩部的肌肉放松了、血液循环改善了,对颈椎就会有帮助。

  • 海洋乡银杏最佳观赏时间(申城银杏进入最佳观赏期)

    喜温暖湿润气候,在土层深厚、排水良好的砂质土壤中生长较旺盛。花期4-5月,种子9-10月成熟。上海植物园地址:徐汇区龙吴路997号共青森林公园上海共青森林公园内有一千多株银杏,分布在公园内的各个角落,伴随着银杏集体变色,这里已然成了一片黄金森林。层层叠叠的银杏叶,在阳光的照耀下分外妖娆,金叶满目的美景令人流连忘返。林内有上百棵银杏树,秋风吹过,银杏叶飘洒而下,霎时一地金黄。

  • 进口科雷傲碰撞测试怎么样?(科雷傲碰撞测试视频)

    测评结果充分显示了,宝马3系在车内外驾乘人员及行人安全性能方面,表现都十分优异;2、虽然该车在耐撞性与维修经济性指数评级为P,但这并不代表该车不耐撞。这也意味着宝马3系虽然维修费用贵,但车辆的耐撞性表现却很亮眼。

  • 南阳养老保险缴费标准(南阳养老保险缴费标准2021)

    南阳养老保险缴费标准城乡居民缴费标准设为每年100元、200元、300元、400元、500元、600元、700元、800元、900元、1000元、1500元、2000元、2500元、3000元、4000元、5000元12个档次,参保人自主选择档次,按年缴费,多缴多得。

  • 上坡怎样刹车(上坡刹车踩法)

    下面内容希望能帮助到你,我们来一起看看吧!轻踩刹车的同时,离合器踩到底,刹车根据路况来踩。如果踩到了停或速度很慢,档位摆回一档,起步的时候离合器缓放到车子抖动,放开刹车再放开离合器。上坡时出现刹车失灵,应适时减入中低挡,保持足够的动力驶上坡顶停车。

  • 热销手机排行榜(目前最实用的手机)

    8月29日消息,知名市调机构StrategyAnalytics发布了2021年第二季度中国安卓手机畅销榜TOP3。作为榜单上唯一一款起售价高于3500元的安卓旗舰,iQOO7的实力还是不容小觑的。作为性价比旗舰的代表,RedmiK40的上榜在情理之中。排在第一名的是OPPOA53,是2020年12月上市的一款入门5G手机。目前共有秘夜黑、流光紫和湖水绿三款配色,不支持多功能NFC。除此之外,亓纪更吃惊的是iQOO7的成绩,一个新品牌能够在短时间内把3500元以上的机型卖到畅销榜,这样的实力真的不容小觑。

  • 蜜桃成熟时演员表(电影蜜桃成熟时主演是谁)

    蜜桃成熟时演员表?蜜桃成熟时演员表《蜜桃成熟时》是由张肇麟导演,谷德昭张肇麟编剧,李丽珍、潘震伟、成奎安、黄光亮、左颂升、邵传勇、焦姣、田青主演的一部情色剧。富家女阿珍狂野不羁,自幼受父母溺爱的她深信自己的处世哲学,只想要追寻自由简单生活与浪漫炽热的爱情。暑假期间,父母安排其前往英国参加夏令营,她却想趁此机会计划一个属于自己的假期,暗地里将机票送给好友,自己却前往男友处准备与其过甜蜜的二人世界。

  • 帜组词拼音(汉字帜组词)

    招帜[zhāozhì]招旗麾帜[huīzhì]将帅的旗帜,下面我们就来聊聊关于帜组词拼音?接下来我们就一起去了解一下吧!麾帜[huīzhì]将帅的旗帜。毒帜[dúzhì]军中的大旗。毒,用同“纛”。降帜[jiàngzhì]降旗。幡帜[fānzhì]亦作“幡织”。酒帜[jiǔzhì]即酒帘。白帜[báizhì]战败者表示投降的白旗。拔赵帜[bázhàozhì]同“拔赵帜易汉帜”。清林寿图《送彭仁山副戎屯华阴》诗:“奋然鼓勇拔赵帜,不则严仗完燕城。”

  • 做公开演讲的技巧和方法(公开演讲的技巧有哪些)

    人们往往会对愿意在他人面前展现真实自己的人印象深刻。当人们知道为什么这件事情对他们重要的时候,往往就会将其记住。将其益处在演讲的开头就说出来。当不断的通过自己的录音录像来发现并纠正问题时,你的演讲才会变得越来越好。同时还需要注意观众的反应。

  • 2022上海技能补贴项目在哪里查询(2022上海技能补贴项目在哪里查询进度)

    经本市人力资源社会保障部门备案的企业职业技能等级评价机构或社会培训评价组织,按规定组织实施经备案的职业技能等级认定项目纳入补贴项目范围,即:纳入补贴项目范围需满足评价机构及其认定项目的双备案。职业技能等级分为初级工(五级)、中级工(四级)、高级工(三级)、技师(二级)和高级技师(一级)。