PPD's blog https://ppd0705.github.io/ Recent content on PPD's blog Hugo -- gohugo.io en Sun, 04 Jan 2026 17:03:13 +0800 2025年终总结 https://ppd0705.github.io/post/2025_summary/ Sun, 04 Jan 2026 17:03:13 +0800 https://ppd0705.github.io/post/2025_summary/ <h1 id="跑步">跑步</h1> <p>今年跑了一千公里,居然比去年多了一倍,开始我以为因为加入跑团导致跑量大增,看了一下反而之前三月份是跑的最多的。 想起来今年从新年第一天以跑20.25公庆祝新年开始,上半年我就比较稳定地保持一周跑三次的节奏。</p> <p>六月份碰巧遇到了开心纯跑前海队,被热情地邀请加入跑团,然后十月开始的新一届为期四个月的跑团服务组成员,每周三次6点开始的例跑, 如果不是当服务组成员,我是不可能坚持这么久的。不过侧面也说明我还是一个很靠谱的人,从来没有临时缺席的情况。</p> <p>今年也是我跑马最多的年份,参加了四个半马,分别是揭阳、成都温江、长沙、清迈。</p> <h1 id="爬山">爬山</h1> <p>年初我发起了一次带领诺友爬南山的活动,包括我总共6个人,过程挺愉快的。我原本以为没有后续了, 没想到 LX 想每个月都爬一次山,于是我们就相互督促, 后面又爬了凤凰山、梧桐山、五园联通、马峦山、梅沙尖等等,年底还去挑战了一下三水线(金龟村下撤)。作为一个来深圳八年都没爬过梧桐山的人, 这一年居然能爬这么多山,现在还是觉得不可思议,这就是所谓的&quot;一群人走的远&quot;吧! 其实组织每月爬山还是有不少挑战的,首先是时间,好几次我都是前一天返回深圳第二天爬山;其次是路线选择,不能选太难的线路,也不能重复。 不过后面我发觉到爬山具体爬什么山其实不是最重要的,最重要的是走出家门,走进大自然,感受其中的能量。</p> <h1 id="阅读">阅读</h1> <p>从年初参加了诺言线下读书会开始,我就积极参加了线上打卡挑战活动。看完了15本书,还有2本看不下去不看了。 今年卸载了微信读书,改成了看实体书,主是手机对我来说还是干扰太多了,不过这样也养成了出门带书的好习惯。</p> <p>收获最大的是《深度关系》这本书,武志红老师讲的东西相比国外的心理学书籍更接近国人体质,所以会更有感触。 愿我们都有好的自恋和好的关系!</p> <h1 id="英语">英语</h1> <p>从去年年底开始在 Preply 线上学英语,今年算是学了完整的一年,大概每周一到两节课的样子。可能用&quot;学&quot;这个字不够准确,因为我其实主要是和老师闲聊,并没有课本或者固定的主题,更不是为了考试。 在平台上找老师也看运气,上半年的那个老师开始还正常,慢慢不时临时改期,最后直接从平台消失了;后面遇到的我现在的英语老师就很好,很准时也很耐心,而且会用心记下每个学生的情况, 这样的聊天感觉像和一个老朋友在聊,不会因为语言不熟练而紧张。</p> <p>要说一年下来我英语水平提高了吗?我想可能并没有。因为我慢慢意识到靠每周一两节线上课是远远不够的,还是自己平时需要多输入和输出英语内容, 而我目前这块并没有做好。但我想收获还是有的,从今年两次出国旅行的经历看来,我对说英语没有那么犯怵了。</p> <h1 id="旅行">旅行</h1> <p>今年去了很多地方,一半是去跑马,有独自出行,有和家人,也有去见朋友。</p> <p>三月底为了去跑揭阳半马,去了普宁和揭阳,第一次去潮汕地区,感觉还挺有意思的,那边寺庙香火好旺啊</p> <p>四月中旬先是特种兵似的游了重庆和成都,在重庆特意住了一晚背包十年,继续感受不同地方背包十年的氛围。第二天早上体验了板凳面,晚上和第一份工作认识的如同学般的前同事吃了九宫格微微辣火锅(本来我想选微辣,后面我就老实了~),逛了洪崖洞,</p> <p><img src="https://ppd0705.github.io/image/2025_summary/01.jpeg" alt="重庆"></p> <p>接着又坐高铁去了成都,体验了一下坐在公园喝茶摆龙门阵。最后一天早晨还跑了温江半马,跑道几乎没有爬升,所以让我破了150。 跑完就去吃了美食作家王刚开的水煮鱼店,味道还不错,吃完我就立刻会深圳转机去大阪了。</p> <p><img src="https://ppd0705.github.io/image/2025_summary/02.jpeg" alt="成都茶馆"></p> <p>到大阪的第一天见了一个诺友,一个三十加的三娃妈妈,独自在日本求学,实在是太有勇气了,她说她当初日文不会,英语不好,完全靠提前准备好可能会问的问题的英语答案, 然后死背下来才通过的申请面试的。晚饭就是在超市买了和牛在她宿舍煎了吃,味道还挺好的,一次特别的吃和牛的体验。 第二天去大阪城公园看了樱花,今年相比往年天气冷,所以看到了正在盛开的樱花,真的好美,而且去的早感觉包场的感觉。 <img src="https://ppd0705.github.io/image/2025_summary/03.jpeg" alt="大阪城公园"></p> <p>晚上去了音乐厅看了鲁叔的演奏,这是我第一次听付费的钢琴演出,现场听的感觉还是挺不一样的,两个小时过的实在太快了,有点不过瘾~</p> <p><img src="https://ppd0705.github.io/image/2025_summary/06.jpeg" alt="鲁叔"></p> <p>后面又去了京都,京都感觉像个安逸小城,沿着鸭川散步好惬意啊。 最后我又临时决定还是去富士山看看,于是立马坐新干线来到山脚下的一个小镇,参照攻略,第二天一大早去了附近的一个公园,不到六点的时候就看到了小镇全貌和远处富士山融为一体的美景,后面去租了自行车环山中湖骑了一圈,看到了在湖中自由徜徉的白天鹅。</p> <p><img src="https://ppd0705.github.io/image/2025_summary/07.jpeg" alt="富士山"></p> <p>结束后我又坐新干线赶回大阪,意外地发现酒店里有免费温泉,于是体验了一下,确实很舒服。</p> <p>六月底和姐姐去了贵阳和安顺,贵州美食真的好吃又便宜,一个肚子真的吃不下,我还会再来吃的~</p> <p><img src="https://ppd0705.github.io/image/2025_summary/12.jpeg" alt="万峰顶"></p> <p>国庆上海的大学室友来找我们,先是带带他们在前海逛了逛,后面我们去了在广州的大学室友那里,他们两夫妻加我这个单身狗在广州和佛山自驾游了一番,吃了好多 好吃的,看了佛山舞狮,最后还去了长隆动物园。</p> <p><img src="https://ppd0705.github.io/image/2025_summary/08.jpeg" alt="佛山舞狮"></p> <p>十月底去跑了一下长沙半马,这次更多是见家乡的同学,当天去了同学家里吃饭,第二天跑完又见了另一个同学。</p> <p>十一月底去福州见了一个前同事和一个当地的朋友,在福州吃了很多当地美食,最后还提了一箱鱼丸回来。</p> <p>最后年底去了清迈跑了凌晨四点开始的半马,在青旅认识了很多来gap的朋友,爬了素贴山,还去英语角和两个外国人聊到12点关门。 <img src="https://ppd0705.github.io/image/2025_summary/09.jpeg" alt="素贴山"></p> <p>后面又去曼谷溜达了一下,果然曼谷更热也更城市化,曼谷的公交车窗子直接没有玻璃,开的速度也超快。</p> <p><img src="https://ppd0705.github.io/image/2025_summary/10.jpeg" alt="唐人街"></p> <h1 id="工作">工作</h1> <p>今年依旧是远程工作的一年,下半年发现了一个步行15分钟可到的新图书馆,人少环境好,我大多数时间在此办公,周一图书馆不开的时候就去星巴克。</p> <p>三季度突然要和隔壁组合并,领导开始说有两个人要我带,但实际又不归我管,这种情况弄的我有点焦虑和难受,后面说开了主要还是各做各的, 我们的代码和技术可以搬用(后面发现这才是主要目的,他们还是要做他们的,是我想太多导致自己压力山大)。目前这个合并(或者说被兼并)并没有显现出产出的提升,效率就更不用说了。 只能说静观其变吧,做好自己要做的和想做的。</p> <p>四季度终于有时间用 rust 开始重写项目了,发现 rust 确实有一些相比 go 好的点,比如支持 int 128,这样就可以很好的用来代替decimal;比如 高性能 json库。但 rust 因为独有的所有权特性,导致需要共享的时候相当难受,某种程度上也是 Arc 满天飞了;另外虽然 rust 也支持同步代码,但 基本上实际场景只能写异步代码,这个还是go 简单好用啊。目前 rust 项目还在测试中,不知道能否有效果,明年在进一步推进。明年还想学的是 solidity,希望也能反馈到工作中来。</p> <h1 id="关系">关系</h1> <p>今年感觉自己更加接纳自己了,更少的匮乏感,更少的自我攻击。即使受到秃头阳痿的中年暴击,也能较为坦然的接受。 每当自己感到自己不够好的时候我就会心里默念&quot;I love you 我爱你&quot;,算是自己把经历匮乏不安的童年的我好好养育一遍吧。</p> <p>今年见到了大多数我生命中重要的人,感恩遇见。</p> <p>以前除了家人同学朋友,我没有其他的人际关系,今年通过爬山和跑步认识了一些人,感觉和深圳这座城市连接更紧密了,感恩遇见。</p> <p>今年和爸爸的关系算是一个低谷吧,有一阵我看到他的来电显示,整个人会产生躯体僵硬反应,那种贬低打压和孝道绑架让我实在难以承受。 从刀姐和金惟纯的<a href="https://www.xiaoyuzhoufm.com/episode/692552e5e09dc0297ac68975">对谈播客</a>获得很多启发,让我从这种负面情绪走出来,对自己全然负责,也让别人对他自己负责。过好自己的生活先吧,电话不一定要接。</p> <h1 id="投资">投资</h1> <p>中文MEME试水亏钱了,1011早上起来了但不知道怎么抓机会,总结就是没怎么尝试。</p> <p>今年最后看了一本畅销书《金钱心理学》,里面提到了尾部的胜利,但只有多尝试才有可能遇到尾部,所以还是要多试。 另外里面也提到了财富是你没有花掉的钱,这些能给你带来选择自由的没花掉的钱才是财富,这提醒我还是要多储蓄少消费。</p> 2024海南环岛摩旅 https://ppd0705.github.io/post/hainan_motorcycle_road_trip/ Fri, 03 Jan 2025 16:58:12 +0800 https://ppd0705.github.io/post/hainan_motorcycle_road_trip/ <h1 id="前言">前言</h1> <p>之所以想去海南摩旅,主要原因是24年发现了一个非常喜欢的 Youtuber &mdash; <a href="https://www.youtube.com/@littlechineseeverywhere">Little Chinese Everywhere</a>, 看到她在广西和海南骑摩托车旅行,感觉摩托车还蛮不错的,相比汽车更轻便自由,又没有自行车那么累(很久之前也想过骑车环岛来着)。刚好圣诞又有假期,于是便起心动念了。</p> <p>动念的第2天便搜索了一下摩托车驾照考证相关的信息,立马报名了去佛山考三轮摩托车D照的项目,中间在深圳练了2次车,第8天就考试拿到驾照了。</p> <p>我是个不怎么喜欢做攻略和准备的人,直到临出发10天前才做具体准备:</p> <ul> <li>看攻略确定西进东出,从海口出发和结束,选了一些觉得有兴趣的点,大概7天</li> <li>确定租车店铺(后面感觉他家确实还可以)</li> <li>装备买了40L防水袋、手套、防水鞋套(没用上),一贯风格就是轻(不)装(想)出(花)行(钱),没有买专门的骑行服和雨衣,用冲锋衣代替</li> </ul> <h1 id="海口-临高第1天">海口-临高(第1天)</h1> <p>凌晨到海口,睡了一觉,走出酒店大门,就看到了椰子树作为行道树的街道,随即又有骑行队伍路过,瞬间感觉自己来对地方了!</p> <link rel="stylesheet" href="https://ppd0705.github.io/css/hugo-easy-gallery.css" /> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/01.jpeg" alt="出酒店门就感觉来对地方了"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/01.jpeg" itemprop="contentUrl"></a> <figcaption> <p>出酒店门就感觉来对地方了</p> </figcaption> </figure> </div> <p>没有预约,直接走入网上提前看好的租车店,花了1分钟选了一台125的踏板车,作为小白,有个入门车对我来说够用了。本来还想着要不要先在海口逛逛, 但老板建议我直接出发,不然第二天行程会很赶,于是我就毫无准备地回酒店拿好行李出发了。</p> <p>一开始我不太敢上机动车道,在非机动车和电瓶车一起,后面慢慢才上机动车道。</p> <p>出了市区很快就上环岛旅行公路啦,路况很好。</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/02.jpeg" alt="上环岛旅游公路啦"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/02.jpeg" itemprop="contentUrl"></a> <figcaption> <p>上环岛旅游公路啦</p> </figcaption> </figure> </div> <p>第一个很让我震撼的场景是发现田里很多人在一起收割甘蔗,很壮观,而且有涉及机械抓和大卡车。我没忍住和割甘蔗的阿姨搭讪,阿姨还热情问我吃不吃甘蔗!</p> <p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/03.jpeg" alt="第一次见收甘蔗,好像和收水稻类似?"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/03.jpeg" itemprop="contentUrl"></a> <figcaption> <p>第一次见收甘蔗,好像和收水稻类似?</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/05.jpeg" alt="两台机械抓在装车"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/05.jpeg" itemprop="contentUrl"></a> <figcaption> <p>两台机械抓在装车</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/04.jpeg" alt="满满一大卡车甘蔗"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/04.jpeg" itemprop="contentUrl"></a> <figcaption> <p>满满一大卡车甘蔗</p> </figcaption> </figure> </div> </p> <p>第二个让我感觉很深刻的是路过一个静谧的小渔村,港湾停满了渔船,马路边摆满了渔网,渔民们正在补网</p> <p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/06.jpeg" alt="静谧的港湾"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/06.jpeg" itemprop="contentUrl"></a> <figcaption> <p>静谧的港湾</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/07.jpeg" alt="补网ing"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/07.jpeg" itemprop="contentUrl"></a> <figcaption> <p>补网ing</p> </figcaption> </figure> </div> </p> <p>不知不觉过了五点,下起了小雨,温度降了很多,我感觉有点失温,油也不多了,于是就近去了新盈镇住宿了,晚上吃到了特色的虾饼和老盐茶</p> <p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/08.jpeg" alt="围炉吃虾饼的方式感觉很新奇"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/08.jpeg" itemprop="contentUrl"></a> <figcaption> <p>围炉吃虾饼的方式感觉很新奇</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/09.jpeg" alt="我吃了9个,只要3块钱"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/09.jpeg" itemprop="contentUrl"></a> <figcaption> <p>我吃了9个,只要3块钱</p> </figcaption> </figure> </div> </p> <h1 id="临高-儋州第2天">临高-儋州(第2天)</h1> <p>今天是个阴雨天,还好雨不大。</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/11.jpeg" alt="河里的人不知道是在干什么"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/11.jpeg" itemprop="contentUrl"></a> <figcaption> <p>河里的人不知道是在干什么</p> </figcaption> </figure> </div> <p>本来想去峨蔓火山公园,但发现油不够了,只好拐去加油了,加完油后面就直接去东坡书院了, 在东坡书院复习了一些早已还给语文老师的诗词。</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/13.jpeg" alt="书院正门"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/13.jpeg" itemprop="contentUrl"></a> <figcaption> <p>书院正门</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/12.jpeg" alt="水光潋滟晴方好,山色空蒙雨亦奇"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/12.jpeg" itemprop="contentUrl"></a> <figcaption> <p>水光潋滟晴方好,山色空蒙雨亦奇</p> </figcaption> </figure> </div> <p>之后就去儋州市区等同学了,晚上吃了好吃的糟粕醋火锅</p> <p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/14.jpeg" alt="店面看起来不起眼,但很快就满座了"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/14.jpeg" itemprop="contentUrl"></a> <figcaption> <p>店面看起来不起眼,但很快就满座了</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/15.jpeg" alt="金桔当蘸料,第一次见"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/15.jpeg" itemprop="contentUrl"></a> <figcaption> <p>金桔当蘸料,第一次见</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/16.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/16.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> </p> <h1 id="儋州-莺歌海第3天">儋州-莺歌海(第3天)</h1> <p>今天是赶路的一天,路况也很好。 路上遇到一个在棋子湾长期旅居的小姐姐,平时在网上教英语,出来散步会带上她的小狗狗</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/17.jpeg" alt="三只可爱小狗狗"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/17.jpeg" itemprop="contentUrl"></a> <figcaption> <p>三只可爱小狗狗</p> </figcaption> </figure> </div> <p>好酷的房车,背景是个大风车 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/18.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/18.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>到目的地莺歌海镇啦,这里是个小渔村,很有生活气息!</p> <p>村子很好逛,总体看下来,他们的建筑风格大体分为三个时代。</p> <p>第一代是一层,可能是盖瓦或者平层,粉刷的白墙 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/25.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/25.jpeg" alt="这个好像没住人了"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/25.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这个好像没住人了</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/31.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/31.jpeg" alt="两只可爱的猫猫"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/31.jpeg" itemprop="contentUrl"></a> <figcaption> <p>两只可爱的猫猫</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/32.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/32.jpeg" alt="村口肉铺"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/32.jpeg" itemprop="contentUrl"></a> <figcaption> <p>村口肉铺</p> </figcaption> </figure> </div> </div> </p> <p>第二代是两层,会贴漂亮的瓷砖 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/24.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/24.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/24.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/27.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/27.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/27.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/28.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/28.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/28.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> </div> </p> <p>第三是就是很现代化的别墅形式了 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/34.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/34.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/34.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/35.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/35.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/35.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/33.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/33.jpeg" alt="鱼肉好鲜美"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/33.jpeg" itemprop="contentUrl"></a> <figcaption> <p>鱼肉好鲜美</p> </figcaption> </figure> </div> </div> </p> <p>不时看到有人家门口有晒各种这样的鱼</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/21.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/21.jpeg" alt="这个问了好像说叫魔鬼鱼"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/21.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这个问了好像说叫魔鬼鱼</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/26.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/26.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/26.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/29.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/29.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/29.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> <p>海边,天气好的时候可以看日落 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/36.jpeg" alt="天气有点阴"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/36.jpeg" itemprop="contentUrl"></a> <figcaption> <p>天气有点阴</p> </figcaption> </figure> </div> </p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/20.jpeg" alt="美丽的民宿门口"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/20.jpeg" itemprop="contentUrl"></a> <figcaption> <p>美丽的民宿门口</p> </figcaption> </figure> </div> <h1 id="莺歌海-三亚第4天">莺歌海-三亚(第4天)</h1> <p>往三亚走,天气开始好起来,中途歇脚的时候,刚好有个骑车的大叔也在我边上停下来了,于是我们聊了好一会。 叔叔来自山东,62岁,职业是农民,目前是农闲时期,不想老在家打麻将吸二手烟,所以一个人带着自行车就坐火车来环岛啦, 普通的装备,不赶时间,主打一个休闲玩耍!</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/37.jpeg" alt="我三十年后还能有这种状态吗?"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/37.jpeg" itemprop="contentUrl"></a> <figcaption> <p>我三十年后还能有这种状态吗?</p> </figcaption> </figure> </div> <p>后面去了一下大小洞天景区里面的自然博物馆 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/38.jpeg" alt="大小洞天景区里面的椰林"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/38.jpeg" itemprop="contentUrl"></a> <figcaption> <p>大小洞天景区里面的椰林</p> </figcaption> </figure> </div> </p> <p>场馆挺小的,很多恐龙相关的介绍,可能我期待太高了,感觉不及预期~ <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/40.jpeg" alt="里面不让拍照"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/40.jpeg" itemprop="contentUrl"></a> <figcaption> <p>里面不让拍照</p> </figcaption> </figure> </div> </p> <p>当天住在美丽之冠,外面看起来建筑设计很别致,但里面其实入住率很低,自带的商场都关停了,也许和开发商是恒大有关? <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/39.jpeg" alt="视野还不错"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/39.jpeg" itemprop="contentUrl"></a> <figcaption> <p>视野还不错</p> </figcaption> </figure> </div> </p> <h1 id="三亚-神州半岛第5天">三亚-神州半岛(第5天)</h1> <p>来海南,其实还有一个目的,就是来三亚跑半马啦</p> <p>七点起跑,六点多一点就出门啦,天还没亮 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/42.jpeg" alt="整装待发"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/42.jpeg" itemprop="contentUrl"></a> <figcaption> <p>整装待发</p> </figcaption> </figure> </div> </p> <p>太阳出来了,美丽的椰林跑道 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/46.jpeg" alt="靓丽的300兔子"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/46.jpeg" itemprop="contentUrl"></a> <figcaption> <p>靓丽的300兔子</p> </figcaption> </figure> </div> </p> <p>路过一片沙滩,好美,忍不住停下来拍个照 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/48.jpeg" alt="我去前面探探路"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/48.jpeg" itemprop="contentUrl"></a> <figcaption> <p>我去前面探探路</p> </figcaption> </figure> </div> </p> <p>这次跑步主要目标就是看看跑友们的 cosplay,好好玩 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/41.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/41.jpeg" alt="媒婆也跑步?"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/41.jpeg" itemprop="contentUrl"></a> <figcaption> <p>媒婆也跑步?</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/43.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/43.jpeg" alt="美人鱼"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/43.jpeg" itemprop="contentUrl"></a> <figcaption> <p>美人鱼</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/44.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/44.jpeg" alt="真理!"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/44.jpeg" itemprop="contentUrl"></a> <figcaption> <p>真理!</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/45.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/45.jpeg" alt="这应该是新疆风格?"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/45.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这应该是新疆风格?</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/47.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/47.jpeg" alt="直播跑步的新娘"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/47.jpeg" itemprop="contentUrl"></a> <figcaption> <p>直播跑步的新娘</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/49.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/49.jpeg" alt="真的陪跑?"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/49.jpeg" itemprop="contentUrl"></a> <figcaption> <p>真的陪跑?</p> </figcaption> </figure> </div> </div> </p> <p>跑完休整了一下,又上路了,看到人们在插秧,果然海南是种三季稻的</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/50.jpeg" alt="这算早稻吗?"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/50.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这算早稻吗?</p> </figcaption> </figure> </div> <p>本身走错路了,也听说日月湾在修路,后面直接往神州半岛去了。</p> <p>入住的度假公寓太舒服了,空间大,设施齐全,洗衣机晾衣架都有,非常适合长期待 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/51.jpeg" alt="还能看到内海"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/51.jpeg" itemprop="contentUrl"></a> <figcaption> <p>还能看到内海</p> </figcaption> </figure> </div> </p> <p>休整一下就往海边跑了,忘记换拖鞋了!不过看到了美丽的晚霞!</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/52.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/52.jpeg" alt="好宁静"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/52.jpeg" itemprop="contentUrl"></a> <figcaption> <p>好宁静</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/53.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/53.jpeg" alt="原来海边可以玩烟花"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/53.jpeg" itemprop="contentUrl"></a> <figcaption> <p>原来海边可以玩烟花</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/hainan_motorcycle_road_trip/54.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/54.jpeg" alt="远处也放起了烟花"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/54.jpeg" itemprop="contentUrl"></a> <figcaption> <p>远处也放起了烟花</p> </figcaption> </figure> </div> </div> <h1 id="神州半岛-文昌第6天">神州半岛-文昌(第6天)</h1> <p>一早又来海边了,看到了一个三人叔叔阿姨骑行团,得知他们从宇宙的尽头&mdash;铁岭出发,沿着海岸线一路南下,这是他们这次骑行的第83天! <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/55.jpeg" alt="很有故事感的车"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/55.jpeg" itemprop="contentUrl"></a> <figcaption> <p>很有故事感的车</p> </figcaption> </figure> </div> </p> <p>到了一片没有游客的海滩,发现有很多贝壳 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/56.jpeg" alt="海滩独享"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/56.jpeg" itemprop="contentUrl"></a> <figcaption> <p>海滩独享</p> </figcaption> </figure> </div> </p> <p>捡了一个多小时贝壳,只挑带孔洞的捡,很上头 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/57.jpeg" alt="我好富有"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/57.jpeg" itemprop="contentUrl"></a> <figcaption> <p>我好富有</p> </figcaption> </figure> </div> </p> <p>想着摆个心形来着,结果海浪一来,就被冲远了 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/58.jpeg" alt="不要跑!"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/58.jpeg" itemprop="contentUrl"></a> <figcaption> <p>不要跑!</p> </figcaption> </figure> </div> </p> <p>第一次见漫山的菠萝树 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/59.jpeg" alt="远处都是菠萝"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/59.jpeg" itemprop="contentUrl"></a> <figcaption> <p>远处都是菠萝</p> </figcaption> </figure> </div> </p> <p>美丽的海边 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/61.jpeg" alt="这里沙子好白"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/61.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这里沙子好白</p> </figcaption> </figure> </div> </p> <p>道路两旁满满的椰子树 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/62.jpeg" alt="靠边走不会被椰子砸到吧"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/62.jpeg" itemprop="contentUrl"></a> <figcaption> <p>靠边走不会被椰子砸到吧</p> </figcaption> </figure> </div> </p> <h1 id="文昌-海口第7天">文昌-海口(第7天)</h1> <p>今天本来没有抱什么期待,加速往回赶,想早点还车,但后面还是去了一下木兰湾。 说实话木兰灯塔没什么看的,近海也没什么看的。 但是好奇如我,多走了两步,看到了这一周来觉得最美的海滩。我在这里也停留了一个多小时,海浪声听得超级舒服</p> <p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/63.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/63.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/64.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/64.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/65.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/65.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/67.jpeg" /> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/67.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>在我的旧有认知里面以为绿色的是苔藓,其实它是海草,很喜欢这一抹绿意 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/66.jpeg" alt="海草近景"/> </div> <a href="https://ppd0705.github.io/image/hainan_motorcycle_road_trip/66.jpeg" itemprop="contentUrl"></a> <figcaption> <p>海草近景</p> </figcaption> </figure> </div> </p> <h1 id="后记">后记</h1> <p>作为一个新手,7 天 1080 公里顺利完成环岛,我感觉很幸运。回过头想,摩托确实感觉有点有点脆弱,尤其是走在车流中的时候, 不过如果想太多的话,也就不会出发啦!</p> <p>环岛旅游公路大部分路况都很好,专门建了停车充电上厕所的休息区,所以驾车或骑车都很合适。但路上加油站比较少,有时需要绕路加油, 所以对于小油箱的摩托车来说,额外准备一个油箱更好。海南摩托车加油需要身份证,但注册中石化的易捷加油的话就可以直接用小程序加油了,我还体验了2次自助加油!</p> <p>从这次住的酒店来看,不少酒店都改造成智能家居模式了,灯光窗帘等都可以语音控制;另外海南的酒店喜欢用4个6加4个8这个密码;最后不得不说海南这边的度假酒店的设计实在适合长待的人!</p> <p>这次让我印象最深的人还是那个山东叔叔,作为一个62岁的农民,农闲时能有这种心力独自出来骑车环岛,我很佩服。当然我们聊到后面有机会可以走中线,他也说对他这个年纪的人来说以后说不准了。30年后我有这种心力吗?</p> <p>这次我最喜欢的地方是莺歌海和神州半岛,以后如果有机会长待的话,我希望每个地方能住上一段时间。</p> <p>这次旅行我好像没有一次感到孤独,即使海边看情侣放烟花,我也只是尽情欣赏。大概率和驾驶摩托有关, 我第一天还带了一会耳机听歌,后面就再也没用耳机了,路况好的时候我常常油门拧到底,不过125的极限时速也不会超过100。专注驾驶某种程度也算是进入了心流,整个人 沉浸在过程中,杂念很少。另外我适应了在酒店点外卖,之前我出去玩很少点外卖,但这次我点了好几次外卖,这样就不用纠结去外面找吃的时候适不适合一个人吃的问题了!</p> 2024年终总结 https://ppd0705.github.io/post/2024_summary/ Tue, 31 Dec 2024 07:23:11 +0800 https://ppd0705.github.io/post/2024_summary/ <h1 id="工作">工作</h1> <p>今年的关键词还是重构,基础系统和信号模型都做了重构,还算是小有收获。</p> <p>明年要做什么我其实还没太想清楚,重构目前做的差不多了,更多的需要创新。 今年我学了一些Rust/Solana/Jupiter相关的东西,或许这些积淀明年能做点什么东西出来</p> <p>今年是完全远程工作的第二年,大部分时间在家工作,部分时间在图书馆。 去图书馆去的少的主要原因年中有一段时间身体突然不太舒服中断了,年末又偶尔去图书馆了。</p> <p>今年团队合并加入了两个小伙伴,虽然交流也并没那么密切,但感觉还是有不少收获。</p> <h1 id="生活">生活</h1> <p>今年的关键词是重新连接。</p> <p>今年旅行还不少,主要是江苏、肯尼亚、海南。 清明去了江苏,在苏州见到了新同事前同事,逛了逛春意盎然的园子,喜欢苏州这座城市。 后面和分别在江浙沪的三个大学室友在常州相聚,还去月子中心看了初生的小宝宝。</p> <p>五一跟团去了非洲,终于亲眼见到了真实的动物大世界!另外和同行的小伙伴不时的互动交流也是看到了很多不一样的人生。</p> <p>最后就是年底的海南摩旅环岛,此刻我在文昌,今天也是旅程最后的一天。整个行程,没做什么准备,我也永远是那个没什么装备的那个人。大概看了一下小红书,选了几个感兴趣的地点就出发了。 回头看来,感触最多的点都是路上意外遇到的。最喜欢莺歌海和神州半岛,以后有机会我都想住上个一星期;另外我在一片无名的海边捡了一个多小时贝壳,太好玩了,满载了一堆天然带孔洞的贝壳而归; 最后我从遇到的人身上感触最深的就是最重要的就是出发,限制没那么多。</p> <p>另外我也见了2次在广州的同学,之前5年没去广州了。</p> <p>回家见了小时候的朋友,和他一家人吃了饭,去了他的新家坐了坐。</p> <p>在深圳趁机见了一下来出差的朋友,上次见面2年前还是我去她家吃饭。</p> <p>特意绕了点路,去见了大学唯一海南的同学,我是第一个来海南找她蹭饭的人,吃了好吃的糟粕醋火锅!</p> <p>有一个第一份工作认识的很有缘分的朋友没有机会见着,但年底我主动联系然后我们语音畅聊了一个多小时,明年争取去川渝跑个步去找他见一面!</p> <p>去佛山跑步的时候顺便去见了不是很熟悉但这两年重新走动的表哥,看看他们日常的生活状态也挺有感触的。</p> <p>最后我重新联系了我的高中计算机老师,十多年没有联系了,我在QQ上表达了我的感谢,老师过了几天后回复我了,也给了我满满的祝福!后面有机会去学校看望她!</p> <p>时隔三年半之后,今年重新回到半马跑道,十二月第一个和最后一个周日分别跑了一场,而且都破2了,二十多岁都没破过2!现在我的身体肯定没有二十多岁好,说明训练是有用的!</p> <p>今年也在找对象做了一些尝试和努力,虽然没有结果,但过程也是好的。尤其是年底的一场短暂的上头,让我看到了很不一样的自己!</p> <p>今年第四季度生活节奏超级好,一边是规律跑步,一边是每日阅读打卡,整个人变得很充实。今年看到让我最震撼的一句话是重读《力量从哪里来》这本书发现的!</p> <blockquote> <p>我们最大的不敢是不敢发光!另外只有允许自己发光时,我们才会允许他人发光。</p> </blockquote> <h1 id="展望">展望</h1> <blockquote> <p>Go ahead, have fun. &mdash; Faker</p> </blockquote> <p><img src="https://ppd0705.github.io/image/2024_summary/01.jpeg" alt="01"></p> <p><img src="https://ppd0705.github.io/image/2024_summary/02.jpeg" alt="02"></p> <p><img src="https://ppd0705.github.io/image/2024_summary/03.jpeg" alt="03"></p> 2024端午小记 https://ppd0705.github.io/post/2024_dragon_boat_festival/ Sun, 16 Jun 2024 11:17:34 +0800 https://ppd0705.github.io/post/2024_dragon_boat_festival/ <p>端午节回家待了几天,做点记录</p> <h1 id="生机">生机</h1> <p>我们这里现在只种一季水稻,刚好端午节前后就是插秧的时节,这个项目目前还没有被很好地机械化,工作量还蛮大,在家人们的相互帮忙下合力完成</p> <link rel="stylesheet" href="https://ppd0705.github.io/css/hugo-easy-gallery.css" /> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/01.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/01.jpeg" itemprop="contentUrl"></a> </figure> </div> <p>燕子也回来了 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/02.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/02.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>一些水果 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/2024_dragon_boat_festival/03.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/03.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/03.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/2024_dragon_boat_festival/04.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/04.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/04.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/2024_dragon_boat_festival/05.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/05.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/05.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>一些蔬菜 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/2024_dragon_boat_festival/06.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/06.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/06.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/2024_dragon_boat_festival/07.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/07.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/07.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/2024_dragon_boat_festival/08.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/08.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/08.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>当下小学生社交通证 &mdash; 烟卡 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/09.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/09.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>教他们玩我的时代的游戏 &mdash; 跳棋 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/12.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/12.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>自创规则地玩扑克 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/11.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/11.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>小朋友的想象 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/10.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/10.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <p>给你花花 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/13.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/13.jpeg" itemprop="contentUrl"></a> </figure> </div> 也给你 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/14.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/14.jpeg" itemprop="contentUrl"></a> </figure> </div> 她好快乐啊 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/15.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/15.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <h1 id="老去">老去</h1> <p>高铁上和我一同从深圳回家的三伯母</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/16.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/16.jpeg" itemprop="contentUrl"></a> </figure> </div> <p>路上偶遇小姑父姑妈</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/18.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/18.jpeg" itemprop="contentUrl"></a> </figure> </div> <p>终于拿到退休证的爸爸,虽然每月只有一千多,但作为一个没有退休的农民而言,也算难得。</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/17.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/17.jpeg" itemprop="contentUrl"></a> </figure> </div> <p>大姨父一个人独居,最近独自醉酒后离世,挂历还在。 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/19.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/19.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <h1 id="结尾">结尾</h1> <p>自然而然 <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/2024_dragon_boat_festival/20.jpeg" /> </div> <a href="https://ppd0705.github.io/image/2024_dragon_boat_festival/20.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> 五一肯尼亚之旅 https://ppd0705.github.io/post/kenya_trip/ Sun, 19 May 2024 15:52:44 +0800 https://ppd0705.github.io/post/kenya_trip/ <h1 id="前言">前言</h1> <p>其实很早就看到了李奕写的<a href="https://mp.weixin.qq.com/s/w53mDRVF1h7uc2U7sVXRmQ">报名介绍</a>,想着要不趁机去看看《动物世界》记录片里面的非洲大草原,我甚至年初写到了今年计划里面。 但一犹豫金钱和时间,就拖延起来,后面三月份才把自己从候补名单里面捞出来。</p> <h1 id="行前准备">行前准备</h1> <p>机票我在飞猪上购买的,选的埃塞俄比亚航空,抵达时间比较合适,价格也比较便宜。 (后面才知到机票可以直接从官网买可能价格更优;另外埃塞俄比亚首都机场是中国人前往非洲的最大中转站,来回都遇到不同非洲国家工作的中国人)</p> <p>去肯尼亚需要打黄热疫苗,但深圳和广州很难预约,据电话了解好像一天就 20 个名额,感觉不比春节火车票容易。 后面去苏州出差的第一天,我随手看了一下,苏州和常州的很好预约,于是我在苏州打了疫苗。</p> <p>肯尼亚温度在二十多度,和国内南方差不多,在最后一天花了两个小时打包行李,我背着一个 30L 的双肩包就出发了。</p> <h1 id="内罗毕第0-1天">内罗毕(第0-1天)</h1> <p>早上达到了埃塞尔比亚机场等待转机,阴雨天气,和其他航班来的 4 个小伙伴汇合。</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <link rel="stylesheet" href="https://ppd0705.github.io/css/hugo-easy-gallery.css" /> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/airport_1.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/airport_1.jpeg" alt="机场还有祈祷室!"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/airport_1.jpeg" itemprop="contentUrl"></a> <figcaption> <p>机场还有祈祷室!</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/airport_2.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/airport_2.jpeg" alt="写博客的时候才发现里面有后面才认识的小伙伴"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/airport_2.jpeg" itemprop="contentUrl"></a> <figcaption> <p>写博客的时候才发现里面有后面才认识的小伙伴</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/airport_3.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/airport_3.jpeg" alt="他们围着坐着在充电"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/airport_3.jpeg" itemprop="contentUrl"></a> <figcaption> <p>他们围着坐着在充电</p> </figcaption> </figure> </div> </div> <p>中午抵达内罗毕机场,发现是个大晴天,感觉云从地里冒出来的,好惊喜。</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/airport_4.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/airport_4.jpeg" alt="下机后发现天气真好"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/airport_4.jpeg" itemprop="contentUrl"></a> <figcaption> <p>下机后发现天气真好</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/airport_6.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/airport_6.jpeg" alt="云是从地里冒出来的吗"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/airport_6.jpeg" itemprop="contentUrl"></a> <figcaption> <p>云是从地里冒出来的吗</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/airport_5.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/airport_5.jpeg" alt="好多接机的人"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/airport_5.jpeg" itemprop="contentUrl"></a> <figcaption> <p>好多接机的人</p> </figcaption> </figure> </div> </div> <p>晚上已经到了的 11 个小伙伴以及李奕一起碰面了,吃了粤菜。暖场自我介绍活动大概是这样的:叫什么,哪里人,学什么,做什么,一个个人趣事或者小时候经历。 人太多,只记得个大概,哈哈。</p> <p>第二天上午,我们在当地向导的带领和警察的保护下,我们参观了基贝拉──非洲第一大贫民窟。</p> <p>入口内外主要是一些服装店铺,很多货源都来自中国 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_1.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_1.jpeg" alt="市场外围,很多中国来的二手衣服"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_1.jpeg" itemprop="contentUrl"></a> <figcaption> <p>市场外围,很多中国来的二手衣服</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_24.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_24.jpeg" alt="市场外围,鞋子600先令起"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_24.jpeg" itemprop="contentUrl"></a> <figcaption> <p>市场外围,鞋子600先令起</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_26.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_26.jpeg" alt="市场内部,衣服店"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_26.jpeg" itemprop="contentUrl"></a> <figcaption> <p>市场内部,衣服店</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_2.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_2.jpeg" alt="市场入口,比较狭窄,牵手的母女"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_2.jpeg" itemprop="contentUrl"></a> <figcaption> <p>市场入口,比较狭窄,牵手的母女</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_3.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_3.jpeg" alt="市场内部,比较泥泞"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_3.jpeg" itemprop="contentUrl"></a> <figcaption> <p>市场内部,比较泥泞</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_25.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_25.jpeg" alt="市场内部,比较狭窄"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_25.jpeg" itemprop="contentUrl"></a> <figcaption> <p>市场内部,比较狭窄</p> </figcaption> </figure> </div> </div> </p> <p>菜市场 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_4.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_4.jpeg" alt="菜市场,4-20 表示4个20先令"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_4.jpeg" itemprop="contentUrl"></a> <figcaption> <p>菜市场,4-20 表示4个20先令</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_27.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_27.jpeg" alt="这个摊位的东西好像都比较常见"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_27.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这个摊位的东西好像都比较常见</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_6.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_6.jpeg" alt="各种水果"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_6.jpeg" itemprop="contentUrl"></a> <figcaption> <p>各种水果</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_28.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_28.jpeg" alt="洋葱洋葱洋葱"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_28.jpeg" itemprop="contentUrl"></a> <figcaption> <p>洋葱洋葱洋葱</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_29.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_29.jpeg" alt="番茄番茄番茄"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_29.jpeg" itemprop="contentUrl"></a> <figcaption> <p>番茄番茄番茄</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_7.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_7.jpeg" alt="番茄大亨给我们介绍番茄"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_7.jpeg" itemprop="contentUrl"></a> <figcaption> <p>番茄大亨给我们介绍番茄</p> </figcaption> </figure> </div> </div> </p> <p>居住区 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_30.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_30.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_30.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_9.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_9.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_9.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_10.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_10.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_10.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>各种个性十足的巴士,感觉售票员是个危险职业 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_12.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_12.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_12.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_13.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_13.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_13.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_14.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_14.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_14.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_32.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_32.jpeg" alt="好酷"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_32.jpeg" itemprop="contentUrl"></a> <figcaption> <p>好酷</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_31.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_31.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_31.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_33.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_33.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_33.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> 房子上也有各种涂鸦作品 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_15.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_15.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_15.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_16.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_16.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_16.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_17.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_17.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_17.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>泥地足球 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_18.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_18.jpeg" alt="球出界了"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_18.jpeg" itemprop="contentUrl"></a> <figcaption> <p>球出界了</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_19.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_19.jpeg" alt="感觉比较难进球"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_19.jpeg" itemprop="contentUrl"></a> <figcaption> <p>感觉比较难进球</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_20.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_20.jpeg" alt="坐在铁轨上看球"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_20.jpeg" itemprop="contentUrl"></a> <figcaption> <p>坐在铁轨上看球</p> </figcaption> </figure> </div> </div> 一路路过好多个小的基督教堂,里面大都放着欢快的歌谣,我们后面参观了一个,里面实在太嗨了,简直就是一个大型蹦迪现场。 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_21.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_21.jpeg" alt="小小的房子里面站满了人"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_21.jpeg" itemprop="contentUrl"></a> <figcaption> <p>小小的房子里面站满了人</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_22.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_22.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_22.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/kibera_23.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/kibera_23.jpeg" alt="是牧师也是MC"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/kibera_23.jpeg" itemprop="contentUrl"></a> <figcaption> <p>是牧师也是MC</p> </figcaption> </figure> </div> </div> 这个与我对教堂的固有印象大相径庭,后面看到<a href="https://book.douban.com/subject/34984904/">《穿越非洲两百年》</a>书里的描述才理解。</p> <blockquote> <p>教堂里,人们敲锣打鼓,载歌载舞,极具非洲特色。这或许与传教士们的努力有关,为了传播信仰,他们改造了基督教严肃的一面,将非洲的歌舞成功地引入其中,形成了快速的传播。</p> </blockquote> <p>最后我们参观了向导的家,十平米左右大小,一家六口住在一起,她说 &ldquo;We don&rsquo;t have much but we have each other&rdquo;,这让我想起了 up 主蕾儿乔什看世界的视频 <a href="https://www.bilibili.com/video/BV1oj421D7YM/">我在印度最大的贫民窟生活了三天</a>。</p> <h1 id="瓦塔木第1-3天">瓦塔木(第1-3天)</h1> <p>午饭过后我们乘坐小飞机到了印度洋旁边的小镇瓦塔木</p> <p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_1.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_1.jpeg" alt="这种飞机好像能坐20人左右"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_1.jpeg" itemprop="contentUrl"></a> <figcaption> <p>这种飞机好像能坐20人左右</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_2.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_2.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_2.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_3.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_3.jpeg" alt="也有大一点的飞机"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_3.jpeg" itemprop="contentUrl"></a> <figcaption> <p>也有大一点的飞机</p> </figcaption> </figure> </div> </div> 飞机上俯瞰 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_30.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_30.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_30.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_32.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_32.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_32.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_31.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_31.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_31.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>我们入住的酒店叫 TreeHouse,整个酒店就置身于海边的树林里。 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_26.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_26.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_26.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_27.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_27.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_27.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_28.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_28.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_28.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_24.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_24.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_24.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_25.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_25.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_25.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_23.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_23.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_23.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>屋顶俯瞰周边</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_4.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_4.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_4.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_9.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_9.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_9.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_22.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_22.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_22.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_10.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_10.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_10.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_14.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_14.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_14.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_21.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_21.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_21.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> <p>我住在屋顶的房间,名字叫 starbed <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_6.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_6.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_6.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_7.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_7.jpeg" alt="露天浴室?"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_7.jpeg" itemprop="contentUrl"></a> <figcaption> <p>露天浴室?</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_8.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_8.jpeg" alt="想的话可以来个日出浴?"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_8.jpeg" itemprop="contentUrl"></a> <figcaption> <p>想的话可以来个日出浴?</p> </figcaption> </figure> </div> </div> </p> <p>果然名不虚传,仰望天空 360 度全是星星,晚上大家轮流在大沙发上躺着看星空聊天 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_33.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_33.jpeg" alt="房间外是个大阳台"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_33.jpeg" itemprop="contentUrl"></a> <figcaption> <p>房间外是个大阳台</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_34.JPG');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_34.JPG" alt="好多星星"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_34.JPG" itemprop="contentUrl"></a> <figcaption> <p>好多星星</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_35.JPG');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_35.JPG" alt="好多星星&#43;n"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_35.JPG" itemprop="contentUrl"></a> <figcaption> <p>好多星星&#43;n</p> </figcaption> </figure> </div> </div> </p> <p>能看星星,当然也能看日出 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_13.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_13.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_13.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_11.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_11.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_11.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_12.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_12.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_12.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>另一栋房子的屋顶是一个视野超好的瑜伽室,我个人本身对瑜伽和冥想都不太感冒,但想着来都来了体验一下, 结果这个体验成了我的一个 high point。过程中不扣动作细节,更注重呼吸和和身心的连接,练完感觉整个人既放松又充满能量。</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_17.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_17.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_17.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_15.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_15.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_15.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_16.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_16.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_16.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> <p>另外我们还去划了皮划艇,去年在万荣在向导的带领下还落水的我这次没有落水,哈哈,感谢 Nico~</p> <p>好原生态的水域 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_36.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_36.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_36.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_45.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_45.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_45.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_46.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_46.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_46.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>中途上岸参观了一下红树林,超级原生态!水沟里面有好多小海螺,沙地里有数不清的小螃蟹 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_50.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_50.jpeg" alt="好多种子和小树苗"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_50.jpeg" itemprop="contentUrl"></a> <figcaption> <p>好多种子和小树苗</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_51.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_51.jpeg" alt="水渠里面有好多海螺"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_51.jpeg" itemprop="contentUrl"></a> <figcaption> <p>水渠里面有好多海螺</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_58.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_58.jpeg" alt="好多小螃蟹,跑的超快"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_58.jpeg" itemprop="contentUrl"></a> <figcaption> <p>好多小螃蟹,跑的超快</p> </figcaption> </figure> </div> </div> </p> <p>继续前行,很幸运地看到了火烈鸟,听说它们一般不来这里 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_38.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_38.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_38.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_39.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_39.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_39.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_40.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_40.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_40.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>大家绑在一起看落日,向导还给准备了爆米花和茶,巴适! <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_41.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_41.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_41.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_42.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_42.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_42.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_44.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_44.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_44.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_53.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_53.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_53.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_55.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_55.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_55.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_57.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_57.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_57.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> 返程啦,在太阳落山之后的余烬光辉中,大家在三三两两地聊天,感觉世界很安静。 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_43.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_43.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_43.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_54.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_54.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_54.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/watamu_56.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/watamu_56.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/watamu_56.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <h1 id="内罗毕第3-4天">内罗毕(第3-4天)</h1> <p>回内罗毕中转,在小飞家吃天水麻辣烫,吃的好撑(二手瓜的原因?),最后的红烧肉反而吃不下了。</p> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" style="max-width:300px" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/nairobi_1.jpeg" alt="内罗毕也是个鲜花城市"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/nairobi_1.jpeg" itemprop="contentUrl"></a> <figcaption> <p>内罗毕也是个鲜花城市</p> </figcaption> </figure> </div> <h1 id="马赛马拉第4-7天">马赛马拉(第4-7天)</h1> <p>又坐小飞机啦,发现机场居然可以是泥地!</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_4.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_4.jpeg" alt="天气好好"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_4.jpeg" itemprop="contentUrl"></a> <figcaption> <p>天气好好</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_5.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_5.jpeg" alt="飞机降落,转弯了"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_5.jpeg" itemprop="contentUrl"></a> <figcaption> <p>飞机降落,转弯了</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_7.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_7.jpeg" alt="我们相当于包机了"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_7.jpeg" itemprop="contentUrl"></a> <figcaption> <p>我们相当于包机了</p> </figcaption> </figure> </div> </div> <p>飞机飞的好低,来一波俯瞰图 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_50.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_50.jpeg" alt="树木沿着河流生长"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_50.jpeg" itemprop="contentUrl"></a> <figcaption> <p>树木沿着河流生长</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_51.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_51.jpeg" alt="看起来被洪水淹了"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_51.jpeg" itemprop="contentUrl"></a> <figcaption> <p>看起来被洪水淹了</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_52.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_52.jpeg" alt="左边好多牛羊"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_52.jpeg" itemprop="contentUrl"></a> <figcaption> <p>左边好多牛羊</p> </figcaption> </figure> </div> </div> </p> <p>有朋友好奇我们怎么看动物的?是否安全?那首先给大家看看我们坐的车,车是完全敞开式的,相比天窗式,其实更方便观察动物</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_48.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_48.jpeg" alt="和另一辆车相互对拍"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_48.jpeg" itemprop="contentUrl"></a> <figcaption> <p>和另一辆车相互对拍</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_47.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_47.jpeg" alt="在狮子眼中我们都是NPC"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_47.jpeg" itemprop="contentUrl"></a> <figcaption> <p>在狮子眼中我们都是NPC</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_49.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_49.jpeg" alt="下雨天也可以应对"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_49.jpeg" itemprop="contentUrl"></a> <figcaption> <p>下雨天也可以应对</p> </figcaption> </figure> </div> </div> <p>下面开始动物大赏!</p> <p>斑马斑马,憨憨的斑马</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_25.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_25.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_25.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_24.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_24.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_24.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_36.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_36.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_36.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_37.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_37.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_37.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_38.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_38.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_38.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_34.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_34.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_34.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> <p>狮子狮子,嬉戏的狮子 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_28.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_28.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_28.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_29.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_29.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_29.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_30.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_30.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_30.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_27.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_27.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_27.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_32.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_32.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_32.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_33.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_33.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_33.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>其他可爱的动物 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_9.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_9.jpeg" alt="近距离看到的第一个大型动物"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_9.jpeg" itemprop="contentUrl"></a> <figcaption> <p>近距离看到的第一个大型动物</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_10.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_10.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_10.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_11.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_11.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_11.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_35.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_35.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_35.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_16.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_16.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_16.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_26.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_26.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_26.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_54.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_54.jpeg" alt="雨中的大象家族"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_54.jpeg" itemprop="contentUrl"></a> <figcaption> <p>雨中的大象家族</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_53.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_53.jpeg" alt="角马"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_53.jpeg" itemprop="contentUrl"></a> <figcaption> <p>角马</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_46.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_46.jpeg" alt="最后的早上看到了吗喽家族"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_46.jpeg" itemprop="contentUrl"></a> <figcaption> <p>最后的早上看到了吗喽家族</p> </figcaption> </figure> </div> </div> </p> <p>来点小伙伴们用相机拍的的近景图 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_64.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_64.jpeg" alt="Dik-dik 兔子般大小的羚羊"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_64.jpeg" itemprop="contentUrl"></a> <figcaption> <p>Dik-dik 兔子般大小的羚羊</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_60.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_60.jpeg" alt="疣猪 彭彭"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_60.jpeg" itemprop="contentUrl"></a> <figcaption> <p>疣猪 彭彭</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_57.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_57.jpeg" alt="河马上岸了"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_57.jpeg" itemprop="contentUrl"></a> <figcaption> <p>河马上岸了</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_62.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_62.jpeg" alt="嬉戏的狮子"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_62.jpeg" itemprop="contentUrl"></a> <figcaption> <p>嬉戏的狮子</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_58.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_58.jpeg" alt="你瞅啥"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_58.jpeg" itemprop="contentUrl"></a> <figcaption> <p>你瞅啥</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_59.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_59.jpeg" alt="看到小象了吗"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_59.jpeg" itemprop="contentUrl"></a> <figcaption> <p>看到小象了吗</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_56.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_56.jpeg" alt="好肥的鬣狗"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_56.jpeg" itemprop="contentUrl"></a> <figcaption> <p>好肥的鬣狗</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_61.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_61.jpeg" alt="秃鹫在和鬣狗抢腐食"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_61.jpeg" itemprop="contentUrl"></a> <figcaption> <p>秃鹫在和鬣狗抢腐食</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_63.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_63.jpeg" alt="看到豹子的食物约等于看到豹子了吧?"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_63.jpeg" itemprop="contentUrl"></a> <figcaption> <p>看到豹子的食物约等于看到豹子了吧?</p> </figcaption> </figure> </div> </div> </p> <p>去酒店的路上,我们先救了一辆已经陷进泥潭的面包车,后面我们自己的另外一辆车也陷进去啦,我们在唱 Hakuna Matata 给大家加油~ <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_12.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_12.jpeg" alt="先拉出了面包车"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_12.jpeg" itemprop="contentUrl"></a> <figcaption> <p>先拉出了面包车</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_13.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_13.jpeg" alt="尝试往前拉车"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_13.jpeg" itemprop="contentUrl"></a> <figcaption> <p>尝试往前拉车</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_14.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_14.jpeg" alt="后面后退来拉来的"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_14.jpeg" itemprop="contentUrl"></a> <figcaption> <p>后面后退来拉来的</p> </figcaption> </figure> </div> </div> </p> <p>马赛马拉最后的午餐,我们坐在树荫下,边吃午餐,边随意分享一部自己喜欢的文学影视等作品 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_66.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_66.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_66.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_69.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_69.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_69.jpeg" itemprop="contentUrl"></a> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_68.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_68.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_68.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> </p> <p>大概记得有如下:</p> <ul> <li>动漫《排球小子》</li> <li>书 《命运》、《被讨厌的勇气》</li> <li>综艺 《种地吧》、《无限挑战》</li> <li>电影 《三傻》、《爱在三部曲》</li> <li>电视剧 《0.5的男人》</li> </ul> <p>我分享的是鲁叔的音乐<a href="https://music.163.com/#/song?id=28465109&amp;userid=16819894">《白云》</a>,当场播放起来,成了很应景的背景音,甚至被有的小伙伴当做是当天的 high point。</p> <p>(ps 回来后我已经看起了《命运》)</p> <p>多才多艺的同行小伙伴里面有一个故事大王 Echo,她正好是做遗愿清单工作坊的,于是我们饭后趁机免费体验了一次。</p> <p>作为暖场,Echo 先分享了她自己的一个爱情故事,好多小伙伴给听哭了 (-_-。)</p> <p>工作坊的简要流程是先冥想放松,想象自己的一生,回答如下问题</p> <ul> <li>理想寿命</li> <li>死前状态的三个关键词</li> <li>写出十条清单</li> <li>选出其中最重要的三条</li> <li>确定每条完成的时间点 (相互提问完之后回答)</li> </ul> <p>大家当场分享了理想寿命和死前关键词,80岁好像是最低的数字,哈哈! 我们的唯一CP中的男生说要活 124 岁, 为了走在对方后面。好感人 !(听说他们晚上回去因为这个先后问题还争吵了)</p> <p>死前关键词其实大家大差不太多。</p> <p>另外还有一个环节就是两两分组,通过给对方的清单提问,来帮助对方对对清单更明确和深刻。</p> <p>我和李奕一组,我们的清单风格差别挺大的,我一开始看了挺诧异的,她竟然划掉了我认为很重要的点。后面我想了想, 她这样做是因为本身在这个划去的点上已经做的很好了,另外她留下的点让我想到了&quot;能量越大,影响力越大&quot;的观点, 对比起来,我写的都是很个人的事情,哈哈。 因为没有提前想过,我就写出了五条,有的还有点重合。李奕帮我问了我很多问题,让我对我写下的答案清晰了不少。</p> <p>当晚晚餐的时候,大家每个人都分享了感觉稍稍私密的清单,不同的人确实还是有不一样的,有的很具体,有的很宏观。</p> <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_42.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_42.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_42.jpeg" itemprop="contentUrl"></a> </figure> </div> </div> <p>最后一天上午,我们参观了一个马赛人的村庄 <div class="gallery caption-position-bottom caption-effect-slide hover-effect-zoom hover-transition" itemscope itemtype="http://schema.org/ImageGallery"> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_17.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_17.jpeg" alt="手持木棍的马赛人"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_17.jpeg" itemprop="contentUrl"></a> <figcaption> <p>手持木棍的马赛人</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_18.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_18.jpeg" alt="人畜自然地生活在一起"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_18.jpeg" itemprop="contentUrl"></a> <figcaption> <p>人畜自然地生活在一起</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_19.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_19.jpeg" alt="咩咩咩"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_19.jpeg" itemprop="contentUrl"></a> <figcaption> <p>咩咩咩</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_20.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_20.jpeg" alt="他们的房子围成一个圈,可能牛羊就关在中间"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_20.jpeg" itemprop="contentUrl"></a> <figcaption> <p>他们的房子围成一个圈,可能牛羊就关在中间</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_21.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_21.jpeg" alt="房子近景"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_21.jpeg" itemprop="contentUrl"></a> <figcaption> <p>房子近景</p> </figcaption> </figure> </div> <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img" style="background-image: url('/image/kenya_trip/masai_23.jpeg');"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_23.jpeg" alt="房子内部,女主人正在烙饼,房子左右两边都是床"/> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_23.jpeg" itemprop="contentUrl"></a> <figcaption> <p>房子内部,女主人正在烙饼,房子左右两边都是床</p> </figcaption> </figure> </div> </div> </p> <p>时间飞快,得说再见了!马赛马拉! <div class="box"> <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" > <div class="img"> <img itemprop="thumbnail" src="https://ppd0705.github.io/image/kenya_trip/masai_2.jpeg" /> </div> <a href="https://ppd0705.github.io/image/kenya_trip/masai_2.jpeg" itemprop="contentUrl"></a> </figure> </div> </p> <h1 id="后记">后记</h1> <p>这算是我第一次参团中长途旅行,整体体验还蛮好的,真的是把自己带来就行了,其他的都安排好了。我们超级幸运, 航班没有延误,行程没有遇到洪水(同期很多人被洪水影响,甚至产生生命危险)。</p> <p>每晚的 high/low point 分享,即是每个人对一天的回顾,也是相互了解和熟悉的契机,是很好的仪式流程。</p> <p>把自己抛到一个完全陌生的环境是一个很好的自我了解的机会,我感觉我又更加了解自己一点了。</p> <p>作为唯一一个英语不行的人,感谢同行小伙伴的帮助,让我旅行体验提升很多。</p> <p>到马赛马拉的那天,天气骤变,我中暑发烧了,还有点慌,多谢 Chloe 万里带来的藿香正气水。搞笑的是最后回内罗毕后, 才发现不少人也中暑了,盈盈给其他人都进行了中国传统艺能——刮痧,房间里不时传出奇怪的惨叫声。(ps 去马赛马拉注意防风保暖)</p> <p>吃了很多好吃的,但光顾着吃了,没怎么拍美食~</p> <p>Shuming 有条清单是&quot;reconnect with old friends&quot;,我去了好久不去的广州见了大学室友。</p> <p>回来时差倒了好几天,终于正常了~</p> 2024出差小记 https://ppd0705.github.io/post/2024_business_trip/ Thu, 04 Apr 2024 16:22:03 +0800 https://ppd0705.github.io/post/2024_business_trip/ <h1 id="前言">前言</h1> <p>关于这次苏州出差行,我在春节前就有了念头,但迟迟没有最后确定日期,因为这个纯属我个人主观想法。 新近合并的团队成员在苏州,只见了一面,他们随口提到办公室有多余的工位,感觉对方还是蛮好相处以及有很多可以学习的地方, 我想也许大家线下办公一段时间可以交流了解地更充分。所以这其实是一次自费自愿出差,然后拖延症如我,周四才决定下周一去。</p> <h1 id="出差">出差</h1> <p>为了最大化周末时间,我选择了周日晚上坐动卧早上到上海再转高铁到苏州。虽然办公室在北边的万达广场,但我还是特意选择了 从南广场出来,看看亲切的平门城楼,然后慢慢步行看看两边的街景,溜达着前往办公室。</p> <p><img src="https://ppd0705.github.io/image/2024_business_trip/01.jpeg" alt="01"></p> <p>到了才知道,为了避开早高峰,他们提前到八点开始上班,另外他们自费租的办公室视野很好,工位也挺多。 十分钟步行通勤时间,宽敞放松的办公环境,久违的线下办公感觉还是很好的。除了交流双方的工作模式外,因为线下,确实多了 好多闲聊的机会,很多聊天内容是不太可能在线上通讯软件上产生的。另外世界好小,发现居然还有共同认识的人。</p> <p>总的来说,对于目前感觉处在能力瓶颈有点闭门造车的我,新成员的加入给了我一些新的探索方向的启发。</p> <h1 id="赏景">赏景</h1> <p>阴差阳错,办公室旁边就是春分街,来的这周又正好是耶稣受难节和复活节(因为放假第一次知道这两个节日),所以我错峰出行地去逛了网师园和留园。 不得不说,苏州花红叶绿的春天的实在太美了,眼睛和心灵都得到洗涤。</p> <p><img src="https://ppd0705.github.io/image/2024_business_trip/02.jpeg" alt="02"></p> <h1 id="访友">访友</h1> <p>作为一个内向独立的人,友谊维系是一个我很需要修炼的人生课题。</p> <p>感谢对我的一声吆喝就做出积极回应的大学室友们,也感谢我自己,让分别位于江浙沪的他们再次聚集起来。 异地聚会确实挺不容易的,但我们没有具体的计划,定好了大概的时间地点,后面就都是随机行事了。 虽然有一些时日没见,但线下面对面交流起来大家就慢慢熟络起来,大部分时间我们都在吃吃喝喝聊天中度过,聊聊各自的生活和工作,同一大学专业的我们如今所做的职业和行业都不相同。 通过这次近距离的相处,我发现可能变化最大的是 Kenny 同学,对,他居然给自己微信取了一个英文昵称! 才知道他离开了当初我们俩共同校招进去的待了七年的公司,我以为他大概率不会主动离开的。 另外人也变得主动热情多了,在说到老婆孩子在附近的月子中心邀请我们去看的时候,另外两个同学觉得会有打扰或者不方便婉言拒绝的时候(我内心是想去的),仍再三邀请,于是我们一起去看望了黄老师和 22 天大的端端小朋友,在月子中心的 一小时感觉时间变慢了,世界变安静了,大家的注意力都放在了这个如此小的人类幼崽身上;</p> <p><img src="https://ppd0705.github.io/image/2024_business_trip/03.jpeg" alt="03"></p> <p>当大家关切我的婚恋问题时,他多次说如果需要他可以帮忙介绍,虽然异地本来可能性不大,但我知道他不是在说客套话,这个话是对我的肯定和关心,这个话我不太可能会说,我感觉以前的他也不太会说,我想是良好的夫妻关系改变了他吧。 晚上九点主场散去,剩下的三个人又喝起了茶,聊了一些私人话题,大家都有各自的课题需要去应对。 十一点左右,Kenny 同学突然提起:要不去网吧吧?于是在时隔八年之后,我们再次一齐走向网吧,在那有点寒冷的春夜街头,我们有种逃课出来偷偷上网的感觉,搞笑的是我们找了一个小时才找到一个有三连坐的网吧。 最后当那熟悉的三个 ID 再次出现在游戏界面时,还是很让人感慨的(我的大刀早已饥渴难耐了)。</p> <p><img src="https://ppd0705.github.io/image/2024_business_trip/04.jpeg" alt="04"></p> <p>另外也分别见了两个第一份工作的同事,只是约一顿饭,但都畅聊了三四个小时,一个四年没见,一个七年没见,好像也没有很生分,线下见面的感觉还是蛮好的。 大家也都各自通过自己的方式换了行业换了职业,聊聊前司,聊聊职业危机,聊聊婚恋问题等等,时间好像一眨眼就过去了。</p> <p>最后我也去上海见了两个只有网络上有交集的朋友,我发现两边离火车站近的话,工作日晚上苏州到上海跨城约个饭也是可以的。我也发现人少的场合我也没那么社恐, 我完全敞开的时候,即使陌生人也可以敞开的聊天。</p> <h1 id="后记">后记</h1> <p>虽然这次去的时候动卧错买成二等座,回来飞机晚点三小时,主动与人链接的时候既笨拙又敏感,但遇到的同事、同学和朋友都很有爱,我吸收到了满满能量。感谢~</p> 2023年终总结 https://ppd0705.github.io/post/2023_summary/ Sun, 31 Dec 2023 16:56:53 +0800 https://ppd0705.github.io/post/2023_summary/ <h1 id="工作">工作</h1> <p>今年工作的关键词依然是重构。后端从 flask+sqlalchemy+celery 换到了 gin+gorm+cron,而前端从 jQuery+Bootstrap 换到了 Vue+AntDesign。 这是我第一次写前后端分离的项目,之前也没有用过 Vue,刚开始完成 MVP 版本的时候还是蛮开心的,到后面就是漫长的实现业务需求的阶段。 不过做为一个需求分析、设计、前端、后端、测试的全沾工程师好处就是踩过所有坑体会每一个细节、随时重构、只保留必要的功能。</p> <p><img src="https://ppd0705.github.io/image/2023_summary/github_contributors.png" alt="github_contributors"></p> <p>今年是完全远程工作的一年,远程工作的好处之一就是可以四处打工,我在老挝和云南工作过 一段时间,不过体验并没有我想象的那么美好。首先我的工作需要很多时候需要自己去探索和调研,很费精力,工作日基本一眨眼就天黑了; 其次因为一直在换地方,我得不停考虑住哪里,洗衣服方便不,去哪里吃饭;最后我发现我也没那么喜欢旅行,我可能主旋律还是习惯待在一个熟悉的环境里面,所以 我今年主要都在图书馆工作,喜欢这个充满着学习和思考氛围、男女老少皆宜的场域。</p> <p><img src="https://ppd0705.github.io/image/2023_summary/yulongxueshan.jpg" alt="snowy_mountain_view_workstation"></p> <p>职业发展可能是每个打工人都会感到迷茫和困惑的话题,我今年年中有去尝试去争取一下, 对我个人来说算一次勇敢尝试,也发现了我不知道的的游戏规则,另外还是相信真诚和以身作则是最好的领导力。</p> <h1 id="生活">生活</h1> <p>今年旅行主要去了老挝、云南、景德镇。 我个人旅行风格是只做大概的规划,但大晚上到了老挝,乌漆嘛黑,语言不通,手机快没电,多多少少还是有点慌的,感谢后面一同结伴的叔叔阿姨们。 老挝行总体来说作为一个外国人去感受一个语言、饮食、信仰完全不一样的国度去感受一下还是蛮开拓视野的。 云南行也算一次新奇的体验。当你刚到昆明这个城市坐上地铁,在某一个站你发现上车的每个人都抱着两束花,你会想这个城市这么浪漫吗? 当你去逛菜市场,你会发现很多当地特有的食物,你会发现这里不单单按公斤计价,有的还按堆计价。 当你出了丽江火车站,一抬头,发现远方不只有白云,居然还有雪山,后面你会发现这里到处都能看到雪山。 当你去了大理,你会发现这里到处赶集,人潮涌动;你会发现这里酒吧也可以是路边摊的形式,一把吉他,几条折叠板凳;你会发现这里不少素食馆,味道还蛮好的。 当你到了景德镇,你会发现琳琅满目的陶瓷作品,以及作品背后的工作室和作坊;你会发现真的有人来这学习手艺,成为景漂的一员。</p> <p><img src="https://ppd0705.github.io/image/2023_summary/hutiaoxia.jpg" alt="hutiaoxia"></p> <p>今年也做了一些破旧立新的尝试。 换了从大学时就带起的眼镜,世界好像是用眼睛看的清晰一点了。 另外可能是成长经历的原因,其实一直有点害怕去医院,在姐姐的陪伴下去了医院,看了一下医生,治好了一个拖延很久的小毛病,终于首次使用上了医保。 有一天看小红书突然兴起,决定自己动手把住了六年的房间重新刷一下墙漆,于是花了一个周末边学边做完成了这个开始时不觉得艰巨的任务。最后效果只能说勉强还行,但做之前玩笑说可以将刷墙发展成第二职业看来很难有竞争力。 今年重新拾起了好多年没有打过的羽毛球,感觉是个不错的运动,可惜后面脚受伤之后就没打了。 今年甚至下载了青藤之恋尝试去交友,算是不错的尝试,更了解自已一点了吧,另外也在上面围观其他人的人生经历,看到更大的世界。</p> <p>今年跑步跑了 323 公里,和去年持平,也没有参加任何比赛。</p> <p>今年英语换到了多邻国上学习,但用了几个月之后感觉没有什么提高,就中断了。今年刻意减少 B 站的使用时间,希望多在油管上 看看英文视频,但其实在油管上也主要还是看中文视频,哈哈。</p> <p>今年技术上的学习主要在学习如何写前端,工作上需要用到学习确实更有效率;学习 NATS 的设计原理,七年前的 1.0 版本还可以跑,基础软件还是蛮稳定的;C++ 通过 《 C++ 之旅》第三版入了一下门,有 操作符重载和模版这两大魔法,感觉 C++ 好灵活,另外编译器和设计标准不同步感觉有点坑,有的 C++ 20 的功能都没有实现。</p> <p>今年看过的书差不多忘光了,但记得《论世衡史》和 《置身事内》里面有一个类似的观点:无论是闭关锁国,还是自由贸易,背后其实都是 利益多方的复杂博弈的结果。很让人震撼,但也很真实。</p> <h1 id="展望">展望</h1> <ul> <li>日常跑步</li> <li>探索新事情</li> </ul> <h1 id="链接">链接</h1> <ul> <li><a href="https://mp.weixin.qq.com/s/XCEJgoIx9vhJf9ItXEsL8w">2023:装满「多巴胺」的工具箱</a></li> <li><a href="https://mp.weixin.qq.com/s/G0X3Oe5r_7_xJifXEkk7rQ">寻求改变的时候,你的出发点是什么</a></li> <li><a href="https://xargin.com/ghost-story/">一些鬼故事</a></li> <li><a href="https://mp.weixin.qq.com/s/sV3dA-eiOIkrFLFkSWTpvA">让奇迹发生</a></li> </ul> [笔记]C++之旅 https://ppd0705.github.io/post/cpp_tour/ Wed, 29 Nov 2023 18:51:08 +0800 https://ppd0705.github.io/post/cpp_tour/ <h1 id="第一章-基础">第一章 基础</h1> <p>hello world 示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="n">import</span> <span class="n">std</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Hello, World!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="函数">函数</h2> <p>函数声明包含名称、参数和返回值,返回值在名称之前。</p> <p>函数的类型由他的返回值类型和参数类型序列组成 普通函数 <code>double get(const vector&lt;double&gt;&amp; vec, int index)</code> 的类型是 <code>double(const vector&lt;double&gt;&amp;, int)</code>; 而类成员函数包含类名,如<code>char&amp; ClassA::operator[](int index)</code> 的类型是<code>char&amp; ClassA::(int)</code></p> <p>如果两个函数有相同的名称但不同的参数类型,则编译器会选择最适合每个调用的函数</p> <h2 id="类型变量与运算">类型、变量与运算</h2> <p>常见的基础类型有 char、bool、int、unsigned 和double 等等</p> <p>常用算是类型转换会以最高操作对象精度元素,如 double 和 int 类型的加法会以 double 的精度进行</p> <p>可以使用 C语言方式 <code>=</code> 来初始化对象,但建议使用 <code>{}</code> 来初始化,避免隐式类型转换</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="n">doube</span> <span class="n">d1</span> <span class="o">=</span> <span class="mf">2.3</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">double</span> <span class="n">d2</span> <span class="p">{</span><span class="mf">2.3</span><span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">double</span> <span class="n">d3</span> <span class="o">=</span> <span class="p">{</span><span class="mf">2.3</span><span class="p">};</span> <span class="c1">// =符合可以省略 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">i1</span> <span class="o">=</span> <span class="mf">7.3</span><span class="p">;</span> <span class="c1">// 发生隐式转换,i1=7 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">i2</span> <span class="p">{</span><span class="mf">7.8</span><span class="p">}</span> <span class="c1">// 报错! </span></span></span></code></pre></td></tr></table> </div> </div><p>当定义变量时,可以从初始化符号推导出来,可以无需指定类型,用 auto 代替</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">b</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> <span class="c1">// bool 类型 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">123</span><span class="p">;</span> <span class="c1">// int 类型 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="n">d</span> <span class="o">=</span> <span class="mf">1.2</span><span class="p">;</span> <span class="c1">// double 类型 </span></span></span></code></pre></td></tr></table> </div> </div><h2 id="常量">常量</h2> <p>有 2 种常量</p> <ul> <li>const: 主要用来说明接口,编译器负责执行 const 承诺,可以在运行时计算</li> <li>constexpr: 主要用于声明常量,把数据置于只读内存区域,必须由编译器计算</li> </ul> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">var</span> <span class="o">=</span> <span class="mi">17</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">double</span> <span class="n">sqv</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">constexpr</span> <span class="kt">int</span> <span class="n">dmv</span> <span class="o">=</span> <span class="mi">27</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">constexprt</span> <span class="kt">double</span> <span class="n">sqv2</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">var</span><span class="p">);</span> <span class="c1">// 错误:不能是个非常量表达式 </span></span></span></code></pre></td></tr></table> </div> </div><p>被声明为 constexpr 或者 consteval 的函数是 c++ 版本的纯函数,必须在编译时计算,且不能有任何副作用,只能使用输入参数作为信息。</p> <h2 id="指针数组和引用">指针、数组和引用</h2> <p>在声明中,<code>[]</code> 表示对应类型的数组,<code>*</code> 表示指向对应类型的指针,<code>&amp;</code> 表示指向对应对象的引用。 引用和指针类似,但可以不使用前缀<code>*</code>就能直接访问引用对象的值,而且引用初始化滞后就不能再指向其他的对象。</p> <p>在表达式中,前置一元操作符<code>*</code> 表示取内容,前置一元操作符<code>&amp;</code> 表示取地址</p> <p>建议使用 <code>nullptr</code> 而非 <code>0</code> 来表示空指针,避免和整数类型混淆</p> <h2 id="初始化">初始化</h2> <p>初始化是将一段没有被初始化的内存区域变成一个有效的对象,对几乎所有的数据类型而言,对未初始化的对象的读写操作都是未定义的。</p> <p>要让赋值操作成功进行,被赋值的对象必须拥有一个有效的值</p> <h2 id="赋值">赋值</h2> <p>对于内置类型来说,赋值语句就是简单的机器赋值指令。两个对象是独立的,修改 y 值的时候不会影响 x 的值。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> </span></span></code></pre></td></tr></table> </div> </div><p>如果希望不同的对象指向(共享)相同的值,必须明确指定</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="o">*</span> <span class="n">q</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">y</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">p</span> <span class="o">=</span> <span class="n">q</span><span class="p">;</span> <span class="c1">// p 和 q 两个指针都指向了 y </span></span></span></code></pre></td></tr></table> </div> </div><p>给引用赋值改变的是引用对象的值</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="o">&amp;</span> <span class="n">r</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="o">&amp;</span> <span class="n">r2</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="n">r2</span><span class="p">;</span> <span class="c1">// 从 r2 读取,通过 r 写入,x 变成 3 </span></span></span></code></pre></td></tr></table> </div> </div><h1 id="第二章-用户自定义类型">第二章 用户自定义类型</h1> <h2 id="结构">结构</h2> <p>struct 将所需的元素组织在一起</p> <p>如下有一个简单的 Vector struct 示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Vector</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">double</span><span class="o">*</span> <span class="n">elem</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">sz</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vector_init</span><span class="p">(</span><span class="n">Vector</span><span class="o">&amp;</span> <span class="n">v</span><span class="p">,</span> <span class="kt">int</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">elem</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">double</span><span class="p">[</span><span class="n">s</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">sz</span> <span class="o">=</span> <span class="n">s</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">doubel</span> <span class="nf">read_and_sum</span><span class="p">(</span><span class="kt">int</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Vector</span> <span class="n">v</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">vector_init</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">!=</span><span class="n">s</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">v</span><span class="p">.</span><span class="n">elem</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">s</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">+=</span> <span class="n">v</span><span class="p">.</span><span class="n">elem</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">sum</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>自定义类型的名称通常使用首字母大写,以便和标准库类型区分</p> <p>访问 struct 成员有两种方式, 通过名字或引用时用<code>.</code> 符号,通过指针时用<code>-&gt;</code> 符号</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">f</span><span class="p">(</span><span class="n">Vector</span> <span class="n">v</span><span class="p">,</span> <span class="n">Vector</span><span class="o">&amp;</span> <span class="n">rv</span><span class="p">,</span> <span class="n">Vector</span><span class="o">*</span> <span class="n">pv</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i1</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">sz</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i2</span> <span class="o">=</span> <span class="n">rv</span><span class="p">.</span><span class="n">sz</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i3</span> <span class="o">=</span> <span class="n">pv</span><span class="o">-&gt;</span><span class="n">sz</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="类">类</h2> <p>类将数据和操作组织在一起,类的 public 成员定义了该类的接口,private 成交则只能通过接口访问</p> <p>class 和 struct 没有本质区别,唯一区别在于 struct 成员默认是 public 的。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Vector</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">Vector</span><span class="p">(</span><span class="kt">int</span> <span class="n">s</span><span class="p">)</span> <span class="o">:</span> <span class="n">elem</span><span class="p">{</span><span class="k">new</span> <span class="kt">double</span><span class="p">[</span><span class="n">s</span><span class="p">]},</span> <span class="n">sz</span><span class="p">{</span><span class="n">s</span><span class="p">}</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="o">&amp;</span><span class="k">operator</span><span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">elem</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="nf">size</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">sz</span><span class="p">;</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="o">*</span><span class="n">elem</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">sz</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><p>Vector 对象是一个句柄,包含指向 元素的指针(elem) 和元素的数量(sz), Vector 包含 Vector()、 operator[] 和 size() 三个接口。</p> <p>和类名同名的成员函数为构造函数,用来构造类的实例</p> <h2 id="枚举">枚举</h2> <p>枚举类型用来表示少量整数数值的集合,提升代码的可读性,降低潜在错误。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">Color</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">red</span><span class="p">,</span> <span class="n">blue</span><span class="p">,</span> <span class="n">green</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">TrafficLight</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">green</span><span class="p">,</span> <span class="n">yellow</span><span class="p">,</span> <span class="n">red</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><p>enum 后面的 class 表示这个枚举类型是强类型,具备独立的作用域,不同的 enum class 是不同的类型</p> <p>enum class 不可以隐式地和整数混用</p> <h2 id="联合">联合</h2> <p>union 是一种特殊的 struct,他的所有成员被分配在同一块内存区域中,实际占用的空间就是它最大的成员所占用的空间。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">Type</span> <span class="p">{</span><span class="n">ptr</span><span class="p">,</span> <span class="n">num</span><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">union</span> <span class="nc">Value</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span><span class="o">*</span> <span class="n">p</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Entry</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Type</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">Value</span> <span class="n">v</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">f</span><span class="p">(</span><span class="n">Entry</span><span class="o">*</span> <span class="n">pe</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">pe</span><span class="o">-&gt;</span><span class="n">t</span> <span class="o">==</span> <span class="n">Type</span><span class="o">::</span><span class="n">num</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;num: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">pe</span><span class="o">-&gt;</span><span class="n">v</span><span class="p">.</span><span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;ptr: &#34;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="p">(</span><span class="n">pe</span><span class="o">-&gt;</span><span class="n">v</span><span class="p">.</span><span class="n">p</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第三章-模块化">第三章 模块化</h1> <p>c++ 中有两种方式可以实现分离编译</p> <ul> <li>头文件</li> <li>模块</li> </ul> <p>头文件有明显的缺点</p> <ul> <li>编译耗时,对于同一个文件,每 #include 一次编译器就需要处理一次</li> <li>顺序依赖</li> <li>代码膨胀</li> </ul> <p>c++ 20 引入了 <code>module</code>, 使用 import 来引入 module</p> <h2 id="函数参数和返回值">函数参数和返回值</h2> <h3 id="参数传递">参数传递</h3> <p>参数默认使用传值的方式,为了性能,可以使用传引用的方式</p> <h3 id="返回值">返回值</h3> <p>返回值的默认行为也是复制传值。 可以通过给对象提供移动构造方法,将对象移动到函数之外</p> <h3 id="返回类型后置">返回类型后置</h3> <p>相比传统的记法,后置返回记法更符合逻辑</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">auto</span> <span class="nf">next_elem</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">Elem</span><span class="o">*</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="nf">sqrt</span><span class="p">(</span><span class="n">doyble</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">double</span><span class="p">;</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第四章-错误处理">第四章 错误处理</h1> <p>使用 <code>throw error</code> 抛出错误,使用 <code>try ... catch (error)</code> 来捕捉错误</p> <p>错误处理通常有三种方式:</p> <ul> <li>抛出异常</li> <li>返回错误码</li> <li>终止程序(如调用 <code>exit</code>之类的函数)</li> </ul> <h1 id="第五章-类">第五章 类</h1> <h2 id="具体类">具体类</h2> <p>具体类的典型特征是它的成员变量是其定义的一部分</p> <p>RAII (Resource Acquisition Is Initialization) 资源获取即初始化 是 c++ 一种惯用管理内存的方式, 它能保证已构造的对象,最后会被销毁,即其析构函数会被调用。</p> <h2 id="抽象类">抽象类</h2> <p>抽象类将使用者和类的实现细节完全隔离,即将接口和实现完全解耦,并放弃了纯局部变量</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Container</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="kt">double</span><span class="o">&amp;</span> <span class="k">operator</span><span class="p">[](</span><span class="kt">int</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="kt">int</span> <span class="nf">size</span><span class="p">()</span> <span class="k">const</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">Container</span><span class="p">()</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><p>如上所示, Container 即为一个抽象类。使用 关键字<code>virtual</code> 声明的函数为虚函数,虚函数可能会在派生类中被重新定义。 <code>= 0</code> 后缀表示该函数为纯虚函数,必须在派生类中定义该函数。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">List_container</span><span class="o">:</span><span class="k">public</span> <span class="n">Container</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">ld</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">List_container</span><span class="p">()</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="n">List_container</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">initializer_list</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">il</span><span class="p">)</span><span class="o">:</span> <span class="n">ld</span><span class="p">{</span><span class="n">il</span><span class="p">}</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="o">~</span><span class="n">List_container</span><span class="p">()</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">double</span><span class="o">&amp;</span> <span class="k">operator</span><span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="k">override</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="nf">size</span><span class="p">()</span> <span class="k">const</span> <span class="k">override</span> <span class="p">{</span> <span class="k">return</span> <span class="n">ld</span><span class="p">.</span><span class="n">size</span><span class="p">();}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">double</span><span class="o">&amp;</span> <span class="n">List_container</span><span class="o">::</span><span class="k">operator</span><span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">x</span><span class="p">:</span> <span class="n">ld</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="o">--</span><span class="n">i</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">out_of_range</span><span class="p">{</span><span class="s">&#34;List container&#34;</span><span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p><code>: public </code> 表示&quot;派生自&quot;或者&quot;是&hellip;的子类型&quot;。基类和派生类的关系称之为继承。</p> <p><code>override</code> 是可选的,但可以显示声明覆盖基类对应函数的意图</p> <h2 id="虚函数">虚函数</h2> <p>每个含有虚函数的类都有一个虚函数表,所以对应的实例都有一个指针,来指向这个共享的表</p> <h1 id="第六章-基本操作">第六章 基本操作</h1> <h2 id="拷贝和移动">拷贝和移动</h2> <p>我们可以通过定义拷贝构造函数和拷贝赋值操作符来控制拷贝过程,通过使用引用类型可以减少拷贝对象的开销</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="n">Vector</span><span class="o">::</span><span class="n">Vector</span><span class="p">(</span><span class="n">Vector</span><span class="o">&amp;&amp;</span> <span class="n">a</span><span class="p">)</span> <span class="o">:</span><span class="n">elem</span><span class="p">{</span><span class="n">a</span><span class="p">.</span><span class="n">elem</span><span class="p">},</span> <span class="n">sz</span><span class="p">{</span><span class="n">a</span><span class="p">.</span><span class="n">sz</span><span class="p">}</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">.</span><span class="n">elem</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">a</span><span class="p">.</span><span class="n">sz</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>Vector 的移动构造函数如上,符号<code>&amp;&amp;</code> 代表右值引用,可以给该引用绑定一个右值。</p> <p>左值的大致含义是能出现在赋值操作符左侧的内容,而右值正好与其相反,是无法为其赋值的值。</p> <p>右值引用就是引用了一个别人无法赋值的内容,所以可以安全地&quot;窃取&quot;它的值。</p> <h1 id="第七章-模版">第七章 模版</h1> <h2 id="参数化类型">参数化类型</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Vector</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">T</span> <span class="o">*</span><span class="n">elem</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">sz</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">explicit</span> <span class="n">Vector</span><span class="p">(</span><span class="kt">int</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="o">~</span><span class="n">Vector</span><span class="p">()</span> <span class="p">{</span> <span class="k">delete</span><span class="p">[]</span> <span class="n">elem</span><span class="p">;</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">T</span> <span class="o">&amp;</span><span class="k">operator</span><span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">T</span> <span class="o">&amp;</span><span class="k">operator</span><span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="nf">size</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">sz</span><span class="p">;</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="n">Vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">Vector</span><span class="p">(</span><span class="kt">int</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">length_error</span><span class="p">{</span><span class="s">&#34;Vector constructor: negative size&#34;</span><span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">elem</span> <span class="o">=</span> <span class="k">new</span> <span class="n">T</span><span class="p">[</span><span class="n">s</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">sz</span> <span class="o">=</span> <span class="n">s</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="n">T</span> <span class="o">&amp;</span><span class="n">Vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="k">operator</span><span class="p">[](</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="n">size</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">out_of_range</span><span class="p">{</span><span class="s">&#34;Vector::operator[]&#34;</span><span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">elem</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>使用 <code>template</code> 可以将 Vector 改成成支持任意类型的动态数组</p> <p>模版是一种编译时机制,在编译过程进行实例化时,每个实例都会生成一份代码</p> <p>可以对模版添加 concept 概念,用来对模版参数添加限制</p> <h2 id="参数化操作">参数化操作</h2> <h3 id="模版函数">模版函数</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">Sequence</span><span class="p">,</span> <span class="k">typename</span> <span class="n">Value</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="n">Value</span> <span class="n">sum</span><span class="p">(</span><span class="k">const</span> <span class="n">Sequence</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">,</span> <span class="n">Value</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">x</span><span class="p">:</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span> <span class="o">+=</span> <span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">v</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>模版函数可以是类的成员函数,但不能是虚函数,因为编译器不知道模版的所有实例,不能为模版函数生成 vtb1 虚函数表</p> <h3 id="函数对象">函数对象</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Less_than</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">T</span> <span class="n">val</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">Less_than</span><span class="p">(</span><span class="k">const</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">v</span><span class="p">)</span><span class="o">:</span> <span class="n">val</span><span class="p">{</span><span class="n">v</span><span class="p">}</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="nf">operator</span><span class="p">()(</span><span class="k">const</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">x</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span><span class="k">return</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">val</span><span class="p">;}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">fct</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Less_than</span> <span class="n">lti</span> <span class="p">{</span><span class="mi">42</span><span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">Less_than</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">lts</span> <span class="p">{</span><span class="s">&#34;Backus&#34;</span><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">b1</span> <span class="o">=</span> <span class="n">lti</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">b2</span> <span class="o">=</span> <span class="n">lts</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>函数对象用来定义对象,该对象可以像函数一样被调用。</p> <p><code>operator()</code> 称之为应用操作符</p> <h3 id="匿名函数">匿名函数</h3> <p><code>[&amp;](int a) {return a &lt; x}</code> 是一个匿名函数,</p> <p><code>[&amp;]</code>是匿名函数的捕获列表,它指定了函数体内的局部变量可以使用引用形式访问,如果使用值的方式则为<code>[=]</code>, 什么都不捕获则为<code>[]</code></p> <h2 id="模版机制">模版机制</h2> <h3 id="别名模版">别名模版</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">Key</span><span class="p">,</span> <span class="k">typename</span> <span class="n">Value</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Map</span> <span class="p">{};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">Value</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">String_map</span> <span class="o">=</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">Value</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">String_int_map</span> <span class="o">=</span> <span class="n">String_map</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">;</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第八章-概念和泛型编程">第八章 概念和泛型编程</h1> <h2 id="概念">概念</h2> <p>模版中,类型名称指示符 typename 是限定程度最低的,我们可以使用概念使之定义更加清晰明确</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">forward_iterator</span> <span class="n">Iter</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">advance</span><span class="p">(</span><span class="n">Iter</span> <span class="n">p</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="o">--</span><span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">p</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">random_access_iterator</span> <span class="n">Iter</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">advance</span><span class="p">(</span><span class="n">Iter</span> <span class="n">p</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">p</span> <span class="o">+=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">use_advance</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">vip</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">lsp</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">advance</span><span class="p">(</span><span class="n">vip</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">advance</span><span class="p">(</span><span class="n">lsp</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>如果有多个可选的模版,编译器会选择满足最严格参数需求的版本</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">B</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">concept</span> <span class="n">Boolean</span> <span class="o">=</span> <span class="k">requires</span><span class="p">(</span><span class="n">B</span> <span class="n">x</span><span class="p">,</span> <span class="n">B</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="nb">true</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="nb">false</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span> <span class="n">x</span> <span class="o">==</span> <span class="n">y</span><span class="p">)</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span> <span class="n">x</span> <span class="o">!=</span> <span class="n">y</span><span class="p">)</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="o">!</span><span class="n">x</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">)</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span> <span class="n">T2</span> <span class="o">=</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">concept</span> <span class="n">Equality_comparable</span> <span class="o">=</span> <span class="k">requires</span> <span class="p">(</span><span class="n">T</span> <span class="n">a</span><span class="p">,</span> <span class="n">T2</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span> <span class="p">}</span> <span class="o">-&gt;</span> <span class="n">Boolean</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">a</span> <span class="o">!=</span> <span class="n">b</span> <span class="p">}</span> <span class="o">-&gt;</span> <span class="n">Boolean</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">b</span> <span class="o">==</span> <span class="n">a</span> <span class="p">}</span> <span class="o">-&gt;</span> <span class="n">Boolean</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">b</span> <span class="o">!=</span> <span class="n">a</span> <span class="p">}</span> <span class="o">-&gt;</span> <span class="n">Boolean</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><p>如上示例,我们可以使用 <code>concept</code> 自己定义概念</p> <h2 id="泛型编程">泛型编程</h2> <h2 id="可变参数模版">可变参数模版</h2> <p>定义模版时,可以令其接受任意数量任意类型的实参,这样的模版称之为可变参数模版</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">concept</span> <span class="n">Printable</span> <span class="o">=</span> <span class="k">requires</span><span class="p">(</span><span class="n">T</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">t</span><span class="p">;};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="n">Printable</span> <span class="p">...</span><span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">print</span><span class="p">(</span><span class="n">T</span><span class="o">&amp;&amp;</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="p">...</span> <span class="o">&lt;&lt;</span> <span class="n">args</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="sc">&#39;\n&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> [笔记]数据结构和算法之美(更新中) https://ppd0705.github.io/post/algo_beauty/ Thu, 29 Jun 2023 07:21:46 +0800 https://ppd0705.github.io/post/algo_beauty/ <h1 id="第二讲">第二讲</h1> <h2 id="十个重点数据结构">十个重点数据结构</h2> <ul> <li>数组</li> <li>链表</li> <li>栈</li> <li>队列</li> <li>散列表</li> <li>二叉树</li> <li>堆</li> <li>跳表</li> <li>图</li> <li>Trie 树</li> </ul> <h2 id="十个重点算法">十个重点算法</h2> <ul> <li>递归</li> <li>排序</li> <li>二分查找</li> <li>搜索</li> <li>哈希</li> <li>贪心</li> <li>分治</li> <li>回溯</li> <li>动态规划</li> <li>字符串匹配</li> </ul> <h1 id="第三讲-复杂度分析上">第三讲 复杂度分析(上)</h1> <h2 id="大-o-复杂度表示法">大 O 复杂度表示法</h2> <p><img src="https://ppd0705.github.io/image/algo_beauty/3_1.webp" alt="O"></p> <ul> <li>n 表示数据规模的大小</li> <li>f(n) 表示每行代码执行的次数总和</li> <li>T(n) 表示代码执行的时间</li> <li>O 表示 T(n) 和 f(n) 成正比</li> </ul> <p>大 O 时间复杂度表示的是代码执行时间随着数据规模增长的变化趋势</p> <h2 id="时间复杂度分析">时间复杂度分析</h2> <h3 id="三个实用分析方法">三个实用分析方法</h3> <ol> <li>只关注循环执行次数最多的一段代码</li> <li>加法法则: 总复杂度等于量级最大的那段代码的复杂度</li> <li>乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积</li> </ol> <h3 id="常见的时间复杂度">常见的时间复杂度</h3> <p><img src="https://ppd0705.github.io/image/algo_beauty/3_2.webp" alt="common_examples"></p> <h2 id="空间复杂度分析">空间复杂度分析</h2> <p>空间复杂度指算法的存储空间随着数据规模增长的变化趋势</p> <h1 id="第四讲-复杂度分析下">第四讲 复杂度分析(下)</h1> <p>时间复杂度按情况可以分为四个</p> <ul> <li>最好情况时间复杂度 best case time complexity</li> <li>最坏情况时间复杂度 worst case time complexity</li> <li>平均情况时间复杂度 average case time complexity</li> <li>均摊时间复杂度 amortized time complexity</li> </ul> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// n表示数组array的长度 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">find</span><span class="p">(</span><span class="kt">int</span><span class="p">[]</span> <span class="n">array</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">pos</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">pos</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">pos</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>上述代码,最好情况是 x == array[0], 时间复杂度为 O(1) ; 最坏情况 x 不在 array 里, 时间复杂度为 O(n)。</p> <p>x 在数组中的位置,有 n+1中情况: 0~n-1 位置中 和 不在数组中。把每种情况需要遍历的元素个数相加再除以 n+1, 就可以得到 需要遍历的元素个数的平均值:</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/3_3.webp" alt="average_case"></p> <p>省略掉系数,常量,简化后得到复杂度为 O(n)</p> <p>上面的计算方式有个问题就是没有考虑 n+1种情况出现的概率情况,不出现在数组中的概率要大于处在某个位置的概率。 为了简化问题,假设出现和不出现在数组中的概率都为 1/2, 那么出现在数组中某个位置的概率为 1/2n.</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/3_4.webp" alt="average_case2"></p> <p>这个值是概率论中的加权平均值,也叫期望值,所以这里的平均时间复杂度叫加权平均复杂度</p> <p>省略掉系数,常量,简化后得到复杂度也为 O(n),</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"> <span class="c1">// array表示一个长度为n的数组 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 代码中的array.length就等于n </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span><span class="p">[]</span> <span class="n">array</span> <span class="o">=</span> <span class="n">new</span> <span class="kt">int</span><span class="p">[</span><span class="n">n</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert</span><span class="p">(</span><span class="kt">int</span> <span class="n">val</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">==</span> <span class="n">array</span><span class="p">.</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">array</span><span class="p">.</span><span class="n">length</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">=</span> <span class="n">sum</span> <span class="o">+</span> <span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">sum</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">array</span><span class="p">[</span><span class="n">count</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>上述代码实现了数组插入功能,如果数组没满,直接插入,如果满了,将数组求和放入第一个位置,然后将新的数据插入。</p> <p>最好情况时间复杂度为 O(1), 即数组没满的时候</p> <p>最坏情况时间复杂度为 O(n), 即数组满了的时候</p> <p>平均情况时间复杂度为 O(1), 计算过程如下</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/3_5.webp" alt="average_case3"></p> <p>针对示例,每次 O(n) 的插入操作,都会跟着 n-1 次 O(1) 的插入操作,那么把耗时多的一次操作均摊到接下来的 耗时少的 n-1 次操作,均摊下来,这一组连续插入操作的均摊时间复杂度为 O(1).</p> <h1 id="第五讲-数组">第五讲 数组</h1> <p>数组(Array) 是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据。</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/5_1.webp" alt="arr_1"></p> <p>假设上图数组 a 的地址为 base_address = 1000, 那么 元素 a[i] 的地址为 base_address + i * data_type_size</p> <h2 id="时间复杂度">时间复杂度</h2> <ul> <li>下标访问:O(1)</li> <li>插入:最好 O(1), 最坏 O(n), 平均 (1+2+&hellip;+n)/n = O(n)</li> <li>删除:最好 O(1), 最坏 O(n), 平均 O(n)</li> </ul> <h1 id="第六讲-链表">第六讲 链表</h1> <p>链表通过指针将一组零散的内存块串联在一起,常见的有单向链表、双向链表和循环链表</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/6_1.webp" alt="link_list"></p> <p>链表相比数组的优点在于插入和删除的复杂度是 O(1)</p> <h1 id="第八讲-栈">第八讲 栈</h1> <p>栈只允许在一端插入和删除数据,并且满足先进后出的特性,入栈和出栈的复杂度为 O(1)</p> <h1 id="第九讲-队列">第九讲 队列</h1> <p>队列和栈比较相似,只允许在一端插入,在另一端删除数据,并且满足先进先出的特性,入队和出队的复杂度为 O(1)</p> <h1 id="第十讲-递归">第十讲 递归</h1> <p>递归问题三个前置条件</p> <ul> <li>一个问题的解可以分为若干个子问题的解</li> <li>该问题和子问题的求解思路完全一样</li> <li>存在递归终止条件</li> </ul> <h1 id="第十一讲-排序上">第十一讲 排序(上)</h1> <h2 id="评价算法的主要维度">评价算法的主要维度</h2> <ul> <li>时间复杂度</li> <li>空间复杂度,是否为原地排序</li> <li>稳定性,相等元素排序前后的先后顺序是否不变</li> </ul> <p>常见三种 O(n2) 复杂度 排序算法详情如下</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/11_1.webp" alt="on2_sort"></p> <h2 id="冒泡排序">冒泡排序</h2> <p><img src="https://ppd0705.github.io/image/algo_beauty/11_2.webp" alt="bubble_sort"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">bubble_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span> </span></span><span class="line"><span class="cl"> <span class="n">swapped</span> <span class="o">=</span> <span class="kc">False</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">i</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">swapped</span> <span class="o">=</span> <span class="kc">True</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="ow">not</span> <span class="n">swapped</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span> </span></span></code></pre></td></tr></table> </div> </div><p>冒泡排序最好的情况,数据完全有序,复杂度为 O(n);</p> <p>冒泡排序最坏的情况,数据完全逆序,复杂度为 O(n2);</p> <p>有序元素对:指对于 i, j 两个位置的元素满足 a[i] &lt;= a[j] 且 i &lt; j 的情况。 零有序度:数据完全逆序,有序度为 0; 满有序度:数据完全有序,有序度为 n*(n-1)/2。</p> <p>数据的平均有序度为 (n*(n-1)/2)/2, 所以冒泡排序的平均复杂度为 O(n2)</p> <h2 id="插入排序">插入排序</h2> <p>将数组分为两个区间</p> <ul> <li>已排序区间: 初始值只有一个元素,即第一个元素</li> <li>未排序区间</li> </ul> <p>核心思想是依次取未排序区间的元素,在已排序区间找到合适的位置插入,保证已排序区间一直有序</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/11_3.webp" alt="insertion_sort"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">insertion_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)):</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">-=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span> </span></span></code></pre></td></tr></table> </div> </div><p>插入排序最好的情况,数据完全有序,每次只要比较一个数据就能确定数据插入的位置,复杂度为 O(n);</p> <p>插入排序最坏的情况,数据完全逆序,复杂度为 O(n2);</p> <p>数组平均插入复杂度未 O(n), 所以插入排序的平均复杂度为 O(n2)</p> <h2 id="选择排序">选择排序</h2> <p>选择排序也分为已排序区间和未排序区间,每次选择未排序区间最小元素放到已排序区间的末尾</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/11_4.webp" alt="selection_sort"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">selection_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">idx</span> <span class="o">=</span> <span class="n">i</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">nums</span><span class="p">[</span><span class="n">idx</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">idx</span> <span class="o">=</span> <span class="n">j</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">idx</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> </span></span></code></pre></td></tr></table> </div> </div><p>选择排序是不稳定的算法,如 [5, 8, 5, 2, 9]</p> <p>选择排序的最好/最坏/平均复杂度都为 O(n2)</p> <h1 id="第十二讲-排序下">第十二讲 排序(下)</h1> <p><img src="https://ppd0705.github.io/image/algo_beauty/12_1.webp" alt="dividend_sort"></p> <h2 id="归并排序">归并排序</h2> <p>归并排序利用分治的思想,并用递归的技巧来实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">merge</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">res</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">)</span> <span class="ow">and</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">right</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">left</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">[</span><span class="n">j</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">left</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">right</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">left</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">right</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">right</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">res</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">merge_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">nums</span> </span></span><span class="line"><span class="cl"> <span class="n">mid</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">merge</span><span class="p">(</span><span class="n">merge_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">[:</span><span class="n">mid</span><span class="p">]),</span> <span class="n">merge_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">mid</span><span class="p">:]))</span> </span></span></code></pre></td></tr></table> </div> </div><p>归并排序的空间复杂度未 O(n) (临时空间使用后就释放了)</p> <p>归并排序的最好/最坏/平均复杂度都为 O(nlogn)</p> <h2 id="快速排序">快速排序</h2> <p>快速排序是不稳定的排序算法</p> <p>归并排序平均复杂度都为 O(nlogn), 但极极端情况可能退化未 O(n2), 比如数组本身有序,又选择最后一个元素作为 pivot 的情况</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">partition</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">l</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">r</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">pivot</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">=</span> <span class="n">l</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">r</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">pivot</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">r</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">r</span><span class="p">],</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">i</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_quick_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">l</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">r</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">l</span> <span class="o">&gt;=</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> </span></span><span class="line"><span class="cl"> <span class="n">idx</span> <span class="o">=</span> <span class="n">partition</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">_quick_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">idx</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">_quick_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="n">idx</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">quick_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span> </span></span><span class="line"><span class="cl"> <span class="n">_quick_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十三讲-线性排序">第十三讲 线性排序</h1> <h2 id="桶排序bucket-sort">桶排序(Bucket Sort)</h2> <p><img src="https://ppd0705.github.io/image/algo_beauty/13_1.webp" alt="bucket_sort"></p> <p>假设有 n 个数据, 均匀地分布到 m 个桶内,每个桶内有 k = n/m 个元素,每个桶使用快排排序,复杂度为 O(klogk), 总复杂度为 O(nlog(n/m)),当 m 接近 n 时,log(n/m) 是个非常小的常量,所以复杂度接近 O(n)</p> <p>桶排序的前提条件是数据能均匀分布在每个桶中,并且桶与桶之间有大小顺序。极端情况如果数据分布在一个桶中,复杂度退化为 O(nlogn)</p> <p>桶排序适合外部排序</p> <h2 id="计数排序-counting-sort">计数排序 (Counting Sort)</h2> <p>计数排序是一种特殊的桶排序,相当于每个桶中的数据都是相同的值。</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/13_2.webp" alt="counting_sort"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">counting_sort</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span> </span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">c</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">nums</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">c</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="n">c</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">))]</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">idx</span> <span class="o">=</span> <span class="n">c</span><span class="p">[</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">c</span><span class="p">[</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="o">-=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)):</span> </span></span><span class="line"><span class="cl"> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">r</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </span></span></code></pre></td></tr></table> </div> </div><p>计数排序适用于数据范围不大的非负整数的场合</p> <h2 id="基数排序-radix-sort">基数排序 (Radix Sort)</h2> <p><img src="https://ppd0705.github.io/image/algo_beauty/13_3.webp" alt="radix_sort"></p> <p>基数排序要求数据可以分割出独立的位来比较,且位的范围不大</p> <h1 id="第十四讲-快速排序优化">第十四讲 快速排序优化</h1> <h2 id="优化分区点">优化分区点</h2> <p>如果数组原本就是有序的,且分区点都取最后一个,这样复杂度就会退化到 O(n2)。</p> <p>有两种简单的优化方式</p> <ol> <li>三数取中法:从区间前中后取三个数,比较大小选中间的那个</li> <li>随机法</li> </ol> <h2 id="glibc-qsort-的优化方式">glibc qsort 的优化方式</h2> <ol> <li>数据量不大的时候使用归并排序</li> <li>使用三数取中法</li> <li>快排区间小于4时使用插入排序,并使用哨兵机制来优化</li> <li>在堆上实现栈,避免过深的递归导致栈溢出</li> </ol> <h1 id="第十五讲-二分查找">第十五讲 二分查找</h1> <p>二分查找的依赖条件是:数据有序且可以随机访问</p> <ol> <li>对于不存在重复元素的解法:</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">v</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">l</span> <span class="o">&lt;=</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">l</span><span class="o">+</span><span class="n">r</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">==</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">m</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">m</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> </span></span></code></pre></td></tr></table> </div> </div><ol start="2"> <li>存在重复元素的第一个值:</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">v</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">l</span> <span class="o">&lt;=</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">l</span><span class="o">+</span><span class="n">r</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">==</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">m</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">m</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">m</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">m</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> </span></span></code></pre></td></tr></table> </div> </div><ol start="3"> <li>存在重复元素的最后一个值:</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">v</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">l</span> <span class="o">&lt;=</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">l</span><span class="o">+</span><span class="n">r</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">==</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">m</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> <span class="ow">or</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">m</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">m</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> </span></span></code></pre></td></tr></table> </div> </div><ol start="4"> <li>存在重复元素的大于等于的第一个值:</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">v</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">l</span> <span class="o">&lt;=</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">l</span><span class="o">+</span><span class="n">r</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">m</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">m</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">m</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> </span></span></code></pre></td></tr></table> </div> </div><ol start="5"> <li>存在重复元素的小于等于的最后一个值:</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">v</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="p">,</span> <span class="n">r</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">l</span> <span class="o">&lt;=</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">l</span><span class="o">+</span><span class="n">r</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">m</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span> <span class="ow">or</span> <span class="n">nums</span><span class="p">[</span><span class="n">m</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">m</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十七章-跳表">第十七章 跳表</h1> <p><img src="https://ppd0705.github.io/image/algo_beauty/17_1.webp" alt="skip_list"></p> <p>通过给链表添加索引,这样就可以实现快速增删查</p> <p>通过随机函数确定插入在哪一层</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="c1">// https://github.com/MauriceGit/skiplist </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">SkipList</span><span class="p">)</span> <span class="nf">generateLevel</span><span class="p">(</span><span class="nx">maxLevel</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">level</span> <span class="o">:=</span> <span class="nx">maxLevel</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="c1">// First we apply some mask which makes sure that we don&#39;t get a level </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// above our desired level. Then we find the first set bit. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">var</span> <span class="nx">x</span> <span class="kt">uint64</span> <span class="p">=</span> <span class="nx">rand</span><span class="p">.</span><span class="nf">Uint64</span><span class="p">()</span> <span class="o">&amp;</span> <span class="p">((</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="nb">uint</span><span class="p">(</span><span class="nx">maxLevel</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">zeroes</span> <span class="o">:=</span> <span class="nx">bits</span><span class="p">.</span><span class="nf">TrailingZeros64</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">zeroes</span> <span class="o">&lt;=</span> <span class="nx">maxLevel</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">level</span> <span class="p">=</span> <span class="nx">zeroes</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">level</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>redis 作者回答 zset 选用跳表的理由:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">// https://news.ycombinator.com/item?id=1171423 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">antirez on March 6, 2010 | root | parent | next [–] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">There are a few reasons: </span></span><span class="line"><span class="cl">1) They are not very memory intensive. It&#39;s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">2) A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">3) They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code. </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十八章-散列表上">第十八章 散列表(上)</h1> <p>散列表是基于数组的随机访问特性,利用散列函数,将数据映射到数组的某个位置</p> <h2 id="散列冲突">散列冲突</h2> <p>常用的解放方案有 开放寻址法(open addressing)和链表法 (chaining)</p> <h3 id="开放寻址法">开放寻址法</h3> <p>最简单的一种开放寻址法为线性探测:如果位置被占用,会继续寻找。</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/18_1.webp" alt="linear_probing"></p> <p>二次探测 quadratic probing: 步长变为步数的平方,如 hash(x) + 0、 hash(x) + 1^2、hash(x) + 2^2 双重散列 double hashing: 使用多个散列函数,第一个发现占用后,使用第二个</p> <h3 id="链表法">链表法</h3> <p>链表法将散列值相同的数据放在同一个链表中</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/18_2.webp" alt="chaining"></p> <p>查找和删除的复杂度和链表长度 k 成正比,即 O(k)</p> <h1 id="第十九章-散列表中">第十九章 散列表(中)</h1> <h2 id="哈希函数设计">哈希函数设计</h2> <ul> <li>不过太耗时</li> <li>尽可能随机且随机分布</li> </ul> <h2 id="扩容">扩容</h2> <p>扩容时,只申请空间,不搬迁数据。</p> <ul> <li>当插入数据时,将数据插入新的表,并搬迁一个旧表的值到新表</li> <li>查询数据时,先查新表,没查到再查旧表</li> </ul> <p><img src="https://ppd0705.github.io/image/algo_beauty/19_1.webp" alt="increase_capacity"></p> <h2 id="java-hashmap-实现优化">Java HashMap 实现优化</h2> <p>采用链表法解决冲突,链表长度超过8时改用红黑树</p> <h1 id="第二十一讲-哈希算法上">第二十一讲 哈希算法(上)</h1> <h2 id="定义">定义</h2> <p>将任意长度的二进制值串映射为固定长度的二进制值串的规则称为哈希算法。</p> <p>优秀哈希算法要求</p> <ul> <li>单向,不可从哈希值推导出原始值</li> <li>对数据敏感,差了一个 bit 的两个数据的哈希值大不相同</li> <li>冲突概率非常小</li> <li>执行效率高效</li> </ul> <h2 id="应用场景">应用场景</h2> <ul> <li>安全加密,md5 哈希值有 2^128 种可能</li> <li>唯一标识</li> <li>数据校验</li> <li>散列函数</li> </ul> <h1 id="第二十二讲-哈希算法在分布式服务中的应用">第二十二讲 哈希算法在分布式服务中的应用</h1> <h2 id="负载均衡">负载均衡</h2> <p>对客户端会话ID 或者 IP 地址计算哈希值,然后对服务器列表大小进行取余,得到列表索引位置。</p> <h2 id="数据分片">数据分片</h2> <h2 id="分布式存储">分布式存储</h2> <p>一致性哈希算法示例: <a href="https://geektutu.com/post/geecache-day4.html">GeeCache一致性哈希</a></p> <h1 id="第二十三章-二叉树">第二十三章 二叉树</h1> <h2 id="树的相关概念">树的相关概念</h2> <p>根节点: 没有父节点的节点 叶子节点: 没有子节点的节点</p> <p>高度、深度 和层的示意如下图</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/23_1.webp" alt="height_depth_level"></p> <h2 id="二叉树">二叉树</h2> <p>完全二叉树:叶子节点都在最后两层,最后一层的叶子节点都靠左排列,除了最后一层,其他层节点的个数要达到最大</p> <p>满二叉树:叶子节点都在最后一层,除了叶子节点之外,每个节点都有两个子节点</p> <h3 id="链式存储方式">链式存储方式</h3> <p>使用链表来存储,每个节点有三个字段,一个字段存储数据,另外两个字段分别指向左右子节点</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/23_2.webp" alt="link_tree"></p> <h3 id="顺序存储方式">顺序存储方式</h3> <p>根节点存储下标为 1 的位置,左节点存在下标为 2<em>i 的位置,右节点存储在 2</em>i+1 的位置</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/23_3.webp" alt="arr_tree"></p> <h3 id="遍历">遍历</h3> <p>二叉树有三种经典的遍历方式:前序遍历、中序遍历、后续遍历</p> <p><img src="https://ppd0705.github.io/image/algo_beauty/23_4.webp" alt="tree_traversal"></p> <h1 id="第二十四章-二叉查找树">第二十四章 二叉查找树</h1> <p>二叉查找树:对于任意一个节点, 其左子树的每个节点均小于它,右子树的每个节点都大于它</p> <h2 id="查找操作">查找操作</h2> <p>从根节点出发,如果等于要找的数据则返回,如果小于则从左子树找,否则从右子树找。</p> <h2 id="插入操作">插入操作</h2> <p>从根节点出发,如果大于根节点,当右节点为空,则直接插到右节点的位置,否则遍历右子树; 如果小于根节点,当左节点为空,则直接插到左节点的位置,否则遍历左子树。</p> <h2 id="删除操作">删除操作</h2> <ol> <li>当目标节点没有子节点,将其父节点指向的指针置为空</li> <li>当目标节点有一个子节点,将其父节点指向的指针指向目标节点的子节点</li> <li>当目标节点有两个子节点,用目标节点右子树的最小节点来替换目标节点</li> </ol> <h1 id="第二十五章-平衡二叉查找树">第二十五章 平衡二叉查找树</h1> <p>极端情况下二叉查找树会退化成链表,所以需要设计平衡二叉树来避免性能退化的问题。</p> <p>平衡二叉树的严格定义:任何一个节点的左右子树的高度差值不能大于一。</p> <p>AVL 树完全满足平衡二叉树的严格定义,查找效率高,但为了维持这种高度的平衡,每次插入和删除都要调整,比较复杂和耗时。</p> <p>红黑树只做到了近似平衡,在维护平衡的成本上,比 AVL 树的成本低。所以查找、插入和删除操作的性能都比较稳定</p> <p>红黑树中有两种颜色的节点:黑色、红色,节点具有有如如下要求:</p> <ul> <li>根节点是黑色的</li> <li>每个叶子节点都是黑色的空节点</li> <li>相邻节点不能同时为红色,即红色节点被黑色隔开</li> <li>每个节点,其到达叶子节点的所有路径中黑色节点的数量是一样的</li> </ul> <h1 id="链接">链接</h1> <ul> <li><a href="https://time.geekbang.org/column/intro/100017301?tab=intro">数据结构与算法之美-极客时间</a></li> <li><a href="https://geektutu.com/post/geecache-day4.html">GeeCache一致性哈希</a></li> </ul> NATS 学习笔记 https://ppd0705.github.io/post/nats_note/ Thu, 01 Jun 2023 08:31:17 +0800 https://ppd0705.github.io/post/nats_note/ <h1 id="quality-of-service-qos">Quality of Service (QoS)</h1> <h2 id="at-most-once">At most once</h2> <p>Core NATS is a fire-and-forget messaging system. It will only hold messages in memory.</p> <p>If a subscriber is not listening on the subject, or is not active when the message is sent, the message is not received.</p> <h2 id="at-least-exactly-once">At least/ exactly once</h2> <p>JetStream support higher qualities of service (at least once and exactly once)</p> <h1 id="subject-based-messaging">Subject-based Messaging</h1> <p>消息基于 Subject 匹配和传递</p> <p>特殊符号</p> <ul> <li><code>.</code>作为 token 分隔符</li> <li><code>*</code>正则匹配 一个 token</li> <li><code>&gt;</code> 正则匹配一个或者多个 token,且只能位于末尾</li> </ul> <p><img src="https://ppd0705.github.io/image/nats_note/1.svg" alt="subject_matching"></p> <h1 id="core-nats">Core NATS</h1> <p>基于<code>at most once</code> 的服务质量,Core NATS 有三种消息传递模式</p> <ul> <li>publish-subscribe</li> <li>request-reply</li> <li>queuing</li> </ul> <h2 id="publish-subscribe">Publish-Subscribe</h2> <p>Publish-Subscribe 是一对多的通讯模式,一个发送者在一个 Subject 上发送的消息,任何活跃的订阅这个 Subject 的接收者都会收到消息</p> <p><img src="https://ppd0705.github.io/image/nats_note/2.svg" alt="publish_subscribe"></p> <h2 id="request-reply">Request-Reply</h2> <p>Request-Reply 建立在 <code>Publish-Subscribe</code> 基础之上,接受者在收到消息后,会像 Reply subject 发送响应消息</p> <p><img src="https://ppd0705.github.io/image/nats_note/3.svg" alt="request_reply"></p> <h2 id="queue-groups">Queue Groups</h2> <p>Queue Groups 建立在 <code>Request-Reply</code> 基础之上,当有多个接收者时,这些接收者就形成了一个 <code>Queue Group</code>, 每次消息会随机有一个接受者来消费消息</p> <p><img src="https://ppd0705.github.io/image/nats_note/4.svg" alt="queue_group"></p> <h1 id="jetstream">JetStream</h1> <p>JetStream 是建立在 Core NATS 上的分布式持久系统,需要 1、3 或 5 个 server (分别容忍 0、1 和 2 个 server 挂掉)</p> <h2 id="stream">Stream</h2> <h3 id="存储类型">存储类型</h3> <ul> <li>File</li> <li>Memory</li> </ul> <h3 id="保留策略">保留策略</h3> <ul> <li>LimitsPolicy: MaxMsgs, MaxBytes, MaxAge, and MaxMsgsPerSubject</li> <li>InterestPolicy: 当没有消费者消费或者所有消费者消费了,消息会删除</li> <li>WorkQueuePolicy: 每个消息只能被消费一次,一个 WorkQueue 只能有一个消费者</li> </ul> <h3 id="丢弃策略">丢弃策略</h3> <ul> <li>DiscardOld</li> <li>DiscardNew</li> </ul> <h2 id="consumer">Consumer</h2> <h3 id="ack-策略">Ack 策略</h3> <ul> <li>AckExplicit: 每个消息都 Ack</li> <li>AckNone: 无 Ack</li> <li>AckAll: Ack 最后收到的一个消息</li> </ul> <h3 id="deliver-策略">Deliver 策略</h3> <ul> <li>DeliverAll: 接收所有消息</li> <li>DeliverLast: 接收最新的消息</li> <li>DeliverNew: 接收 Consumer 创建之后的消息</li> <li>DeliverByStartSequence: 接收指定 OptStartSeq 之后的消息</li> <li>DeliverByStartTime: 接收指定 OptStartTime 之后的消息</li> </ul> <h3 id="maxackpending">MaxAckPending</h3> <p>最大 Pending 数量,超过则不再传递消息</p> <h2 id="kv-存储">KV 存储</h2> <p>JetStream 可以提供 KV 功能, 具体包括增删改查,甚至可以监听 key的 改动、 获取 value 的历史版本</p> <h2 id="协议">协议</h2> <p>NATS 使用文本格式的协议,基于 TCP 进行通信。以换行符做消息分隔符, 以空格或制表符作为字段分隔符。</p> <p>可以直接使用 telnet 或者 netcat 作为客户端直接和服务段交互</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">nc -c localhost <span class="m">4222</span> </span></span></code></pre></td></tr></table> </div> </div><p>具体操作符类型如下:</p> <table> <thead> <tr> <th>类型</th> <th>发送方</th> <th>语法</th> <th>备注</th> </tr> </thead> <tbody> <tr> <td>INFO</td> <td>Server</td> <td><code>INFO {&quot;option_name&quot;:option_value,...}␍␊</code></td> <td>连接建立之后发送</td> </tr> <tr> <td>CONNECT</td> <td>Client</td> <td><code>CONNECT {&quot;option_name&quot;:option_value,...}␍␊</code></td> <td>指定连接信息</td> </tr> <tr> <td>PUB</td> <td>Client</td> <td><code>PUB &lt;subject&gt; [reply-to] &lt;#bytes&gt;␍␊[payload]␍␊</code></td> <td>发布消息到服务段</td> </tr> <tr> <td>SUB</td> <td>Client</td> <td><code>SUB &lt;subject&gt; [queue group] &lt;sid&gt;␍␊</code></td> <td>订阅消息</td> </tr> <tr> <td>UNSUB</td> <td>Client</td> <td><code>UNSUB &lt;sid&gt; [max_msgs]␍␊</code></td> <td>取消订阅消息</td> </tr> <tr> <td>MSG</td> <td>Server</td> <td><code>MSG &lt;subject&gt; &lt;sid&gt; [reply-to] &lt;#bytes&gt;␍␊[payload]␍␊</code></td> <td>转发消息给客户端</td> </tr> <tr> <td>PING</td> <td>Both</td> <td><code>PING␍␊</code></td> <td>心跳请求</td> </tr> <tr> <td>PONG</td> <td>Both</td> <td><code>PONG␍␊</code></td> <td>心跳相应</td> </tr> <tr> <td>+OK</td> <td>Server</td> <td><code>+OK␍␊</code></td> <td>确认消息,verbose 模式才有</td> </tr> <tr> <td>-ERR</td> <td>Server</td> <td><code>-ERR &lt;error message&gt;␍␊</code></td> <td>错误</td> </tr> </tbody> </table> <h1 id="源码阅读">源码阅读</h1> <h2 id="数据解析">数据解析</h2> <p>基于一个有限状态机,接受到的数据会一个字符一个字节去录入和转移状态</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">i</span> <span class="p">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="nx">buf</span><span class="p">);</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="p">=</span> <span class="nx">buf</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">OP_START</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">OP_P</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">b</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;U&#39;</span><span class="p">,</span> <span class="sc">&#39;u&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span> <span class="p">=</span> <span class="nx">OP_PU</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;I&#39;</span><span class="p">,</span> <span class="sc">&#39;i&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span> <span class="p">=</span> <span class="nx">OP_PI</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;O&#39;</span><span class="p">,</span> <span class="sc">&#39;o&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span> <span class="p">=</span> <span class="nx">OP_PO</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">goto</span> <span class="nx">parseErr</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">OP_PU</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">b</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39;B&#39;</span><span class="p">,</span> <span class="sc">&#39;b&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span> <span class="p">=</span> <span class="nx">OP_PUB</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">goto</span> <span class="nx">parseErr</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">OP_PUB</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">b</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="sc">&#39; &#39;</span><span class="p">,</span> <span class="sc">&#39;\t&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">state</span> <span class="p">=</span> <span class="nx">OP_PUB_SPC</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">goto</span> <span class="nx">parseErr</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">goto</span> <span class="nx">parseErr</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="订阅列表">订阅列表</h2> <p>订阅列表使用前缀树来维护</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">node</span> <span class="kd">struct</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">next</span> <span class="o">*</span><span class="nx">level</span> </span></span><span class="line"><span class="cl"> <span class="nx">psubs</span> <span class="p">[]</span><span class="o">*</span><span class="nx">subscription</span> </span></span><span class="line"><span class="cl"> <span class="nx">qsubs</span> <span class="p">[][]</span><span class="o">*</span><span class="nx">subscription</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">level</span> <span class="kd">struct</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">nodes</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="o">*</span><span class="nx">node</span> </span></span><span class="line"><span class="cl"> <span class="nx">pwc</span><span class="p">,</span> <span class="nx">fwc</span> <span class="o">*</span><span class="nx">node</span> <span class="c1">// pwd、fwc 分别表示 &#39;*&#39; 和 &#39;&gt;&#39; 订阅 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>插入操作</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="nx">l</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">root</span> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">n</span> <span class="o">*</span><span class="nx">node</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">t</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tokens</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">sfwc</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ErrInvalidSubject</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">t</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">pwc</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">pwc</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">fwc</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">fwc</span> </span></span><span class="line"><span class="cl"> <span class="nx">sfwc</span> <span class="p">=</span> <span class="kc">true</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">nodes</span><span class="p">[</span><span class="nx">t</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nf">newNode</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">t</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">pwc</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span><span class="p">.</span><span class="nx">pwc</span> <span class="p">=</span> <span class="nx">n</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">fwc</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span><span class="p">.</span><span class="nx">fwc</span> <span class="p">=</span> <span class="nx">n</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span><span class="p">.</span><span class="nx">nodes</span><span class="p">[</span><span class="nx">t</span><span class="p">]</span> <span class="p">=</span> <span class="nx">n</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span><span class="p">.</span><span class="nx">next</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="p">.</span><span class="nx">next</span> <span class="p">=</span> <span class="nf">newLevel</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="p">=</span> <span class="nx">n</span><span class="p">.</span><span class="nx">next</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">sub</span><span class="p">.</span><span class="nx">queue</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="p">.</span><span class="nx">psubs</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">n</span><span class="p">.</span><span class="nx">psubs</span><span class="p">,</span> <span class="nx">sub</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// This is a queue subscription </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">i</span> <span class="o">:=</span> <span class="nf">findQSliceForSub</span><span class="p">(</span><span class="nx">sub</span><span class="p">,</span> <span class="nx">n</span><span class="p">.</span><span class="nx">qsubs</span><span class="p">);</span> <span class="nx">i</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="p">.</span><span class="nx">qsubs</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">n</span><span class="p">.</span><span class="nx">qsubs</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">sub</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="p">.</span><span class="nx">qsubs</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">n</span><span class="p">.</span><span class="nx">qsubs</span><span class="p">,</span> <span class="p">[]</span><span class="o">*</span><span class="nx">subscription</span><span class="p">{</span><span class="nx">sub</span><span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>删除操作</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="nx">sfwc</span> <span class="o">:=</span> <span class="kc">false</span> </span></span><span class="line"><span class="cl"><span class="nx">l</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nx">root</span> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">n</span> <span class="o">*</span><span class="nx">node</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// Track levels for pruning </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">lnts</span> <span class="p">[</span><span class="mi">32</span><span class="p">]</span><span class="nx">lnt</span> </span></span><span class="line"><span class="cl"><span class="nx">levels</span> <span class="o">:=</span> <span class="nx">lnts</span><span class="p">[:</span><span class="mi">0</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">t</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tokens</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">t</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">sfwc</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ErrInvalidSubject</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">l</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ErrNotFound</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">t</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">pwc</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">pwc</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">fwc</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">fwc</span> </span></span><span class="line"><span class="cl"> <span class="nx">sfwc</span> <span class="p">=</span> <span class="kc">true</span> </span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">nodes</span><span class="p">[</span><span class="nx">t</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">levels</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">levels</span><span class="p">,</span> <span class="nx">lnt</span><span class="p">{</span><span class="nx">l</span><span class="p">,</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">t</span><span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="p">=</span> <span class="nx">n</span><span class="p">.</span><span class="nx">next</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="p">=</span> <span class="kc">nil</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// 反向删除记录 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">levels</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o">--</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span><span class="p">,</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">levels</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">l</span><span class="p">,</span> <span class="nx">levels</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">n</span><span class="p">,</span> <span class="nx">levels</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">t</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span><span class="p">.</span><span class="nf">isEmpty</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span><span class="p">.</span><span class="nf">pruneNode</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">t</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>查找操作</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="c1">// matchLevel is used to recursively descend into the trie. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">matchLevel</span><span class="p">(</span><span class="nx">l</span> <span class="o">*</span><span class="nx">level</span><span class="p">,</span> <span class="nx">toks</span> <span class="p">[]</span><span class="kt">string</span><span class="p">,</span> <span class="nx">results</span> <span class="o">*</span><span class="nx">SublistResult</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">pwc</span><span class="p">,</span> <span class="nx">n</span> <span class="o">*</span><span class="nx">node</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">t</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">toks</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">l</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">l</span><span class="p">.</span><span class="nx">fwc</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">addNodeToResults</span><span class="p">(</span><span class="nx">l</span><span class="p">.</span><span class="nx">fwc</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">pwc</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">pwc</span><span class="p">;</span> <span class="nx">pwc</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">matchLevel</span><span class="p">(</span><span class="nx">pwc</span><span class="p">.</span><span class="nx">next</span><span class="p">,</span> <span class="nx">toks</span><span class="p">[</span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">:],</span> <span class="nx">results</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="p">=</span> <span class="nx">l</span><span class="p">.</span><span class="nx">nodes</span><span class="p">[</span><span class="nx">t</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="p">=</span> <span class="nx">n</span><span class="p">.</span><span class="nx">next</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">l</span> <span class="p">=</span> <span class="kc">nil</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">n</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">addNodeToResults</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">pwc</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">addNodeToResults</span><span class="p">(</span><span class="nx">pwc</span><span class="p">,</span> <span class="nx">results</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="集群">集群</h2> <p>集群节点两两之间会建立一个类型为 ROUTER 的 Client。</p> <p>建立连接后,会通过 <code>SUB</code> 类型消息告知当前节点订阅列表</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">sendLocalSubsToRoute</span><span class="p">(</span><span class="nx">route</span> <span class="o">*</span><span class="nx">client</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">client</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s</span><span class="p">.</span><span class="nx">clients</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">client</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="nx">subs</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="o">*</span><span class="nx">subscription</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">client</span><span class="p">.</span><span class="nx">subs</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">sub</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">client</span><span class="p">.</span><span class="nx">subs</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">subs</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">subs</span><span class="p">,</span> <span class="nx">sub</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">client</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">sub</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">subs</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">rsid</span> <span class="o">:=</span> <span class="nf">routeSid</span><span class="p">(</span><span class="nx">sub</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">proto</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="nx">subProto</span><span class="p">,</span> <span class="nx">sub</span><span class="p">.</span><span class="nx">subject</span><span class="p">,</span> <span class="nx">sub</span><span class="p">.</span><span class="nx">queue</span><span class="p">,</span> <span class="nx">rsid</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">proto</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">route</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">route</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="nx">route</span><span class="p">.</span><span class="nf">sendProto</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">(),</span> <span class="kc">true</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>当有新的节点加入后,会转发新节点的信息</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">forwardNewRouteInfoToKnownServers</span><span class="p">(</span><span class="nx">info</span> <span class="o">*</span><span class="nx">Info</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">info</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">infoJSON</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="nx">InfoProto</span><span class="p">,</span> <span class="nx">b</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">r</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s</span><span class="p">.</span><span class="nx">routes</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">r</span><span class="p">.</span><span class="nx">route</span><span class="p">.</span><span class="nx">remoteID</span> <span class="o">!=</span> <span class="nx">info</span><span class="p">.</span><span class="nx">ID</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nf">sendInfo</span><span class="p">(</span><span class="nx">infoJSON</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>当普通 Client 有新的订阅时,Route Client 会转发消息到所有其他节点</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="c1">// sid 会被重新, 添加 RSID 前缀和客户端信息等 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">routeSid</span><span class="p">(</span><span class="nx">sub</span> <span class="o">*</span><span class="nx">subscription</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">qi</span> <span class="kt">string</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">sub</span><span class="p">.</span><span class="nx">queue</span><span class="p">)</span> <span class="p">&gt;</span> <span class="mi">0</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">qi</span> <span class="p">=</span> <span class="s">&#34;Q&#34;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s%s:%d:%s&#34;</span><span class="p">,</span> <span class="nx">qi</span><span class="p">,</span> <span class="nx">RSID</span><span class="p">,</span> <span class="nx">sub</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nx">cid</span><span class="p">,</span> <span class="nx">sub</span><span class="p">.</span><span class="nx">sid</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">broadcastInterestToRoutes</span><span class="p">(</span><span class="nx">proto</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">arg</span> <span class="p">[]</span><span class="kt">byte</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">atomic</span><span class="p">.</span><span class="nf">LoadInt32</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">s</span><span class="p">.</span><span class="nx">logging</span><span class="p">.</span><span class="nx">trace</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">arg</span> <span class="p">=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">proto</span><span class="p">[:</span><span class="nb">len</span><span class="p">(</span><span class="nx">proto</span><span class="p">)</span><span class="o">-</span><span class="nx">LEN_CR_LF</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">protoAsBytes</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">proto</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">route</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">s</span><span class="p">.</span><span class="nx">routes</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// FIXME(dlc) - Make same logic as deliverMsg </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">route</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="nx">route</span><span class="p">.</span><span class="nf">sendProto</span><span class="p">(</span><span class="nx">protoAsBytes</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">route</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="nx">route</span><span class="p">.</span><span class="nf">traceOutOp</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">,</span> <span class="nx">arg</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>发布消息时,如果是 Route 类型的客户端,会对remoteID 做去重,以免给同一个 Route 客户端发送多次。 并且消息只会从 普通客户端发送到 Route 客户端,不会出现 Route 客户端 再发到 Route 客户端的情况</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Golang" data-lang="Golang"><span class="line"><span class="cl"><span class="c1">// processMsg is called to process an inbound msg from a client. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">client</span><span class="p">)</span> <span class="nf">processMsg</span><span class="p">(</span><span class="nx">msg</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">SublistResult</span> </span></span><span class="line"><span class="cl"> <span class="nx">isRoute</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">typ</span> <span class="o">==</span> <span class="nx">ROUTER</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">sub</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">r</span><span class="p">.</span><span class="nx">psubs</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Check if this is a send to a ROUTER, make sure we only send it </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// once. The other side will handle the appropriate re-processing </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// and fan-out. Also enforce 1-Hop semantics, so no routing to another. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">sub</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nx">typ</span> <span class="o">==</span> <span class="nx">ROUTER</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 只会转发一次 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">isRoute</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">continue</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// route 去重 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">rmap</span><span class="p">[</span><span class="nx">sub</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nx">route</span><span class="p">.</span><span class="nx">remoteID</span><span class="p">];</span> <span class="nx">ok</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Debugf</span><span class="p">(</span><span class="s">&#34;Ignoring route, already processed&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">sub</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">continue</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">rmap</span><span class="p">[</span><span class="nx">sub</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nx">route</span><span class="p">.</span><span class="nx">remoteID</span><span class="p">]</span> <span class="p">=</span> <span class="nx">routeSeen</span> </span></span><span class="line"><span class="cl"> <span class="nx">sub</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Normal delivery </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">mh</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">msgHeader</span><span class="p">(</span><span class="nx">msgh</span><span class="p">[:</span><span class="nx">si</span><span class="p">],</span> <span class="nx">sub</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">deliverMsg</span><span class="p">(</span><span class="nx">sub</span><span class="p">,</span> <span class="nx">mh</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="reference">Reference</h1> <ul> <li><a href="https://docs.nats.io/">NATS Document</a></li> <li><a href="https://github.com/cz-theng/nats-source">NATS 1.0 源码学习笔记</a></li> </ul> 迁移Python项目到Golang https://ppd0705.github.io/post/migrate_python_project_to_go/ Fri, 12 May 2023 07:40:27 +0800 https://ppd0705.github.io/post/migrate_python_project_to_go/ <h1 id="前言">前言</h1> <p>最近在用 Golang 重构一个 Python web 项目,之前后端主要的技术栈是 flask+sqlalchemy+celery,综合现有情况, 我选择了使用 gin+gorm+cron 来做相应替换。但重构过程中发现某些功能在 Golang 生态中并不是那么完善,需要自己进行二次开发, 主要是在 ORM 和定时任务这两块。</p> <h1 id="orm">ORM</h1> <h2 id="model-生成">Model 生成</h2> <p>Model 生成有比较完善的工具 <a href="https://gorm.io/gen/">gen</a>,通过读数据库schema信息直接生成 Struct。一些常用的配置:</p> <ul> <li><code>WithDataTypeMap</code> 用来做类型映射,如<code>bigint</code> 映射成 <code>int</code></li> <li><code>WithJSONTagNameStrategy</code> 用来设置 json tag 生成规则,如 password 字段的 tag 设置成<code>-</code></li> <li><code>WithImportPkgPath</code> 用来添加第三包引入,如使用了 <code>decimal</code> 类型的话需要引入<a href="https://github.com/shopspring/decimal">decimal</a></li> <li><code>gen.WithMethod</code> option 来添加自定义方法</li> </ul> <h2 id="migration">Migration</h2> <p>Golang 中比较广泛使用的三个方案有 <a href="https://github.com/golang-migrate/migrate">golang-migrate</a>、<a href="https://github.com/pressly/goose">goose</a> 和 <a href="https://github.com/ariga/atlas">atlas</a>,但大多需要自己手写 SQL, 远没有 Python 中的 <a href="https://alembic.sqlalchemy.org/en/latest/">alembic</a> 简单好用。后面查阅了一番文档,atlas 还是可以通过 gorm Model 来生成 migration SQL,只是步骤复杂了点。</p> <h3 id="生成-migration-sql">生成 migration SQL</h3> <ol> <li>使用 gorm db.AutoMigrate 方法将所有 Model生成到一个临时的数据库中</li> <li>使用 <code>atlas migrate diff</code> 对比 migrations 文件夹和临时数据库表结构的差别从而生成 migration SQL</li> </ol> <h3 id="升级">升级</h3> <p>使用 <code>atlas migrate apply 1</code> 命令升级一个版本</p> <h3 id="降级">降级</h3> <ol> <li>找到上一个版本号<code>prevVersion</code></li> <li>使用<code>atlas schema apply</code> 命令, 其中 <code>--to</code> 参数中指定 version=<code>${prevVersion}</code></li> <li>使用<code>atlas migrate set ${prevVersion}</code>来设置当前版本</li> </ol> <h1 id="定时任务">定时任务</h1> <p><a href="https://github.com/robfig/cron">cron</a> 代码简洁明了, <code>cron.Recover</code>和<code>cron.SkipIfStillRunning</code> 插件功能也很实用。</p> <h2 id="rest-接口">REST 接口</h2> <p>基于使用场景我做了一下封装,添加了一些 REST 接口:</p> <ol> <li>调用 <code>cron.Remove</code>和 <code>cron.Add</code> 来实现动态停止和启动 Job,</li> <li>获取已注册 Job 列表</li> <li>单次运行 Job</li> </ol> <h2 id="job-扩展">Job 扩展</h2> <p>默认的 Job只有 EntryID 信息,所以做了如下扩展。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Job</span> <span class="kd">interface</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">Run</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="nf">ID</span><span class="p">()</span> <span class="kt">int</span> </span></span><span class="line"><span class="cl"> <span class="nf">Name</span><span class="p">()</span> <span class="kt">string</span> </span></span><span class="line"><span class="cl"> <span class="nf">Schedule</span><span class="p">()</span> <span class="kt">string</span> </span></span><span class="line"><span class="cl"> <span class="nf">Remark</span><span class="p">()</span> <span class="kt">string</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="相关链接">相关链接</h1> <ul> <li><a href="https://gorm.io/gen/database_to_structs.html">gen: database_to_structs</a></li> <li><a href="https://atlasgo.io/guides/orms/gorm">atlas: automatic migration planning for gorm</a></li> </ul> 2022年终总结 https://ppd0705.github.io/post/2022_summary/ Sat, 31 Dec 2022 09:39:13 +0800 https://ppd0705.github.io/post/2022_summary/ <h1 id="工作">工作</h1> <p>今年工作的关键词是重构。编程语言由 python 转 go,引入了消息队列 <del>kafka</del> nats,一个人吭哧吭哧写了半年,将核心组件和业务重新设计了一遍,并顺利落地。 我一方面很享受决策自由,以及看到重构完成后的简洁优雅带来的满满成就感;另一方面感到孤独,不过好在有 github、优质博客等供我学习和激发灵感。</p> <p><img src="https://ppd0705.github.io/image/2022_summary/github_contributors.png" alt="github_contributors"></p> <p>今年六月底公司开始执远程工作制度,经过半年的体验,我确定我目前的岗位和我个人都挺适合远程办公的。 之于岗位,首先我们组非常小巧而独立,几乎不依赖公司其他组同事,组内大概就是每天开5分钟晨会;其次某种层面来说我们组的主要工作成果可以通过数字直接考量,简单来说就是贡献了多少成交量和盈亏了多少钱。 之于个人,我有足够的能力和意愿去解决当前工作中的大部分问题;我是一个内向型人格,我享受独处;我追求工作上的成就感,如果工作让我感受不到自己的价值和成长,我会主动离开。</p> <p>今年组内入职了两个新同事。一个同事试用期后离开了,作为面试参与者反思了一下,面试时技术能力和行事风格的考量不应该被一些简历标签所影响。 另外一个同事则相处融洽,且让我们提升了不少战斗力。</p> <p>今年行业环境极其寒冷,各种重大事故不胜其数,谁会相信市值第五大代币崩盘?谁会相信第二大交易所会破产?或许明年的寒意还会更胜?</p> <h1 id="生活">生活</h1> <p>今年上半年基本是做核酸扫场所码度过的。某一天出门时还好好的,下班时却发现住的地方被封控只进不出,我想了大概3秒,对我来说最好的选择还是得选择&quot;进&quot;。连续封控的那十八天 虽然不能出门,但物资不缺,少了通勤时间,感觉一天时间变长了,有时间自己做饭,心情也很平静。</p> <p>六月底开始远程办公后我想着何不回家住会呢?当时以为自己待不了多久,没带多少行李,踩着双拖鞋就回家了,没想到至今我都没有再返回深圳。 回家住这么久最大的收获就是重新和我爸认识了解了一遍;其次就是吃了各种很多年没有吃过的应季食物吧。</p> <p><img src="https://ppd0705.github.io/image/2022_summary/xiaoyuxia.jpg" alt="xiaoyuxia"></p> <p>在农村就久住就会发现出门没有车实在不方便,于是在九月初买了车。开始我想我这技术最远去个县城就顶天了,没想到 一个月之后就怀着忐忑的心情独自一个人北上去了长沙,但在长沙两个月我就开了2次车,哈哈。在长沙见了几次同学,感受了一下他们的生活状态; 住在湘江边上的安置小区,很多本地人,坐在宣传栏边上聊天的大妈都是人手一栋楼,你停下来看租房广告便会问你要什么样的户型的; 一个偶尔的机缘我报了一门口语课,这是我第一次上口语课,算是很新鲜的体验吧,上课确实算一种和一个新地方建立连接的方式, 尤其是口语课,顺着课程话题就认识了解老师和同学的经历了。</p> <p>今年跑步跑了 320 公里,里程数相比去年少,主要还是因为在农村不太方便跑步。</p> <p>今年看的书也比较少,最推荐的书是《也许你该找个人聊聊》,一些笔记:</p> <ul> <li>我们是在人与人的关系中成长的</li> <li>我们大多数人的心中都会有个&quot;他们&quot;,即使并没有谁在观察我们的生活</li> <li>我意识到,即使到了七十岁,你的心还是会像十七岁时一样脆弱,容易受伤,充满渴望和激情</li> <li>当人们不知道改变会带来什么的时候,往往不愿意放弃现有的东西,这个阶段的痛点是改变意味着失去,而新情况有又叫人不安</li> <li>我们以为罗列遗愿清单是为了避免遗憾,但事实上我们是在靠它回避死亡</li> <li>痛苦没有高低等级之分</li> </ul> <p>今年年假还一天没用,利用周末和法定节假日就近旅行也玩耍了不少,上半年跟着前同事自驾游了 2 次;下半年和家人去了一下衡山、曾国藩故居,自己又独自爬了曾国藩古道。</p> <p>今年世界杯我一场没看,但我看了很多 LOL 的比赛,夏季赛和世界赛都很精彩,谁能想到 4 号种子能逆袭夺冠呢?</p> <p>今年的最后一个周一,至今没有打疫苗的我终于感染了新冠,头两天发烧头痛,第三天好了之后开始咳嗽和嗓子疼,整体上影响不大了。</p> <h1 id="展望">展望</h1> <ul> <li>多找人聊聊天</li> <li>日常跑步,参加一次半马</li> <li>日常英语练习,精读《How I Met Your Mother》Season 1</li> <li>带家人旅行一次</li> <li>深度学习一下 nats,至少产出一篇文章</li> <li>入门一下 C++,了解其优劣</li> </ul> nats丢消息排查小记 https://ppd0705.github.io/post/debug_nats_message_losing/ Tue, 13 Dec 2022 09:24:20 +0800 https://ppd0705.github.io/post/debug_nats_message_losing/ <h2 id="背景">背景</h2> <p>最近我们把消息队列从 kafka 迁移到 nats, 主要原因是kafka的延迟不能忍受,5ms 其实也不小了,但在几千上万个 topic 的情况下,延迟有时会上升至数百 ms(我猜 topic 多的情况下顺序写的优势不再存在)。迁移到 nats 之后, 使用 Publish-Subscribe 的延迟大概就 1ms, 真香!</p> <p>但小部分场景需要用到 JetStream 来做持久化,使用了 JetStream 一段时间后发现,每天会随机的丢几条消息,发生的时间完全没有规律。</p> <h2 id="排查过程">排查过程</h2> <p>通过命令行查 Stream 消息,发现丢失的消息是有被持久化的,所以问题要么出在服务端:没有把消息发送给客户端;要么客户端把消息弄丢了。 大概看了一些服务端的代码,感觉没有头绪,想想还是从客户端入手比较简单。</p> <p>首先在客户端接收原始消息那里加了一行日志,经过核对发现客户端确实收到了消息,但没有返回到上层,中间某个地方把消息弄丢了。</p> <p>再看丢消息的时间点刚好在超时时间的临界点,猜想应该是超时处理的时候把消息丢了。于是写了一个消费者测试代码,把超时时间从 1 秒 调到 0.1 秒,发现 丢消息的概率显著提升。</p> <p>最后确实发现了丢消息的地方(<a href="https://github.com/nats-io/nats.py/blob/8d75773d5529d9b4299099752a0c2e26ab862d19/nats/aio/subscription.py#L159">代码</a>)。</p> <p>但中间由于我加 debug 日志不够,产生了一个让我费解的疑问。具体如下:</p> <p><img src="https://ppd0705.github.io/image/debug_nats_message_losing/01.PNG" alt="code"></p> <p>我在 164、167、174 行加了 debug 代码。</p> <p><img src="https://ppd0705.github.io/image/debug_nats_message_losing/02.PNG" alt="log"></p> <p>从日志截图来看,代码运行到 164 后直接到了 174 行,167 行没有被运行。这里让我很不接,明明没有协程切换,为什么 167 行没有执行到。</p> <p><img src="https://ppd0705.github.io/image/debug_nats_message_losing/03.jpg" alt="dialog"></p> <p>知道在前同事群里请教,才发现是 166 行 <code>set_result</code> 报错了,所以没有执行到 167 行。</p> <p>消除这个疑问之后,发现了问题的本源: <img src="https://ppd0705.github.io/image/debug_nats_message_losing/04.png" alt="cancel_future"></p> <p>获取消息超时后,会取消<code>future</code>,但问题是取消的过程中可能发生协程切换,如果切换到 <code>pending_queue.get()</code>这个协程方法,就会导致丢掉一条消息。</p> <h2 id="相关链接">相关链接</h2> <ul> <li><a href="https://github.com/nats-io/nats.py/issues/392">丢消息issue</a></li> </ul> 你好!三十岁 https://ppd0705.github.io/post/hello_30/ Tue, 02 Aug 2022 06:30:31 +0800 https://ppd0705.github.io/post/hello_30/ <p>不曾想到今年可以有机会在家远程工作,好似迎来了一个久违的&quot;暑假&quot;,而在这期间三十岁也向我招来了手。 虽然我不怎么过生日,也不怎么吃蛋糕,但还是想在这个节点用文字来回顾来时路,记录当下时,展望未来希。</p> <p>我应该从小算一个偏内向、简单、不捣蛋的小孩。 不知道是不是晚一年上学的缘故,一开始学习上就比较顺利,考试拿第一名,当班长帮老师打杂。 我家的教育方针应该是那种能读就读的放任自然式,拿了奖状回家也没有奖励那种。 然后有三五个从幼儿园到初中同路上学的玩伴,其实整体上我的学校生活还是蛮轻松愉快的。</p> <p>十岁那年算我家发生变故的一年,妈妈突然去世,几个月之后奶奶突发眼疾彻底失明。我当然没有一夜长大的觉悟, 继续和同伴上学放学,只不过不再回自己家,而是去伯伯们家,奶奶轮到哪个伯伯照顾,我就跟着去哪家,所以我可以算吃四家饭长大的。 伯伯们其实都还算好,不过由此变得敏感一些是难免的。</p> <p>夸张一点说我和奶奶从此算是相依为命了,我成了奶奶的眼睛,奶奶成了我的港湾。俨然奶奶是那个无条件爱我的人,那些放学回家特意留给我的零食、那把夏天驱蚊的蒲扇、 那些冬天为我烤干的湿袜子,以及奶奶那份至死保留的独立精神,都沉淀在的我人生底色中。</p> <p>稍微长大的我还是想去外面看看,高考没有填一个省内志愿,大学毕业也想着既然在华东读书,也在华东工作看看。</p> <p>工作后的第二个春节回家,我已经感受到奶奶已经快走到尽头了,我也感受到工作的城市即使有大学室友、胜似同学的同事仍感受不到归属感。 于是辞了工作,家里蹲了半年,陪奶奶过了九十岁生日,自己重新归零学习另外一个领域的东西转行。</p> <p>奶奶离开后,我有两年春节没有回家,因为我觉得春节就是在急急忙忙地走过场没有意思,但今年我回了,主要原因是我接纳了爸爸, 也看到了他需要我。</p> <p>重塑父子关系应该算很要的人生功课。我和爸爸缺失了太多相处时光,由此产生了很多隔阂。 我觉得他有很多做的不好的地方,比如喝酒逞强把自己喝醉然后再睡一天觉、屁股粘在牌桌上而没有时间陪伴家人。 我也缺乏很多对他的了解,比如我不知道他中年丧妻当时是什么感受怎么走过来的、如何面对衰老和其带来劳动价值的完全丧失。</p> <p>我想人是会变的。以前我可能会趁着远程办公的机会去某个远方旅居一段时间,而这次我选择了回家。 自己开玩笑说可能过不了几天会被爸爸赶出来,但是并没有,我和爸爸两个人相处得很好,可能我们都变了。 我之前会想要是奶奶还在就好了,现在也释怀了,奶奶离开对她也算一种解脱,而我要怀着期许好好活着。 母胎单身的我今年居然告了个白,虽然略显尴尬,但把心里话说出来,心结就解开了。</p> <p>最近的状态就是六点起床,二十二点睡觉的节奏,我其实很享受这样的节奏。 我几乎不依赖博物馆、图书馆、电影院、酒吧、咖啡馆、外卖等城市资源; 相比有些吵闹的大开间办公空间,我更喜欢安静的环境。 最近工作也进入了一个甜蜜期,有机会用一个新语言按自己的想法重新写一套新系统,从五一开动,到现在大体雏形完成。</p> <p>聊一嘴未来,大环境下金融危机、战争可能随时会来;抵不过社会运转规律,方言会消失,农村会凋零,想想有点悲凉。 还是希望世界和平、社会更加开放和包容。对于个人,希望家人平安健康,希望能找到人生队友,希望工作顺利。</p> [笔记]k8s入门 https://ppd0705.github.io/post/getting_started_with_k8s/ Sun, 24 Jul 2022 10:50:35 +0800 https://ppd0705.github.io/post/getting_started_with_k8s/ <h1 id="第零讲-环境准备">第零讲 环境准备</h1> <p>使用 virtualbox 安装 ubuntu 虚拟环境</p> <h2 id="个人采坑记录">个人采坑记录</h2> <h3 id="共享粘贴板设置">共享粘贴板设置</h3> <p>配置了双向粘贴版 和 安装 <code>apt install virtualbox-guest-utils</code> 还是不行, 每次得手动启动 <code>VBoxClient --clipborad</code>. 通过 <code>df -h</code> 查看目录找到 挂载的脚步 在 <code>/media/ubuntu/VBox_GAs_6.1.34/</code> 目录 后面卸载 virtualbox-guest-utils, 运行 <code>sh /media/ubuntu/VBox_GAs_6.1.34/VBoxLinuxAdditions.run</code> 安装才可以</p> <h1 id="第一讲-初始容器">第一讲 初始容器</h1> <p><code>sudo usermod -aG docker ${USER}</code> 将当前用户加入 docker 组,然后重启, 否则需要 root 权限。</p> <p><code>docker version</code> 查看客户端和服务端版本信息</p> <p><code>docker info</code> 查看服务端系统信息</p> <h1 id="第二讲-被隔离的进程">第二讲 被隔离的进程</h1> <p>容器就是一个特殊的隔离环境,它能够让进程只看到这个环境里的有限信息。</p> <p>隔离实现基于 Linux 提供的三种技术:</p> <ul> <li>namcespace:资源隔离</li> <li>cgroup:配额限制</li> <li>chroot:文件系统根目录限制</li> </ul> <h1 id="第三讲-docker-实用命令">第三讲 docker 实用命令</h1> <h2 id="常用镜像操作命令">常用镜像操作命令</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/3_1.webp" alt="image_cmd"></p> <h2 id="常用容器操作命令">常用容器操作命令</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/3_2.webp" alt="container_cmd"></p> <h1 id="第四讲-dockerfile-编写">第四讲 Dockerfile 编写</h1> <p>镜像是由许多只读层堆叠起来的</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/4_1.webp" alt="image_layer"></p> <p>可以通过 <code>docker inspect</code>命令查看镜像的分层信息</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/4_2.webp" alt="image_layer2"></p> <p>示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"># Dockerfile </span></span><span class="line"><span class="cl"># docker build -t ngx-app . </span></span><span class="line"><span class="cl"># docker build -t ngx-app:1.0 . </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">ARG IMAGE_BASE=&#34;nginx&#34; </span></span><span class="line"><span class="cl">ARG IMAGE_TAG=&#34;1.21-alpine&#34; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">FROM ${IMAGE_BASE}:${IMAGE_TAG} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">COPY ./default.conf /etc/nginx/conf.d/ </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">RUN cd /usr/share/nginx/html \ </span></span><span class="line"><span class="cl"> &amp;&amp; echo &#34;hello nginx&#34; &gt; a.txt </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">EXPOSE 8081 8082 8083 </span></span></code></pre></td></tr></table> </div> </div><p>第五讲 镜像仓库</p> <p>镜像仓库 registry 是提供镜像服务的网站,如上传、下载镜像等</p> <p><a href="https://hub.docker.com">Docker Hub</a> 是当前最大的镜像仓库,上面有官方镜像、认证镜像、非官方镜像等</p> <p>名字规则: user/image:tag</p> <p>离线环境可以使用归档命令来共享</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker save ngx-app:latest -o ngx.tar </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">docker load -i ngx.tar </span></span></code></pre></td></tr></table> </div> </div><h1 id="第六讲-容器如何如外接互通">第六讲 容器如何如外接互通</h1> <h2 id="如何拷贝容器内数据">如何拷贝容器内数据</h2> <ul> <li>宿主机到容器</li> </ul> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker cp a.txt 062:/tmp </span></span></code></pre></td></tr></table> </div> </div><ul> <li>容器到宿主机</li> </ul> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker cp 062:/tmp/a.txt ./b.txt </span></span></code></pre></td></tr></table> </div> </div><h2 id="如何共享文件目录">如何共享文件目录</h2> <p>volume 挂载</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker run -v host_path:container_path image:tag </span></span></code></pre></td></tr></table> </div> </div><h2 id="如何网络互通">如何网络互通</h2> <h3 id="host-模式">host 模式</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker run -d --rm --net<span class="o">=</span>host nginx:alpine </span></span></code></pre></td></tr></table> </div> </div><p>对比 <code>ip addr</code>命令在宿主机和容器的结果,可以发现是相同的</p> <h3 id="bridge-模式">bridge 模式</h3> <p>桥接模式类似现实世界的交换机,不过是由软件虚拟出来的, 容器和宿主机通过虚拟网卡接入网桥 docker0,收发网络包</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/6_1.webp" alt="bridge_docker0"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">docker run -d --rm --net<span class="o">=</span>bridge nginx:alpine </span></span></code></pre></td></tr></table> </div> </div><p>使用 <code>-p host_port:container_port</code> 映射宿主机和容器端口</p> <h1 id="第九讲-本机搭建-k8s-环境">第九讲 本机搭建 k8s 环境</h1> <p>使用 <a href="https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/">minikube</a></p> <p>启动 minikube</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">minikube start --kubernetes-version<span class="o">=</span>v1.23.3 --image-mirror-country<span class="o">=</span>cn </span></span></code></pre></td></tr></table> </div> </div><p>启动容器服务</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">kubectl</span><span class="o">=</span><span class="s2">&#34;minikube kubectl --&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl run ngx --image<span class="o">=</span>nginx:alpine </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十讲-k8s-工作机制">第十讲 k8s 工作机制</h1> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/10_1.webp" alt="k8s_architecture"></p> <p>k8s 有许多模块组成,这些模块可分为组件(component)和插件(addon)两类</p> <h2 id="master-组件">master 组件</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/10_2.webp" alt="master"></p> <h3 id="apiserver">apiserver</h3> <p>apiserver 是 k8s 系统唯一入口,对外提供了一些列 RESTful API,其他组件 只能和它直接通信, 相当于联络员</p> <h3 id="etcd">etcd</h3> <p>etcd 用来持久化存储系统里各种资源对象和状态,相当于配置管理员。 其他组件想读写 etcd 的数据必须通过 apiserver</p> <h3 id="scheduler">scheduler</h3> <p>scheduler 负责容器的编排工作,检查阶段的资源状态,把 pod 调度到 最合适的节点运行, 相当于部署人员。</p> <h3 id="controller-manager">controller-manager</h3> <p>controller-manager 负责维护容器和节点等资源状态,实现故障检测、服务迁移、应用伸缩等功能, 相当于监控运维人员。</p> <h2 id="node-组件">Node 组件</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/10_3.webp" alt="node"></p> <h3 id="kubelet">kubelet</h3> <p>kubelet 负责管理 Node 相关绝大部分操作, Node 上只有它与 apiserver 通信, 实现状态报告、命令下发、启停容器等功能,相当于小管家。</p> <p>kubelet 因为要管理整个节点,容器化会限制它的能力,所有运行在物理机上。</p> <h3 id="kube-proxy">kube-proxy</h3> <p>kube-proxy 是 Node 的网络代理,转发 TCP/UDP 数据包,相当于专职小邮差</p> <h3 id="container-runtime">container-runtime</h3> <h2 id="插件">插件</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/10_4.webp" alt="addons"></p> <p>比较重要的两个插件:DNS、Dashboard</p> <h1 id="第十一讲-api">第十一讲 API</h1> <h2 id="yaml">YAML</h2> <p>k8s 使用 yaml 声明式(declarative)地描述资源</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/11_1.webp" alt="yaml"></p> <h2 id="api-对象">API 对象</h2> <p>k8s 在理论层面抽象出了许多个概念,用来描述系统的管理运维工作,这些概念叫做 &ldquo;API 对象&rdquo;。</p> <p>可以使用<code>kubectl api-resources</code> 查看对象列表</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/11_2.webp" alt="api_resources"></p> <h2 id="如何描述-api-对象">如何描述 API 对象</h2> <p>命令式启动pod <code>kubectl run ngx --image=nginx:alpine</code> 可以改写成如下声明式配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">env</span><span class="p">:</span><span class="w"> </span><span class="l">demo</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="l">chrono</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>可以将 配置类比成 HTTP 报文,分为两部分</p> <ul> <li>header: apiVersion, kind, metadata</li> <li>body: spec</li> </ul> <p>可以使用如下命令创建和删除资源</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl apply -f ngx-pod.yaml </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl delete -f ngx-pod.yaml </span></span></code></pre></td></tr></table> </div> </div><h2 id="编写-yaml-技巧">编写 yaml 技巧</h2> <ol> <li>使用<code>kubectl api-resources</code>查看对应的 API 版本 和 类型</li> <li>使用<code>kubectl explain</code> 查看对应资源的详细说明</li> <li>使用<code>--dry-run=client -o yaml</code> 生成 yaml 文件</li> </ol> <h1 id="第十二讲-pod">第十二讲 Pod</h1> <p>Pod 是对容器的&quot;打包&quot;,是 k8s 最小的调度部署单位, 类似于 Linux 里面的进程组</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/12_1.webp" alt="pod"></p> <h2 id="如何使用-yaml-描述-pod">如何使用 yaml 描述 Pod</h2> <p>Pod 有四个基本组成部分</p> <ul> <li>apiVersion: 固定为 v1</li> <li>kind: 固定为 Pod</li> <li>metadata: <ul> <li>name: 指定名称,可以带 pod 后缀方便和其他资源区分</li> <li>labels: 标签,可添加任意的 key/value, 如 env=dev、region=north</li> </ul> </li> <li>containers: 容器列表 <ul> <li>image: 镜像</li> <li>name: 名字</li> <li>ports:对外暴露的端口</li> <li>imagePullPolicy: 镜像拉取策略,默认为 IfNotPresent, 本地不存在才会来取远程镜像</li> <li>env: 定义环境变量,运行时指定</li> <li>command: 容器启动时执行的命令,相当于Dockerfile 的 ENTRYPOINT</li> <li>args: command 参数,相当于 Dockerfile 的 CMD 指令</li> </ul> </li> </ul> <h2 id="如何使用-kubectl-操作-pod">如何使用 kubectl 操作 Pod</h2> <p>使用 <code>kubectl apply -f</code>、<code>kubectl delete -f</code> 创建或删除 Pod</p> <p>使用 <code>kubectl logs</code> 产看日志</p> <p>使用 <code>kubectl exec -it $pod -- sh</code> 进入 Pod 内部</p> <h1 id="第十三讲-jobcronjob">第十三讲 Job/CronJob</h1> <p>Job/CronJob 都属于离线业务,区别在于 Job 只运行一次</p> <h2 id="job">Job</h2> <p>Job yaml 相比 Pod, 区别在于 spec 使用 template 字段来定义 Pod</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/13_1.webp" alt="job"></p> <p>使用 <code>kubectl apply -f job.yaml</code> 启动 job</p> <p>使用 <code>kubectl get job</code> 查看 job 状态 使用 <code>kubectl get pod</code> 查看 job 中的 pod 的 状态</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/13_2.webp" alt="job_status"></p> <p>部分离线作业重要字段</p> <ul> <li>activeDeadlineSeconds: pod 超时时间</li> <li>backoffLimit: pod 失败重试次数</li> <li>completions: Job 需要完成运行多少个 Pod, 默认为1</li> <li>parallelism: completions 对应的并发度</li> </ul> <p>示例如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">batch/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Job</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sleep-job</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">activeDeadlineSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">15</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">backoffLimit</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">completions</span><span class="p">:</span><span class="w"> </span><span class="m">4</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">parallelism</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">OnFailure</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">busybox</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">echo-job</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">sh</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- -<span class="l">c</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">sleep $(($RANDOM % 10 + 1)) &amp;&amp; echo done</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><h2 id="cronjob">CronJob</h2> <p>CronJob 其实组合了Job</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/13_3.webp" alt="cronjob"></p> <h1 id="第十四讲-configmapsecret">第十四讲 ConfigMap/Secret</h1> <p>ConfigMap 保存明文配置, Secret 保存 私密配置</p> <h2 id="configmap">ConfigMap</h2> <p>ConfigMap 可以使用 <code> kubectl create cm info --from-literal=aa=bb --dry-run=client -o yaml &gt; info-cm.ayml</code> 创建模版</p> <p>示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">info</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">data</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">count</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;10&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">debug</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;on&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;/etc/systemd&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">greeting</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"> </span></span></span><span class="line"><span class="cl"><span class="sd"> </span><span class="w"> </span><span class="l">say hello to kubernetes.</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>创建和查看</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl apply -f info-cm.yaml </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl get cm </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl describe cm info </span></span></code></pre></td></tr></table> </div> </div><h2 id="secret">Secret</h2> <p>Secret 使用 <code>kubectl create secret generic</code> 创建模板</p> <p>示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Secret</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">data</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cm9vdA== </span><span class="w"> </span><span class="c"># root</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">pwd</span><span class="p">:</span><span class="w"> </span><span class="l">MTIzNDU2 </span><span class="w"> </span><span class="c"># 123456</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">db</span><span class="p">:</span><span class="w"> </span><span class="l">bXlzcWw= </span><span class="w"> </span><span class="c"># mysql</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl create secret generic user --from-literal<span class="o">=</span><span class="nv">aa</span><span class="o">=</span>bb --dry-run<span class="o">=</span>client -o yaml &gt; user-secret.yaml </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl apply -f user-secret.yaml </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl get secret </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl describe secret user </span></span></code></pre></td></tr></table> </div> </div><h2 id="使用">使用</h2> <h3 id="环境变量">环境变量</h3> <p>使用 <code>configMapKeyRef</code>和<code>secretKeyRef</code> 来引用</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">env-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">env</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">COUNT</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configMapKeyRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">info</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">count</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">GREETING</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configMapKeyRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">info</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">greeting</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">USERNAME</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">secretKeyRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">name</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">PASSWORD</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">valueFrom</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">secretKeyRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">pwd</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">busybox</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">busy</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;/bin/sleep&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;300&#34;</span><span class="p">]</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>引用示意图如下</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/14_1.webp" alt="config_map_ref"></p> <h3 id="加载文件">加载文件</h3> <p>Pod 可以挂载 Volume, 可以在 <code>spec</code> 中指定,在 <code>containers</code>引用</p> <p>引用示意图如下</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/14_2.webp" alt="config_map_volume"></p> <p>示意配置如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vol-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cm-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configMap</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">info</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sec-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">secret</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">secretName</span><span class="p">:</span><span class="w"> </span><span class="l">user</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">volumeMounts</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/cm-items</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cm-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/sec-items</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sec-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">busybox</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">busy</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">imagePullPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">IfNotPresent</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;/bin/sleep&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;300&#34;</span><span class="p">]</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>挂载后,ConfigMap 和 Secret 的 键成了文件名,值成了文件内容</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/14_3.webp" alt="config_map_volume2"></p> <h1 id="第十七讲-搭建多节点-k8s-集群">第十七讲 搭建多节点 k8s 集群</h1> <h2 id="实验环境准备">实验环境准备</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/17_1.webp" alt="machine"></p> <h2 id="安装前">安装前</h2> <ol> <li>修改hostname, k8s 使用 hostname 来区分集群节点,所以 hostname 不能重名。</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo vim /etc/hostname </span></span></code></pre></td></tr></table> </div> </div><ol start="2"> <li>安装 docker 运行时,将其 cgroup 的驱动程序改为 systemd</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/docker/daemon.json </span></span></span><span class="line"><span class="cl"><span class="s">{ </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;exec-opts&#34;: [&#34;native.cgroupdriver=systemd&#34;], </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;log-driver&#34;: &#34;json-file&#34;, </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;log-opts&#34;: { </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;max-size&#34;: &#34;100m&#34; </span></span></span><span class="line"><span class="cl"><span class="s"> }, </span></span></span><span class="line"><span class="cl"><span class="s"> &#34;storage-driver&#34;: &#34;overlay2&#34; </span></span></span><span class="line"><span class="cl"><span class="s">} </span></span></span><span class="line"><span class="cl"><span class="s">EOF</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> docker </span></span><span class="line"><span class="cl">sudo systemctl daemon-reload </span></span><span class="line"><span class="cl">sudo systemctl restart docker </span></span></code></pre></td></tr></table> </div> </div><ol start="3"> <li>启用 br_netfilter 模块, 使 k8s 能够检查和转发网络流量</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/modules-load.d/k8s.conf </span></span></span><span class="line"><span class="cl"><span class="s">br_netfilter </span></span></span><span class="line"><span class="cl"><span class="s">EOF</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf </span></span></span><span class="line"><span class="cl"><span class="s">net.bridge.bridge-nf-call-ip6tables = 1 </span></span></span><span class="line"><span class="cl"><span class="s">net.bridge.bridge-nf-call-iptables = 1 </span></span></span><span class="line"><span class="cl"><span class="s">net.ipv4.ip_forward=1 # better than modify /etc/sysctl.conf </span></span></span><span class="line"><span class="cl"><span class="s">EOF</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">sudo sysctl --system </span></span></code></pre></td></tr></table> </div> </div><ol start="4"> <li>关闭 swap, 提升 k8s 性能</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo swapoff -a </span></span><span class="line"><span class="cl">sudo sed -ri <span class="s1">&#39;/\sswap\s/s/^#?/#/&#39;</span> /etc/fstab </span></span></code></pre></td></tr></table> </div> </div><h2 id="安装-kubeadm">安装 kubeadm</h2> <ol> <li>更换 k8s 下载源</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt install -y apt-transport-https ca-certificates curl </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg <span class="p">|</span> sudo apt-key add - </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">cat <span class="s">&lt;&lt;EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list </span></span></span><span class="line"><span class="cl"><span class="s">deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main </span></span></span><span class="line"><span class="cl"><span class="s">EOF</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">sudo apt update </span></span></code></pre></td></tr></table> </div> </div><ol start="2"> <li>下载 kubeadm</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt install -y <span class="nv">kubeadm</span><span class="o">=</span>1.23.3-00 <span class="nv">kubelet</span><span class="o">=</span>1.23.3-00 <span class="nv">kubectl</span><span class="o">=</span>1.23.3-00 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 锁定版本,避免错误升级</span> </span></span><span class="line"><span class="cl">sudo apt-mark hold kubeadm kubelet kubectl </span></span></code></pre></td></tr></table> </div> </div><ol start="3"> <li>下载镜像</li> </ol> <p>由于 k8s 镜像在 google 镜像仓库 gcr.io,国内访问困难, 改用国内镜像,然后使用 docker tag 改名</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">repo</span><span class="o">=</span>registry.aliyuncs.com/google_containers </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">for</span> name in <span class="sb">`</span>kubeadm config images list --kubernetes-version v1.23.3<span class="sb">`</span><span class="p">;</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nv">src_name</span><span class="o">=</span><span class="si">${</span><span class="nv">name</span><span class="p">#k8s.gcr.io/</span><span class="si">}</span> </span></span><span class="line"><span class="cl"> <span class="nv">src_name</span><span class="o">=</span><span class="si">${</span><span class="nv">src_name</span><span class="p">#coredns/</span><span class="si">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> docker pull <span class="nv">$repo</span>/<span class="nv">$src_name</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> docker tag <span class="nv">$repo</span>/<span class="nv">$src_name</span> <span class="nv">$name</span> </span></span><span class="line"><span class="cl"> docker rmi <span class="nv">$repo</span>/<span class="nv">$src_name</span> </span></span><span class="line"><span class="cl"><span class="k">done</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="安装-master-节点">安装 master 节点</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># --pod-network-cidr 设置集群使用网段</span> </span></span><span class="line"><span class="cl"><span class="c1"># --apiserver-advertise-address 设置apiserver 地址</span> </span></span><span class="line"><span class="cl">sudo kubeadm init <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --pod-network-cidr<span class="o">=</span>10.10.0.0/16 <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --apiserver-advertise-address<span class="o">=</span>192.168.10.210 <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --kubernetes-version<span class="o">=</span>v1.23.3 </span></span></code></pre></td></tr></table> </div> </div><p>安装后复制 kubectl 配置文件</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir -p <span class="nv">$HOME</span>/.kube </span></span><span class="line"><span class="cl">sudo cp -i /etc/kubernetes/admin.conf <span class="nv">$HOME</span>/.kube/config </span></span><span class="line"><span class="cl">sudo chown <span class="k">$(</span>id -u<span class="k">)</span>:<span class="k">$(</span>id -g<span class="k">)</span> <span class="nv">$HOME</span>/.kube/config </span></span></code></pre></td></tr></table> </div> </div><p>加入集群</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubeadm join 192.168.10.210:6443 --token tv9mkx.tw7it9vphe158e74 <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --discovery-token-ca-cert-hash sha256:e8721b8630d5b562e23c010c70559a6d3084f629abad6a2920e87855f8fb96f3 </span></span></code></pre></td></tr></table> </div> </div><p>安装网络插件,这里使用 flannel</p> <p>使用配置模版 <a href="https://github.com/flannel-io/flannel/blob/master/Documentation/kube-flannel.yml">kube-flannel.yml</a>, 修改网段</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">net-conf.json</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"> </span></span></span><span class="line"><span class="cl"><span class="sd"> { </span></span></span><span class="line"><span class="cl"><span class="sd"> &#34;Network&#34;: &#34;10.10.0.0/16&#34;, </span></span></span><span class="line"><span class="cl"><span class="sd"> &#34;Backend&#34;: { </span></span></span><span class="line"><span class="cl"><span class="sd"> &#34;Type&#34;: &#34;vxlan&#34; </span></span></span><span class="line"><span class="cl"><span class="sd"> } </span></span></span><span class="line"><span class="cl"><span class="sd"> }</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>应用</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl apply -f kube-flannel.yml </span></span></code></pre></td></tr></table> </div> </div><p>使用 <code>kubectl get node</code> 验证</p> <h2 id="安装-worker-节点">安装 worker 节点</h2> <p>克隆 master 虚拟机, 使用 <code>kubeamd join</code> 指令加入即可</p> <h1 id="第十八讲-deployment">第十八讲 Deployment</h1> <p>Deployment 主要用来管理在线业务 Pod</p> <p>生成默认模版</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl create deploy ngx-dep --image<span class="o">=</span>nginx:alpine --dry-run<span class="o">=</span>client -o yaml &gt; ngx-dep.yaml </span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p><code>replicas</code> 表示副本数量</p> <p><code>selector</code> 作用是筛选出要被 Deployment 管理的 Pod 对象, 下属 <code>matchLabels</code> 必须和 <code>template</code> 的 <code>labels</code> 完全相同</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/18_1.webp" alt="deployment_yaml"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 查看deploy状态</span> </span></span><span class="line"><span class="cl">kubectl get deploy </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 使用标签筛选 pod</span> </span></span><span class="line"><span class="cl">kubectl get pod -l <span class="nv">app</span><span class="o">=</span>ngx-dep </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 调整 replicas</span> </span></span><span class="line"><span class="cl">kubectl scale --replicas<span class="o">=</span><span class="m">5</span> deploy ngx-dep </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十九讲-daemonset">第十九讲 DaemonSet</h1> <h2 id="介绍">介绍</h2> <p>DaemonSet 会在 满足条件的所有节点上运行一个 Pod, 就像 Linux 系统里的 守护进程(Daemon)</p> <p>一些应用与节点存在绑定关系,如</p> <ul> <li>网络应用 kube-proxy</li> <li>监控应用 prometheus</li> <li>日志应用 fluentd</li> </ul> <p>DaemonSet 不可以用 <code>kubectl create</code> 创建模板,可以在 Deployment 模版上修改 , 主要区别就是 DaemonSet 没有 <code>replicas</code> 字段</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/19_1.webp" alt="daemon_set_yaml"></p> <h2 id="taint-和-toleration">taint 和 toleration</h2> <p>节点的标签有一个专门字段<code>taint</code> 污点来表述,Pod 可以配置 <code>toleratoin</code> 来筛选 taint</p> <p>taint 可以在 kubectl describe node 查看</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 添加 taint</span> </span></span><span class="line"><span class="cl">kubectl taint node master node-role.kubernetes.io/master:NoSchedule- </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 结尾加&#34;-&#34; 来移除 taint</span> </span></span><span class="line"><span class="cl">kubectl taint node master node-role.kubernetes.io/master:NoSchedule- </span></span></code></pre></td></tr></table> </div> </div><p>可以 Pod 的 spec 下 选择 taint</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">tolerations</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">node-role.kubernetes.io/master</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">effect</span><span class="p">:</span><span class="w"> </span><span class="l">NoSchedule</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">Exists</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><h2 id="静态-pod">静态 Pod</h2> <p>静态 Pod 不受 k8s 系统管控, 不与 apiserver 发生关系</p> <p>对应配置文件默认存放在 /etc/kubernetes/manifests 目录下</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/19_2.webp" alt="static_pods"></p> <p>如 master 节点的四个核心组件都是以 静态 Pod 形式存在的</p> <h1 id="第二十讲-service">第二十讲 Service</h1> <p>Service 工作原理和 Nginx 差不多, k8s 会分给它一个静态 IP, 然后它再去自动管理维护后面动态变化的 Pod 集合,当客户端访问 Service,它根据某种策略把流量转发给后面某个 Pod.</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/20_1.webp" alt="service"></p> <p>可以使用如下命令创建 yaml 模版</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl expose deploy ngx-dep --port<span class="o">=</span><span class="m">80</span> --target-port<span class="o">=</span><span class="m">80</span> --dry-run<span class="o">=</span>client -o yaml </span></span></code></pre></td></tr></table> </div> </div><p>配置中主要有两个关键字 <code>selector</code> 和 <code>ports</code></p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/20_2.webp" alt="service_yaml"></p> <p>查看 ngx-svc 详情</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/20_3.webp" alt="service_detail"></p> <p>exec 到 pod 进行测试</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/20_4.webp" alt="service_test"></p> <p>k8s 会自动给对象分配域名,可以使用 <code>ngx-svc</code>等域名访问</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/20_5.webp" alt="service_dns"></p> <p>service 默认为 <code>ClusterIP</code> 类型,即静态 IP 只能集群内部访问。 可以使用 <code>NodePort</code> 类型, 这样节点会提供一个对外暴露的端口指向该 service</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="l">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">NodePort</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>NodePort 类型有如下缺点:</p> <ul> <li>端口有限,默认只在 30000~32767 范围分配</li> <li>每个节点都开端口,提高了网络通信成本</li> <li>需要对外暴露节点的 IP 地址,为了安全需要搭建一个反向代码,增加了复杂度。</li> </ul> <h1 id="第二十二讲-实战演练">第二十二讲 实战演练</h1> <h2 id="要点回顾">要点回顾</h2> <p>Service 是 Pod IP 地址的抽象,它拥有一个固定的 IP 地址,再使用 iptables 规则 把 流量负责均衡到后面的 Pod, 节点上的 kube-proxy 组件后实时维护被代理的 Pod 状态, 保证 Service 只会转发给健康的 Pod</p> <p>Service 还基于 DNS 插件支持域名,所以客户端不在需要关心 Pod 具体情况</p> <p>Service 是四层的负载均衡,而 Ingress 是 七层负责均衡</p> <p>Ingress 需要 IngressController 和 IngressClass 配合工作</p> <ul> <li>Controller 是真正的集群入口,应用 Ingress 规则调度、分发流量;此外还提供反向代理的角色</li> <li>Class 是用来分组管理 Ingress</li> </ul> <p>IngressController 本身也是一个 Pod, 需要通过 NodePort、LoadBalancer等方式暴露到集群外。</p> <h2 id="wordpress-网站搭建">WordPress 网站搭建</h2> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/21_1.webp" alt="wp_arc"></p> <h3 id="1-部署-mariadb">1. 部署 MariaDB</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-cm</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">data</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">DATABASE</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;db&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">USER</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;wp&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;123&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ROOT_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;123&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mariadb:10</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mariadb</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">envFrom</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;MARIADB_&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configMapRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-cm</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">maria-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">maria-dep</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>使用 ConfigMap + Development + Service 的方式</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl appy -f wp-maria.yaml </span></span></code></pre></td></tr></table> </div> </div><h3 id="2-部署-wordpress">2. 部署 WordPress</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-cm</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">data</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">HOST</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;maria-svc&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">USER</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;wp&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;123&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">NAME</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;db&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress:5</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wordpress</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">envFrom</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">prefix</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;WORDPRESS_DB_&#39;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configMapRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-cm</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">http80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">nodePort</span><span class="p">:</span><span class="w"> </span><span class="m">30088</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">wp-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">NodePort</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">同样使用 ConfigMap + Development + Service 的方式 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">```shell </span></span><span class="line"><span class="cl">kubectl appy -f wp-dep.yaml </span></span></code></pre></td></tr></table> </div> </div><p><code>wp-svc</code> 使用 NodePort 暴露到集群外</p> <h3 id="3-部署-nginx-ingress-controller">3. 部署 Nginx Ingress Controller</h3> <p>定义 IngressClass</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">IngressClass</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-ink</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">controller</span><span class="p">:</span><span class="w"> </span><span class="l">nginx.org/ingress-controller</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>定义 Ingress</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">networking.k8s.io/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Ingress</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-ing</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ingressClassName</span><span class="p">:</span><span class="w"> </span><span class="l">wp-ink</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">rules</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">wp.test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">http</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">paths</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">pathType</span><span class="p">:</span><span class="w"> </span><span class="l">Prefix</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">backend</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">service</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wp-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">number</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>定义 IngressController</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/ </span></span><span class="line"><span class="cl">kind: Deployment </span></span><span class="line"><span class="cl">metadata: </span></span><span class="line"><span class="cl"> name: wp-kic-dep </span></span><span class="line"><span class="cl"> namespace: nginx-ingress </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">spec: </span></span><span class="line"><span class="cl"> replicas: 1 </span></span><span class="line"><span class="cl"> selector: </span></span><span class="line"><span class="cl"> matchLabels: </span></span><span class="line"><span class="cl"> app: wp-kic-dep </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> template: </span></span><span class="line"><span class="cl"> metadata: </span></span><span class="line"><span class="cl"> labels: </span></span><span class="line"><span class="cl"> app: wp-kic-dep </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> spec: </span></span><span class="line"><span class="cl"> serviceAccountName: nginx-ingress </span></span><span class="line"><span class="cl"> hostNetwork: true </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> dnsPolicy: ClusterFirstWithHostNet </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> containers: </span></span><span class="line"><span class="cl"> #- image: nginx/nginx-ingress:2.2.0 </span></span><span class="line"><span class="cl"> - image: nginx/nginx-ingress:2.2-alpine </span></span><span class="line"><span class="cl"> imagePullPolicy: IfNotPresent </span></span><span class="line"><span class="cl"> name: nginx-ingress </span></span><span class="line"><span class="cl"> ports: </span></span><span class="line"><span class="cl"> - name: http </span></span><span class="line"><span class="cl"> containerPort: 80 </span></span><span class="line"><span class="cl"> - name: https </span></span><span class="line"><span class="cl"> containerPort: 443 </span></span><span class="line"><span class="cl"> - name: readiness-port </span></span><span class="line"><span class="cl"> containerPort: 8081 </span></span><span class="line"><span class="cl"> - name: prometheus </span></span><span class="line"><span class="cl"> containerPort: 9113 </span></span><span class="line"><span class="cl"> readinessProbe: </span></span><span class="line"><span class="cl"> httpGet: </span></span><span class="line"><span class="cl"> path: /nginx-ready </span></span><span class="line"><span class="cl"> port: readiness-port </span></span><span class="line"><span class="cl"> periodSeconds: 1 </span></span><span class="line"><span class="cl"> securityContext: </span></span><span class="line"><span class="cl"> allowPrivilegeEscalation: true </span></span><span class="line"><span class="cl"> runAsUser: 101 #nginx </span></span><span class="line"><span class="cl"> capabilities: </span></span><span class="line"><span class="cl"> drop: </span></span><span class="line"><span class="cl"> - ALL </span></span><span class="line"><span class="cl"> add: </span></span><span class="line"><span class="cl"> - NET_BIND_SERVICE </span></span><span class="line"><span class="cl"> env: </span></span><span class="line"><span class="cl"> - name: POD_NAMESPACE </span></span><span class="line"><span class="cl"> valueFrom: </span></span><span class="line"><span class="cl"> fieldRef: </span></span><span class="line"><span class="cl"> fieldPath: metadata.namespace </span></span><span class="line"><span class="cl"> - name: POD_NAME </span></span><span class="line"><span class="cl"> valueFrom: </span></span><span class="line"><span class="cl"> fieldRef: </span></span><span class="line"><span class="cl"> fieldPath: metadata.name </span></span><span class="line"><span class="cl"> args: </span></span><span class="line"><span class="cl"> - -ingress-class=wp-ink </span></span><span class="line"><span class="cl"> - -health-status </span></span><span class="line"><span class="cl"> - -ready-status </span></span><span class="line"><span class="cl"> - -nginx-status </span></span><span class="line"><span class="cl"> - -enable-snippets </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config </span></span><span class="line"><span class="cl"> - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret </span></span><span class="line"><span class="cl"> #- -v=3 # Enables extensive logging. Useful for troubleshooting. </span></span><span class="line"><span class="cl"> #- -report-ingress-status </span></span><span class="line"><span class="cl"> #- -external-service=nginx-ingress </span></span><span class="line"><span class="cl"> #- -enable-prometheus-metrics </span></span><span class="line"><span class="cl"> #- -global-configuration=$(POD_NAMESPACE)/nginx-configuration </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">--- </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">apiVersion: v1 </span></span><span class="line"><span class="cl">kind: Service </span></span><span class="line"><span class="cl">metadata: </span></span><span class="line"><span class="cl"> name: wp-kic-svc </span></span><span class="line"><span class="cl"> namespace: nginx-ingress </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">spec: </span></span><span class="line"><span class="cl"> ports: </span></span><span class="line"><span class="cl"> - port: 80 </span></span><span class="line"><span class="cl"> protocol: TCP </span></span><span class="line"><span class="cl"> targetPort: 80 </span></span><span class="line"><span class="cl"> nodePort: 30080 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> selector: </span></span><span class="line"><span class="cl"> app: wp-kic-dep </span></span><span class="line"><span class="cl"> type: NodePort </span></span></code></pre></td></tr></table> </div> </div><p>需要执行 ingress 目录 setup.sh 脚本</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># https://github.com/chronolaw/k8s_study/blob/master/ingress/setup.sh</span> </span></span><span class="line"><span class="cl">./setup.sh </span></span></code></pre></td></tr></table> </div> </div><p>在 /etc/hosts 加上 worker node 对 wp.test 映射后即可使用 <code>http://wp.test</code> 访问服务</p> <h1 id="第二十四讲-persistentvolume">第二十四讲 PersistentVolume</h1> <p>PersistentVolume, 简称 PV 是存储的抽象,实际就是一些文件系统,如 GlusterFS、NFS、本地磁盘</p> <p>PersistentVolumeClaim, 简称 PVC,用来代表 Pod 向系统申请 PV,申请成功后会将 PV 和 PVC 绑定在一起</p> <p>StorageClass 抽象了特定的存储类型,在 PVC 和 PV 之间充当协调人的角色。</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/24_1.webp" alt="storage_class"></p> <p>PV 配置文件</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolume</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-10m-pv</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">host-test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">ReadWriteOnce</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">capacity</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">10Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">hostPath</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/host-10m-pv/</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>accessModes 定义了设备访问模式:</p> <ul> <li>ReadWriteOnce: 可读写,但只能被一个 Node 上的 Pod 挂载</li> <li>ReadOnlyMany: 仅可读,可以被多个 Node 上的 Pod 挂载</li> <li>ReadWriteMany: 可读写,可以被多个 Node 上的 Pod 挂载</li> </ul> <p>PVC 配置文件</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolumeClaim</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-5m-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">host-test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">ReadWriteOnce</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">5Mi</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>Pod 示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-pvc-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-pvc-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">persistentVolumeClaim</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">claimName</span><span class="p">:</span><span class="w"> </span><span class="l">host-5m-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-pvc-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">host-pvc-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/get_started_with_k8s/24_2.webp" alt="p2"></p> <h1 id="第二十五讲-pvnfs">第二十五讲 PV+NFS</h1> <h2 id="安装-nfs">安装 NFS</h2> <h3 id="服务端">服务端</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt -y install nfs-kernel-server </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">mkdir -p /tmp/nfs </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 添加配置</span> </span></span><span class="line"><span class="cl">sudo <span class="nb">echo</span> <span class="s1">&#39;/tmp/nfs 192.168.10.0/24(rw,sync,no_subtree_check,no_root_squash,insecure)&#39;</span> &gt;&gt; /etc/exports </span></span><span class="line"><span class="cl"><span class="c1"># 通知 nfs </span> </span></span><span class="line"><span class="cl">sudo exportfs -ra </span></span><span class="line"><span class="cl"><span class="c1"># 查看详情</span> </span></span><span class="line"><span class="cl">sudo exportfs -v </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 检查网络挂载</span> </span></span><span class="line"><span class="cl">showmount -e localhost </span></span></code></pre></td></tr></table> </div> </div><h3 id="客户端">客户端</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt -y install nfs-common </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 测试服务端挂载情况</span> </span></span><span class="line"><span class="cl">showmount -e 192.168.10.208 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">mkdir -p /tmp/test </span></span><span class="line"><span class="cl">sudo mount -t nfs 192.168.10.208:/tmp/nfs /tmp/test </span></span></code></pre></td></tr></table> </div> </div><h2 id="静态-pv">静态 PV</h2> <p>pv 配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolume</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-1g-pv</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">nfs</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">ReadWriteMany</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">capacity</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">1Gi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">nfs</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp/nfs/1g-pv</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="m">192.168.10.208</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>pvc 配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolumeClaim</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-static-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">nfs</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">ReadWriteMany</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">1Gi</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>测试 pod 配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-static-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-pvc-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">persistentVolumeClaim</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">claimName</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-static-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-pvc-test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-pvc-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>示意图</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/25_1.webp" alt="p1"></p> <h2 id="动态-pv">动态 PV</h2> <p>使用 Provisioner 来自动管理存储、创建 PV ,</p> <p>NFS Provisioner 是以 Pod 形式运行在 k8s,需要三个<a href="https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy">部署文件</a> rabc.yaml、class.yaml 和 deployment.yaml</p> <p>storageClass 配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">storage.k8s.io/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">StorageClass</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-client</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">provisioner</span><span class="p">:</span><span class="w"> </span><span class="l">k8s-sigs.io/nfs-subdir-external-provisioner </span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">parameters</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">archiveOnDelete</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;false&#34;</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>pvc 配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolumeClaim</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-dyn-10m-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-client</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">ReadWriteMany</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">10Mi</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>测试 pod 配置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-dyn-pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-dyn-10m-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">persistentVolumeClaim</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">claimName</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-dyn-10m-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-dyn-test</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-dyn-10m-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/tmp</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>由于有 NFS Provisioner, 不再需要直接定义 PV 对象,会自动按 PVC 配置创建。</p> <p>整体示意图</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/25_2.webp" alt="p2"></p> <h1 id="第二十六讲-statefulset">第二十六讲 StatefulSet</h1> <p>StatefulSets 和 Deployment 类似, 不过 pod 名称是固定的,pod 启动是有序的。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">StatefulSet</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">serviceName</span><span class="p">:</span><span class="w"> </span><span class="l">redis-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">redis:5-alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">6379</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>声明 svc</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">6379</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">6379</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>验证网络</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/26_1.webp" alt="p1"></p> <p>svc 和 sts 之间的关联</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/26_2.webp" alt="p2"></p> <p>sts 可以通过定义 <code>volumeClaimTemplates</code> 来声明 pvc</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">StatefulSet</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-pv-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">serviceName</span><span class="p">:</span><span class="w"> </span><span class="l">redis-pv-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumeClaimTemplates</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-100m-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">nfs-client</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">ReadWriteMany</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">100Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-pv-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">redis-pv-sts</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">redis:5-alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">6379</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">redis-100m-pvc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/data</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>三者关联图</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/26_3.webp" alt="p3"></p> <p>pvc 状态</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/26_4.webp" alt="p4"></p> <h1 id="第二十七讲-滚动更新">第二十七讲 滚动更新</h1> <h2 id="更新">更新</h2> <p>k8s 里面的应用的版本变化就是指 template 里的 Pod 变化,最终 使用 template 的 Hash 值作为 版本号</p> <p>apply 更新的 yaml 后,可以使用<code>kubectl rollout status</code>查看更新状态</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/27_1.webp" alt="p1"></p> <p>可以使用 <code>kubectl describe deployment ngx-dep</code> 查看具体的 pod 变化</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/27_2.webp" alt="p2"></p> <p>pod 变化过程示意图</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/27_3.webp" alt="p3"></p> <h2 id="历史及回滚">历史及回滚</h2> <p>可使用 <code>kubectl rollout history deployment ngx-dep</code> 查看更新历史, 默认保存最近十次记录</p> <p>可使用 <code>kubectl rollout history deployment ngx-dep --revision=2</code> 查看具体历史的详情</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/27_4.webp" alt="p4"></p> <p>可使用 <code>kubectl rollout undo deployment ngx-dep</code> 来回滚</p> <p>可使用 <code>kubectl rollout undo deployment ngx-dep --to-revision=3</code> 来回滚到任意版本</p> <p>回滚时 pod 变化过程示意图</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/27_5.webp" alt="p5"></p> <p>添加版本备注信息</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">annotations</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">kubernetes.io/change-cause</span><span class="p">:</span><span class="w"> </span><span class="l">v1, ngx=1.21</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">...</span><span class="w"> </span><span class="l">...</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十八讲-如何监控-pod-健康状态">第二十八讲 如何监控 Pod 健康状态</h1> <h2 id="资源配额">资源配额</h2> <p>通过在 容器描述部分添加 resources 字段来声明资源</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-pod-resources</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">10m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">100Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">limits</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">20m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">200Mi</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p><code>requests</code> 表示容器需要申请的资源,<code>limits</code> 表示资源使用上限,超过则可能被强制停止。</p> <p>cpu 的单位<code>m</code>代表千分之一。</p> <p>如果不加资源声明,k8s 可以将 pod 调度到任意节点上,否则 k8s 会根据每个 Pod 声明的需求,像搭积木或者玩俄罗斯方块一样,把节点尽量塞满。</p> <h2 id="检查探针">检查探针</h2> <p>k8s 定义了三种探针</p> <ul> <li>Startup: 启动探针,用来检查应用是否启动成功</li> <li>Liveness: 存活探针,用来检查应用是否正常运行</li> <li>Readiness: 就绪探针,用来检查应用是否可以接受流量</li> </ul> <p>三种探针是递进关系,Startup 成功后,才会执行后面两个探针</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/28_1.webp" alt="p1"></p> <p>如果 Liveness 探针失败, k8s 会重启容器,Readiness 探针失败,会将容器从 Service 负载均衡集合中移除,不分配流量。</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/28_2.webp" alt="p2"></p> <p>k8s 支持使用 shell、tcp、http 三种方式检测</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-pod-probe</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configMap</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/nginx/conf.d</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-conf-vol</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">startupProbe</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">exec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;cat&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;/var/run/nginx.pid&#34;</span><span class="p">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">livenessProbe</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">tcpSocket</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">readinessProbe</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">httpGet</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/ready</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十九讲-命名空间">第二十九讲 命名空间</h1> <p>命名空间是一个逻辑上的概念,它可以将集群切分成一个个彼此独立的区域</p> <p>创建命名空间</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl create ns dev-ns </span></span></code></pre></td></tr></table> </div> </div><p>有了命名空间,就可以给命名空间设定资源配额,示例如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ResourceQuota</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dev-qt</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">dev-ns</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">hard</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests.cpu</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests.memory</span><span class="p">:</span><span class="w"> </span><span class="l">10Gi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">limits.cpu</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">limits.memory</span><span class="p">:</span><span class="w"> </span><span class="l">20Gi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests.storage</span><span class="p">:</span><span class="w"> </span><span class="l">100Gi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">persistentvolumeclaims</span><span class="p">:</span><span class="w"> </span><span class="m">100</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">pods</span><span class="p">:</span><span class="w"> </span><span class="m">100</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">configmaps</span><span class="p">:</span><span class="w"> </span><span class="m">100</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">secrets</span><span class="p">:</span><span class="w"> </span><span class="m">100</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">services</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">count/jobs.batch</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">count/cronjobs.batch</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">count/deployments.apps</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>设定了资源配额之后,所有 Pod 都需要显式声明资源需求,但这样 的严格要求有时不太方便,我们可以声明一个默认资源配额</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">LimitRange</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dev-limits</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">dev-ns</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">limits</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Container</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">defaultRequest</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">200m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">50Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">default</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">500m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">100Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Pod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">max</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">800m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">200Mi</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><h1 id="第三十讲-如何使用-metrics-server-和-prometheus">第三十讲 如何使用 Metrics Server 和 Prometheus</h1> <h2 id="metrics-server">Metrics Server</h2> <p>metrics server 是一个专门来收集核心资源指标的工具,定时从所有节点的 kubelet 里采集信息。</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/30_1.webp" alt="p1"></p> <p>安装</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 下载配置yaml</span> </span></span><span class="line"><span class="cl">wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 修改配置 </span> </span></span><span class="line"><span class="cl"><span class="c1"># deployment.spec.template.spec.containers 对象 添加 &#39;- --kubelet-insecure-tls&#39;</span> </span></span><span class="line"><span class="cl"><span class="c1"># deployment.spec.template.spec 对象 添加 &#39;nodeName: k8s-master #你自己的节点名称&#39;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 重命名镜像</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nv">repo</span><span class="o">=</span>registry.aliyuncs.com/google_containers </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nv">name</span><span class="o">=</span>k8s.gcr.io/metrics-server/metrics-server:v0.6.1 </span></span><span class="line"><span class="cl"><span class="nv">src_name</span><span class="o">=</span>metrics-server:v0.6.1 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">docker pull <span class="nv">$repo</span>/<span class="nv">$src_name</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">docker tag <span class="nv">$repo</span>/<span class="nv">$src_name</span> <span class="nv">$name</span> </span></span><span class="line"><span class="cl">docker rmi <span class="nv">$repo</span>/<span class="nv">$src_name</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 应用</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">kubectl apply -f components.yaml </span></span></code></pre></td></tr></table> </div> </div><p>查看 <img src="https://ppd0705.github.io/image/get_started_with_k8s/30_2.webp" alt="p2"></p> <h2 id="horizontalpodautoscaler">HorizontalPodAutoscaler</h2> <p>HorizontalPodAutoscaler 是 用来 自动伸缩 Pod 数量的对象,简称 hpa.</p> <p>hpa 从 Metrics Server 获取当前应用的指标,按照策略增减 Pod 数量</p> <p>使用 nginx deploy 示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">template</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">containers</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nginx:alpine</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">requests</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">50m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">10Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">limits</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">100m</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">20Mi</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa-svc</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa-dep</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>创建 hpa 模版</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">out</span><span class="o">=</span><span class="s2">&#34;--dry-run=client -o yaml&#34;</span> <span class="c1"># 定义Shell变量</span> </span></span><span class="line"><span class="cl">kubectl autoscale deploy ngx-hpa-dep --min<span class="o">=</span><span class="m">2</span> --max<span class="o">=</span><span class="m">10</span> --cpu-percent<span class="o">=</span><span class="m">5</span> <span class="nv">$out</span> </span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">autoscaling/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">HorizontalPodAutoscaler</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">maxReplicas</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">minReplicas</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">scaleTargetRef</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ngx-hpa-dep</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">targetCPUUtilizationPercentage</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><h2 id="prometheus">Prometheus</h2> <p>使用 <code>kube-prometheus</code> 部署比较方便</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 下载部署文件</span> </span></span><span class="line"><span class="cl">wget https://github.com/prometheus-operator/kube-prometheus/archive/refs/tags/v0.11.0.tar.gz </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 修改配置</span> </span></span><span class="line"><span class="cl"><span class="c1">## 替换gcr 镜像 kubeStateMetrics-deployment.yaml、prometheusAdapter-deployment.yaml</span> </span></span><span class="line"><span class="cl">image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.5.0 </span></span><span class="line"><span class="cl">image: k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">image: chronolaw/kube-state-metrics:v2.5.0 </span></span><span class="line"><span class="cl">image: willdockerhub/prometheus-adapter:v0.9.1 </span></span><span class="line"><span class="cl"><span class="c1">## 添加NodePort IP ,方便本机测试 prometheus-service.yaml、grafana-service.yaml</span> </span></span><span class="line"><span class="cl">spec.type: NodePort </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 创建服务</span> </span></span><span class="line"><span class="cl">kubectl create -f manifests/setup </span></span><span class="line"><span class="cl">kubectl create -f manifests </span></span></code></pre></td></tr></table> </div> </div><p>使用 <code>kubectl get svc -n monitoring</code> 获取 prometheus-k8s 服务 9090对应的端口、grafana 3000 对应的端口, 浏览器访问验证</p> <h1 id="第三十一讲-网络通信">第三十一讲 网络通信</h1> <h2 id="cni">CNI</h2> <p>为了适应跨主机通信的需求,k8s 提出了自己的网络模型 <code>IP-per-pod</code>:</p> <ul> <li>每个 pod 有唯一的 IP 地址</li> <li>pod 里所有容器共享这个 IP 地址</li> <li>集群内所有 pod 共享一个网段</li> <li>pod 可以直接基于 IP 访问另外一个 pod</li> </ul> <p>k8s 定义了一个实现上述网络模型的标准 CNI(container networking interface), 根据实现原理, CNI 插件大致分为三类:</p> <ul> <li>Overlay: 构建了一个工作在真实底层网络之上的&quot;逻辑网络&quot;,把原始的 Pod 网络数据封包,再通过下层网络发送出去,到了目的地再拆包。特点是适应性强,但性能较低。</li> <li>Route:使用系统内置的路由功能来实现 Pod 跨主机通信。特点是性能较高,但对底层网络依赖性强。</li> <li>Underlay:直接使用底层网络来实现CNI,Pod 和主机在一个网络里。 特点是性能最高,但对底层硬件和网络依赖性最强。</li> </ul> <p>插件介绍</p> <ul> <li>Flannel:早期使用 UDP 和 VXLAN 实现了 Overlay 模式,后来使用 Host-Gateway 支持了 Route 模式</li> <li>Calico:Route 模式,使用 BGP 协议来维护路由信息,支持多种网络策略,具备数据加密、安全隔离、流量整形等功能</li> <li>Cilium:同时支持 Overlay 和 Route 模式,深度使用了 eBPF 技术,在内核层次操作网络数据,性能很高。</li> </ul> <h2 id="flannel-工作方式分析">Flannel 工作方式分析</h2> <p>创建 3 个 nginx pod</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">kubectl create deploy ngx-dep --image<span class="o">=</span>nginx:alpine --replicas<span class="o">=</span><span class="m">3</span> </span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/get_started_with_k8s/31_1.webp" alt="p1"></p> <p>默认使用基于 VXLAN 的 Overlay 模式,从单机的角度来看,Flannel 的网络结构几乎和 Docker 一模一样,只不过网桥从 docker0 换成 了 cni0</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/31_2.webp" alt="p2"></p> <p>本机网络连接示意图如下</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/31_3.webp" alt="p3"></p> <p><code>route</code> 查看路由表</p> <ul> <li>10.10.0.0/24 网段的数据,都要走 cni0 设备,也就是“cni0”网桥。</li> <li>10.10.1.0/24 网段的数据,都要走 flannel.1 设备,也就是 Flannel。</li> <li>192.168.10.0/24 网段的数据,都要走 ens160 设备,也就是我们宿主机的网卡</li> </ul> <p>假设我们要从 master 节点的“10.10.0.3”访问 worker 节点的“10.10.1.77”,因为 master 节点的“cni0”网桥管理的只是“10.10.0.0/24”这个网段, 所以按照路由表,凡是“10.10.1.0/24”都要让 flannel.1 来处理, 这样就进入了 Flannel 插件的工作流程。</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/31_4.webp" alt="p4"></p> <p>跨主机网络示意图如下</p> <p><img src="https://ppd0705.github.io/image/get_started_with_k8s/31_5.webp" alt="p5"></p> <h1 id="链接">链接</h1> <ul> <li><a href="https://time.geekbang.org/column/intro/100114501?tab=catalog">极客时间课程</a></li> <li><a href="https://github.com/chronolaw/k8s_study/">课程示例代码仓库</a></li> </ul> [笔记]Kafka 核心技术与实战 https://ppd0705.github.io/post/kafka_in_action/ Wed, 29 Jun 2022 08:20:51 +0800 https://ppd0705.github.io/post/kafka_in_action/ <h1 id="第一讲-消息系统-abc">第一讲 消息系统 ABC</h1> <h2 id="定义">定义</h2> <p>维基百科版: 消息系统是一组规范,利用这组规范在不同系统之间传递语义准确的消息,实现松耦合的异步数据传递。</p> <h2 id="传输模型">传输模型</h2> <ul> <li>点对点</li> <li>发布订阅</li> </ul> <h2 id="核心作用">核心作用</h2> <ul> <li>削峰填谷</li> </ul> <h1 id="第二讲-kafka-术语">第二讲 kafka 术语</h1> <ul> <li>Record: 消息</li> <li>Topic: 主题</li> <li>Partition: 分区,有序不变,每个主题至少有一个分区,从 0 开始</li> <li>Offset: 分区位移,表示分区中消息的位置信息,单调递增且不变</li> <li>Replica: 副本,每个分区可有多个副本,仅 Leader 副本对外提供服务,Follower 副本仅做数据备份</li> <li>Producer: 生产者,发布信息</li> <li>Consumer: 消息者,消费消息</li> <li>Consumer Offset: 消费者位移,表示消费者消费进度</li> <li>Consumer Group: 多个消费者组成一个组,共同消费一个主题,实现高吞吐</li> <li>Rebalance: 重平衡,消费组某个成员挂掉后,其他消费者自动重新分配订阅主题的过程</li> </ul> <p>示意图如下:</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/2_1.webp" alt="term"></p> <p>问:kafka 为什么不像 mysql 那样允许 follower 对外提供服务?</p> <ul> <li>kafka 使用不是典型的读多写少的场景</li> <li>分区 Leader 分布在不同的 Broker 节点, 已提供了负载均衡的功能</li> </ul> <h1 id="第四讲-kafka-发现版本">第四讲 kafka 发现版本</h1> <h2 id="apache-kafka">Apache Kafka</h2> <p>社区版, 迭代速度快,社区响应度高</p> <h2 id="confluent-kafka">Confluent Kafka</h2> <p>kafka创始人创建的商业公司Confluent, 为 kafka 提供了很多高级特性, 如跨数据中心备份、集群监控</p> <h2 id="cdhhdp-kafka">CDH/HDP Kafka</h2> <p>大数据云公司提供版本,集成在数据平台中</p> <h1 id="第六讲-线上集群部署">第六讲 线上集群部署</h1> <h2 id="操作系统">操作系统</h2> <p>kafka 客户端 在 Linux 上使用 epoll I/O 模型, 在 Windows 上使用 select</p> <h2 id="磁盘">磁盘</h2> <p>kafka 大多数场景 是顺序读写,使用机械磁盘可以胜任线上坏境</p> <h2 id="磁盘容量">磁盘容量</h2> <p>容量需要考虑如下因素</p> <ul> <li>新增消息数</li> <li>平均消息大小</li> <li>消息留存时间</li> <li>备份数</li> <li>是否启用压缩</li> </ul> <h2 id="带宽">带宽</h2> <p>根据使用经验,超过 70% 带宽阈值就有网络丢包的可能性,</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/6_1.webp" alt="summary"></p> <h1 id="第七讲-最最最重要集群参数上">第七讲 最最最重要集群参数(上)</h1> <h2 id="broker-参数">Broker 参数</h2> <h3 id="存储相关">存储相关</h3> <ul> <li>log.dirs: 指定目录 如 /home/kafka1,/home/kafka2, 多个目录可以实现故障转移Failover</li> </ul> <h3 id="zookeeper-相关">zookeeper 相关</h3> <ul> <li>zookeeper.connect: zk1:2181,zk2:2181,zk3:2181/kafka1</li> </ul> <h3 id="topic-管理相关">topic 管理相关</h3> <ul> <li>auto.create.topics.enable: 是否允许自动创建Topic</li> <li>unclean.leader.election.enable: 是否运行 Unclean Leader 选举</li> <li>auto.leader.rebalance.enable: 是否允许定期举行 Leader 选举</li> </ul> <h3 id="数据留存相关">数据留存相关</h3> <ul> <li>log.retention.{hours|minutes|ms}: 控制数据保留时长,默认hours=168</li> <li>log.retention.bytes: 消息保存总容量大小,默认不限制</li> <li>log.segment.bytes: 日志文件区块大小,默认 1GB (一个 partition 有 多个 segment 文件组成,不会删除最新的一个segment)</li> <li>segment.ms: 日志文件毫秒周期,默认 7 天</li> <li>message.max.bytes: 最大接受消息大小,默认1000012,976KB</li> </ul> <h1 id="第八讲-最最最重要集群参数下">第八讲 最最最重要集群参数(下)</h1> <h2 id="topic-级别参数">Topic 级别参数</h2> <ul> <li>retention.ms: 消息保存时长,优先于Broker级别设置</li> <li>retention.bytes: 消息保存大小,优先于与Broker级别设置</li> </ul> <h2 id="jvm-参数">JVM 参数</h2> <ul> <li>KAFKA_HEAP_OPTS: 堆大小, 建议设成 6G</li> <li>KAFKA_JVM_PERFORMANCE_OPTS: GC参数</li> </ul> <h2 id="操作系统参数">操作系统参数</h2> <ul> <li>文件描述符限制: 使用 ulimit -n 1000000 改成一个较大值</li> <li>文件系统类型:XFS 性能强于 ext4</li> <li>Flush 落盘时间: 默认是 5s,可适当增大来降低物理磁盘的写操作</li> </ul> <h1 id="第九讲--消息分区机制原理">第九讲 消息分区机制原理</h1> <h2 id="分区作用">分区作用</h2> <p><img src="https://ppd0705.github.io/image/kafka_in_action/9_1.webp" alt="topic_anatomy"> 数据的读写操作是针对分区这个粒度进行的</p> <p>分区的作用就是提供负载均衡的能力,实现系统的高伸缩性</p> <h2 id="分区写入策略">分区写入策略</h2> <h3 id="轮询策略">轮询策略</h3> <p><img src="https://ppd0705.github.io/image/kafka_in_action/9_2.webp" alt="round_robin"></p> <p>默认策略,将消息平均分配到所有分区</p> <h3 id="随机策略">随机策略</h3> <p><img src="https://ppd0705.github.io/image/kafka_in_action/9_3.webp" alt="random"></p> <p>如果追求数据均匀分布, 优先使用轮询策略</p> <h3 id="按消息键分配策略">按消息键分配策略</h3> <p><img src="https://ppd0705.github.io/image/kafka_in_action/9_4.webp" alt="hash_key_ordering"></p> <p>键相同的消息会写入到同一个分区</p> <h3 id="显式指定分区">显式指定分区</h3> <p>kafka-python 可以支持</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">producer</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;some_message_bytes&#39;</span><span class="p">,</span> <span class="n">partiion</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><p>kafka-go 需要实现对应接口达到类似效果</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">CustomBalancer</span> <span class="kd">struct</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">kfk</span><span class="p">.</span><span class="nx">RoundRobin</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">//nolint </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">h</span> <span class="o">*</span><span class="nx">CustomBalancer</span><span class="p">)</span> <span class="nf">Balance</span><span class="p">(</span><span class="nx">msg</span> <span class="nx">kfk</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="nx">partitions</span> <span class="o">...</span><span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Partition</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">h</span><span class="p">.</span><span class="nx">RoundRobin</span><span class="p">.</span><span class="nf">Balance</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="nx">partitions</span><span class="o">...</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">Partition</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>消费者可以显式指定消费分区, 消费者组通过自平衡分配分区</p> <h1 id="第十讲-压缩算法">第十讲 压缩算法</h1> <h2 id="流程">流程</h2> <p>Producer 端压缩、Broker 端 保持、Consumer 端解压缩</p> <h2 id="压缩算法对比">压缩算法对比</h2> <p><img src="https://ppd0705.github.io/image/kafka_in_action/10_1.webp" alt="compression"></p> <p>依据情况选择压缩比和吞吐率合适的算法</p> <h1 id="第十一讲-无消息丢失配置">第十一讲 无消息丢失配置</h1> <p>kafka 只对已提交的消息做有限度的持久化保证</p> <h2 id="producer端">producer端</h2> <ol> <li>使用带回调的方法: send(msg, callback),</li> <li>acks=-1 所有副本收到消息才算已提交</li> <li>retries 设置一个较大值,在网络抖动失败时会重试消息发送 (重试可能导致消息乱序,max.in.flight.requests.per.connection=1可以避免)</li> </ol> <h2 id="broker-端">broker 端</h2> <ol> <li>unclean.leader.election.enable = false 不让落后的broker 参与Leader选举</li> <li>replication.factor &gt;= 3 消息多保持几份</li> <li>min.insync.replicas &gt; 1 控制消息至少写入到多少分布才算已提交 (只在 acks=-1 是生效)</li> </ol> <h2 id="consumer-端">consumer 端</h2> <ol> <li>enable.auto.commit = false 手动提交维护</li> </ol> <h1 id="第十二讲-拦截器">第十二讲 拦截器</h1> <p>生产者可以在发送消息前和提交成功后植入拦截器</p> <p>消费者可以在消费消息前以及提交位移后植入拦截器</p> <h1 id="第十四讲-幂等和事务">第十四讲 幂等和事务</h1> <p>kafka 默认提供的可交付保障是至少一次 at lease once; 通过禁止重试可以实现至多一次 at most once</p> <h2 id="幂等性-producer">幂等性 Producer</h2> <p>通过设置 enable.idempotence = true 可以开启</p> <p>开启以后,单分区单会话能够保证不会出现重复消息</p> <p>背后的实现逻辑是 Broker 多保存数据,当 Producer 发送了相同字段的消息后,Broker 能够知晓消息重复了,并将其丢弃掉</p> <h2 id="事务型-producer">事务型 Producer</h2> <p>设置 enable.idempotence = true, 以及 transaction.id</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">producer</span><span class="o">.</span><span class="na">initTransactions</span><span class="o">();</span> </span></span><span class="line"><span class="cl"><span class="k">try</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">producer</span><span class="o">.</span><span class="na">beginTransaction</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="n">producer</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">record1</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">producer</span><span class="o">.</span><span class="na">send</span><span class="o">(</span><span class="n">record2</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">producer</span><span class="o">.</span><span class="na">commitTransaction</span><span class="o">();</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">KafkaException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">producer</span><span class="o">.</span><span class="na">abortTransaction</span><span class="o">();</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>上面的代码能保证 Record1 和 Record2 要么全部成功,要么全部失败</p> <p>事务可以保证跨分区、跨会话的幂等性</p> <p>消费者端设置隔离级别 isolation.level = read_committed 后会只读取成功完成的事务消息</p> <h1 id="第十五讲-消费者组">第十五讲 消费者组</h1> <h2 id="特性">特性</h2> <ol> <li>consumer group 可以有一个或多个 consumer</li> <li>group id 是 consumer group 的唯一标识</li> <li>单个分区只会分配到一个 consumer</li> </ol> <h2 id="实例数量设置">实例数量设置</h2> <p>假设 一个 group 订阅了 3 个 topic, 每个 topic 有 2 个 partition, 那么设置 6 个实例能最大限度的利用kafka的高伸缩性,每个实例消费一个分区</p> <h2 id="rebalance-触发条件">rebalance 触发条件</h2> <ol> <li>成员数量发生变化,增加或减少</li> <li>订阅主题数量发生变更</li> <li>主题分区数量变更</li> </ol> <h1 id="第十六讲-位移主题">第十六讲 位移主题</h1> <p>__consumer_offsets 主要用来记录消费者的位移数据</p> <p>当第一个 consumer 启动时,kafka 会自动创建位移主题,默认分区数 offsets.topic.num.partitions = 50 ,副本数 offsets.topic.replication.factor = 3</p> <p>消费端位移提交分为手动和自动提交, 手动提交提供了灵活性和可控性, 而自动提交会定期地提交位移,即使没有新消息,这样会导致位移主题消息会越来越多,所以位移主题采用 log compaction 删除策略来删除重复消息</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/16_1.webp" alt="compaction"></p> <h1 id="第十七讲-消费者组重平衡">第十七讲 消费者组重平衡</h1> <p>重平衡就是消费者组中所有消费者就如何订阅主题分区达成共识的过程。 重平衡过程中,所有消费者不消费任何消息,在协调者 coordinator 的帮助下,完成订阅主题分区的分配。</p> <p>broker 在启动时,会启动相应的 coordinator 组件,消费者组通过位移主题 __consumer_offsets 查找对应 coordinator 所在的 broker</p> <p>消费者组所在 consumer_offsets 分区通过 <code>abs(hash(group_id) % topic_partition_count)</code> 计算可以得到</p> <p>如何避免消费者被协调者错误移除?</p> <ul> <li>session.timeout.ms: 心跳超时时间, 建议设置成 6s</li> <li>heartbeat.interval.ms: 心跳间隔,建议设置成 2s (即判定超时前能发送3轮心跳)</li> <li>max.pool.interval.ms: 最大 poll 间隔, 根据业务消费时长设定,应大于消费时长</li> <li>GC 参数: 频繁 GC 可能导致消费者停顿</li> </ul> <h1 id="第-18-讲-位移提交">第 18 讲 位移提交</h1> <h2 id="自动提交">自动提交</h2> <p>consumer 后台线程定时提交位移,相关参数</p> <ul> <li>enable.auto.commit = true (默认开启)</li> <li>auto.commit.interval.ms = 5000 (最小提交间隔,默认5s)</li> </ul> <p>假设消费者每 5s 提交一次位移,在第 3s 时发生重平衡,那么前3s的数据会再重复消费一次</p> <h1 id="手动提交">手动提交</h1> <p>显式通过调用 <code>cosummer.commitSync()</code> 提交位移</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ConsumerRecords</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">records</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">consumer</span><span class="o">.</span><span class="na">poll</span><span class="o">(</span><span class="n">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span> </span></span><span class="line"><span class="cl"> <span class="n">process</span><span class="o">(</span><span class="n">records</span><span class="o">);</span> <span class="c1">// 处理消息 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">try</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">consumer</span><span class="o">.</span><span class="na">commitSync</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">CommitFailedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">handle</span><span class="o">(</span><span class="n">e</span><span class="o">);</span> <span class="c1">// 处理提交失败异常 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">}</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>commitSync 是同步调用, 会阻塞线程,可以使用 commitAsync</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ConsumerRecords</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">records</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">consumer</span><span class="o">.</span><span class="na">poll</span><span class="o">(</span><span class="n">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span> </span></span><span class="line"><span class="cl"> <span class="n">process</span><span class="o">(</span><span class="n">records</span><span class="o">);</span> <span class="c1">// 处理消息 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">consumer</span><span class="o">.</span><span class="na">commitAsync</span><span class="o">((</span><span class="n">offsets</span><span class="o">,</span> <span class="n">exception</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">exception</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">handle</span><span class="o">(</span><span class="n">exception</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="o">});</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>可以将两者结合,利用 commitSync 的重试规避瞬时错误,利用 commitAsync 来减少阻塞</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">try</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ConsumerRecords</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">records</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">consumer</span><span class="o">.</span><span class="na">poll</span><span class="o">(</span><span class="n">Duration</span><span class="o">.</span><span class="na">ofSeconds</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span> </span></span><span class="line"><span class="cl"> <span class="n">process</span><span class="o">(</span><span class="n">records</span><span class="o">);</span> <span class="c1">// 处理消息 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">commitAysnc</span><span class="o">();</span> <span class="c1">// 使用异步提交规避阻塞 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">}</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">handle</span><span class="o">(</span><span class="n">e</span><span class="o">);</span> <span class="c1">// 处理异常 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">consumer</span><span class="o">.</span><span class="na">commitSync</span><span class="o">();</span> <span class="c1">// 最后一次提交使用同步阻塞式提交 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">consumer</span><span class="o">.</span><span class="na">close</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十一讲-java-版消费者-tcp-连接管理">第二十一讲 Java 版消费者 TCP 连接管理</h1> <p>TCP 连接是在调用 KafkaConsumer.poll 方法被创建的。 poll 内部有三个时机可以创建 TCP 连接</p> <h2 id="创建连接时机">创建连接时机</h2> <ol> <li>发起 FindCoordinator 请求时</li> </ol> <p>消费者启动时会向负载最小的 Broker 建立连接,请求告知主题对应的 coordinator Leader 在哪个 broker</p> <ol start="2"> <li>连接协调者</li> </ol> <p>步骤 1 之后会和 coordinator 建立连接</p> <ol start="3"> <li>消费数据时</li> </ol> <p>消费者会向每个分区 Leader 所在的 Broker 建立连接。</p> <p>假设消费的 5 个分区在 4 个 Broker 上,那会建立 4 个连接。</p> <h2 id="案例分析">案例分析</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-zed" data-lang="zed"><span class="line"><span class="cl"><span class="err">#</span><span class="w"> </span><span class="err">建立第一个连接,连接的</span><span class="n">Broker</span><span class="err">的</span><span class="w"> </span><span class="n">ID</span><span class="w"> </span><span class="err">是</span><span class="w"> </span><span class="o">-</span><span class="err">1,</span><span class="w"> </span><span class="err">表明还不知道其任何信息</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">142</span><span class="p">]</span><span class="w"> </span><span class="n">DEBUG</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Initiating</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="n">localhost</span><span class="o">:</span><span class="err">9092</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="err">1</span><span class="w"> </span><span class="n">rack</span><span class="o">:</span><span class="w"> </span><span class="n">null</span><span class="p">)</span><span class="w"> </span><span class="n">using</span><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nn">localhost/</span><span class="err">127</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">1</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">944</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="err">#</span><span class="w"> </span><span class="err">向第一个连接请求集群元数据信息</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">188</span><span class="p">]</span><span class="w"> </span><span class="n">DEBUG</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Sending</span><span class="w"> </span><span class="n">metadata</span><span class="w"> </span><span class="n">request</span><span class="w"> </span><span class="n">MetadataRequestData</span><span class="p">(</span><span class="n">topics</span><span class="o">=</span><span class="p">[</span><span class="n">MetadataRequestTopic</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="err">&#39;</span><span class="n">t4</span><span class="err">&#39;</span><span class="p">)],</span><span class="w"> </span><span class="n">allowAutoTopicCreation</span><span class="o">=</span><span class="n">true</span><span class="p">,</span><span class="w"> </span><span class="n">includeClusterAuthorizedOperations</span><span class="o">=</span><span class="n">false</span><span class="p">,</span><span class="w"> </span><span class="n">includeTopicAuthorizedOperations</span><span class="o">=</span><span class="n">false</span><span class="p">)</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="n">localhost</span><span class="o">:</span><span class="err">9092</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="err">1</span><span class="w"> </span><span class="n">rack</span><span class="o">:</span><span class="w"> </span><span class="n">null</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">1097</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="err">#</span><span class="w"> </span><span class="err">向第一个连接发送</span><span class="w"> </span><span class="n">FindCoordinator</span><span class="w"> </span><span class="err">请求</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">188</span><span class="p">]</span><span class="w"> </span><span class="n">TRACE</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Sending</span><span class="w"> </span><span class="n">FIND_COORDINATOR</span><span class="w"> </span><span class="p">{</span><span class="n">key</span><span class="o">=</span><span class="n">test</span><span class="p">,</span><span class="n">key_type</span><span class="o">=</span><span class="err">0</span><span class="p">}</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="n">correlation</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="err">0</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="o">-</span><span class="err">1</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">496</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="err">#</span><span class="w"> </span><span class="err">收到</span><span class="w"> </span><span class="n">Coordinator</span><span class="w"> </span><span class="err">所在</span><span class="w"> </span><span class="n">Broker</span><span class="w"> </span><span class="err">信息</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">203</span><span class="p">]</span><span class="w"> </span><span class="n">TRACE</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Completed</span><span class="w"> </span><span class="n">receive</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="o">-</span><span class="err">1</span><span class="w"> </span><span class="n">for</span><span class="w"> </span><span class="n">FIND_COORDINATOR</span><span class="w"> </span><span class="n">with</span><span class="w"> </span><span class="n">correlation</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="err">0</span><span class="p">,</span><span class="w"> </span><span class="n">received</span><span class="w"> </span><span class="p">{</span><span class="n">throttle_time_ms</span><span class="o">=</span><span class="err">0</span><span class="p">,</span><span class="n">error_code</span><span class="o">=</span><span class="err">0</span><span class="p">,</span><span class="n">error_message</span><span class="o">=</span><span class="n">null</span><span class="p">,</span><span class="w"> </span><span class="n">node_id</span><span class="o">=</span><span class="err">2</span><span class="p">,</span><span class="n">host</span><span class="o">=</span><span class="n">localhost</span><span class="p">,</span><span class="n">port</span><span class="o">=</span><span class="err">9094</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">837</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="err">#</span><span class="w"> </span><span class="err">和</span><span class="w"> </span><span class="n">Coordinator</span><span class="w"> </span><span class="err">建立第二个连接</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">204</span><span class="p">]</span><span class="w"> </span><span class="n">DEBUG</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Initiating</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="n">localhost</span><span class="o">:</span><span class="err">9094</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="o">:</span><span class="w"> </span><span class="err">2147483645</span><span class="w"> </span><span class="n">rack</span><span class="o">:</span><span class="w"> </span><span class="n">null</span><span class="p">)</span><span class="w"> </span><span class="n">using</span><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nn">localhost/</span><span class="err">127</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">1</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">944</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="err">#</span><span class="w"> </span><span class="err">建立第三到五个连接,用于实际消息的获取,说明这三个</span><span class="w"> </span><span class="n">Broker</span><span class="w"> </span><span class="err">都有要消费的分区</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">237</span><span class="p">]</span><span class="w"> </span><span class="n">DEBUG</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Initiating</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="n">localhost</span><span class="o">:</span><span class="err">9094</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="o">:</span><span class="w"> </span><span class="err">2</span><span class="w"> </span><span class="n">rack</span><span class="o">:</span><span class="w"> </span><span class="n">null</span><span class="p">)</span><span class="w"> </span><span class="n">using</span><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nn">localhost/</span><span class="err">127</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">1</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">944</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">237</span><span class="p">]</span><span class="w"> </span><span class="n">DEBUG</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Initiating</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="n">localhost</span><span class="o">:</span><span class="err">9092</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="o">:</span><span class="w"> </span><span class="err">0</span><span class="w"> </span><span class="n">rack</span><span class="o">:</span><span class="w"> </span><span class="n">null</span><span class="p">)</span><span class="w"> </span><span class="n">using</span><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nn">localhost/</span><span class="err">127</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">1</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">944</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">[</span><span class="err">2019</span><span class="o">-</span><span class="err">05</span><span class="o">-</span><span class="err">27</span><span class="w"> </span><span class="err">10</span><span class="o">:</span><span class="err">00</span><span class="o">:</span><span class="err">54</span><span class="p">,</span><span class="err">238</span><span class="p">]</span><span class="w"> </span><span class="n">DEBUG</span><span class="w"> </span><span class="p">[</span><span class="n">Consumer</span><span class="w"> </span><span class="n">clientId</span><span class="o">=</span><span class="n">consumer</span><span class="o">-</span><span class="err">1</span><span class="p">,</span><span class="w"> </span><span class="n">groupId</span><span class="o">=</span><span class="n">test</span><span class="p">]</span><span class="w"> </span><span class="n">Initiating</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">node</span><span class="w"> </span><span class="n">localhost</span><span class="o">:</span><span class="err">9093</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="o">:</span><span class="w"> </span><span class="err">1</span><span class="w"> </span><span class="n">rack</span><span class="o">:</span><span class="w"> </span><span class="n">null</span><span class="p">)</span><span class="w"> </span><span class="n">using</span><span class="w"> </span><span class="n">address</span><span class="w"> </span><span class="nn">localhost/</span><span class="err">127</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">0</span><span class="p">.</span><span class="err">1</span><span class="w"> </span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="n">apache</span><span class="p">.</span><span class="n">kafka</span><span class="p">.</span><span class="n">clients</span><span class="p">.</span><span class="n">NetworkClient</span><span class="o">:</span><span class="err">944</span><span class="p">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>总结上面的日志信息,可以看出消费者通常会创建三类连接:</p> <ol> <li>确认协调者和获取集群元数据</li> <li>连接协调者,令其执行组成员管理操作</li> <li>执行实际消息的获取</li> </ol> <p>当第三类连接建立后,第一类连接会被关闭,定期请求元数据的需求切换到第三类连接上。</p> <p>问: 第三类连接是主题级别还是 Broker 级别 ?</p> <h1 id="第二十二讲-消费者组消费进度监控">第二十二讲 消费者组消费进度监控</h1> <p>消息滞后使用 Consumer Lag 衡量,假设当前生成了 20 万条消息,消费了 15 万条,那么 Consumer Lag 就等于 5 万。</p> <h2 id="使用命令行工具">使用命令行工具</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ bin/kafka-consumer-groups.sh --bootstrap-server &lt;Kafka broker连接信息&gt; --describe --group &lt;group名称&gt; </span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/kafka_in_action/22_1.webp" alt="consumer_group_lag"></p> <h2 id="使用-java-consumer-api">使用 Java Consumer API</h2> <p>使用 AdminClient 查询</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">TopicPartition</span><span class="o">,</span> <span class="n">Long</span><span class="o">&gt;</span> <span class="nf">lagOf</span><span class="o">(</span><span class="n">String</span> <span class="n">groupID</span><span class="o">,</span> <span class="n">String</span> <span class="n">bootstrapServers</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">TimeoutException</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Properties</span> <span class="n">props</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Properties</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="n">props</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">CommonClientConfigs</span><span class="o">.</span><span class="na">BOOTSTRAP_SERVERS_CONFIG</span><span class="o">,</span> <span class="n">bootstrapServers</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="o">(</span><span class="n">AdminClient</span> <span class="n">client</span> <span class="o">=</span> <span class="n">AdminClient</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">props</span><span class="o">))</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ListConsumerGroupOffsetsResult</span> <span class="n">result</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">listConsumerGroupOffsets</span><span class="o">(</span><span class="n">groupID</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Map</span><span class="o">&lt;</span><span class="n">TopicPartition</span><span class="o">,</span> <span class="n">OffsetAndMetadata</span><span class="o">&gt;</span> <span class="n">consumedOffsets</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="na">partitionsToOffsetAndMetadata</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">props</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConsumerConfig</span><span class="o">.</span><span class="na">ENABLE_AUTO_COMMIT_CONFIG</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span> <span class="c1">// 禁止自动提交位移 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">props</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConsumerConfig</span><span class="o">.</span><span class="na">GROUP_ID_CONFIG</span><span class="o">,</span> <span class="n">groupID</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">props</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConsumerConfig</span><span class="o">.</span><span class="na">KEY_DESERIALIZER_CLASS_CONFIG</span><span class="o">,</span> <span class="n">StringDeserializer</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span> </span></span><span class="line"><span class="cl"> <span class="n">props</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">ConsumerConfig</span><span class="o">.</span><span class="na">VALUE_DESERIALIZER_CLASS_CONFIG</span><span class="o">,</span> <span class="n">StringDeserializer</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span> </span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="o">(</span><span class="kd">final</span> <span class="n">KafkaConsumer</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">consumer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">KafkaConsumer</span><span class="o">&lt;&gt;(</span><span class="n">props</span><span class="o">))</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Map</span><span class="o">&lt;</span><span class="n">TopicPartition</span><span class="o">,</span> <span class="n">Long</span><span class="o">&gt;</span> <span class="n">endOffsets</span> <span class="o">=</span> <span class="n">consumer</span><span class="o">.</span><span class="na">endOffsets</span><span class="o">(</span><span class="n">consumedOffsets</span><span class="o">.</span><span class="na">keySet</span><span class="o">());</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">endOffsets</span><span class="o">.</span><span class="na">entrySet</span><span class="o">().</span><span class="na">stream</span><span class="o">().</span><span class="na">collect</span><span class="o">(</span><span class="n">Collectors</span><span class="o">.</span><span class="na">toMap</span><span class="o">(</span><span class="n">entry</span> <span class="o">-&gt;</span> <span class="n">entry</span><span class="o">.</span><span class="na">getKey</span><span class="o">(),</span> </span></span><span class="line"><span class="cl"> <span class="n">entry</span> <span class="o">-&gt;</span> <span class="n">entry</span><span class="o">.</span><span class="na">getValue</span><span class="o">()</span> <span class="o">-</span> <span class="n">consumedOffsets</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">entry</span><span class="o">.</span><span class="na">getKey</span><span class="o">()).</span><span class="na">offset</span><span class="o">()));</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">interrupt</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 处理中断异常 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="n">Collections</span><span class="o">.</span><span class="na">emptyMap</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">ExecutionException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 处理ExecutionException </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="n">Collections</span><span class="o">.</span><span class="na">emptyMap</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">TimeoutException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="n">TimeoutException</span><span class="o">(</span><span class="s">&#34;Timed out when getting lag for consumer group &#34;</span> <span class="o">+</span> <span class="n">groupID</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="使用jmx-监控指标">使用JMX 监控指标</h2> <p>使用 JMX 指标集成到 Grafana 等监控框架中</p> <p>kafka 消费者 提供了名为 <code>kafka.consumer:type=consumer-fetch-manager-metrics,client-id=“{client-id}”</code> 指标</p> <p>其中有两个指标和消费进度有关:</p> <ul> <li>records-lag-max</li> <li>records-lead-min: 消费者位移和分区最旧消息的最小差值</li> </ul> <h1 id="第二十三讲-副本机制">第二十三讲 副本机制</h1> <h2 id="副本定义">副本定义</h2> <p>副本本质上是一个只能追加写消息的提交日志。</p> <p>这些副本分散保存在不同的 Broker 上, 从而能够对抗 Broker 宕机带来的数据不可用。</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/23_1.webp" alt="replica1"></p> <h2 id="副本角色">副本角色</h2> <p><img src="https://ppd0705.github.io/image/kafka_in_action/23_2.webp" alt="replica2"></p> <p>副本分为两类,Leader 和 Follower, 分区在创建时会选出一个 Leader 副本, 其余副本成为 Follower。</p> <p>所有的读写请求都由 Leader 处理,Follower 只是从 Leader 异步拉取消息从而实现同步</p> <p>当 Leader 挂掉后,ZooKeeper 能够监控到并开启新一轮的选举</p> <p>Follower 不对外提供服务的好处:</p> <ol> <li>方便实现 Read-your-writes 。如果从 Follower 读取消息,由于是异步的,可能看不到最新的消息</li> <li>方便实现单调读 Monotonic Reads 。如果从多个副本读消息,可能出现某条消息一会存在一会不存在</li> </ol> <h2 id="in-sync-replicas-isr">In-sync Replicas (ISR)</h2> <p>ISR 指与 Leader 同步的副本集合, Leader 本身也包括在 ISR 中</p> <p>通过 replica.lag.time.max.ms 参数指定 Follower 能够落后 Leader 的最长时间, 在此范围内的 Follower 都在 ISR 中。</p> <h2 id="unclean-领导者选举">Unclean 领导者选举</h2> <p>当 ISR 为空时,只存在非同步副本。 unclean.leader.election.enable 参数控制是否允许 Unclean 领导者选举</p> <p>开启的好处是使得 Leader 副本一直存在,提高可用性,但是可能会造成数据丢失,不建议开启。</p> <h1 id="第二十四讲-请求处理过程">第二十四讲 请求处理过程</h1> <p>kafka 定义了一组请求协议,用于实现各种交互操作,<code>PRODUCE</code> 请求用于生产消息,<code>FETCH</code>请求用于消费消息,<code>METADATA</code>请求用于获取集群元数据</p> <h2 id="数据类请求">数据类请求</h2> <p>kafka 使用 Reactor 模式处理请求</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/24_1.webp" alt="reactor"></p> <p>Acceptor 线程用于分发请求,分发使用轮询模式</p> <p>网络线程池来处理具体的请求,<code>num.network.threads</code> 参数控制线程数量,默认为3。</p> <p>网络线程拿到请求后,将请求放入一个共享队列中,IO 线程池负责从队列中 取出请求,执行真正的处理。如果是 PRODUCE 请求,则将消息写入磁盘日志; 如果是 FETCH 请求,则从磁盘或页缓存中读取消息。</p> <p>IO 线程处理完请求后, 会将响应发送到网络线程池的响应队列中,然后由 对应的网络线程将响应返回给客户端。</p> <p><code>num.io.threads</code> 参数控制 IO 线程数量,默认为8。</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/24_2.webp" alt="io_thread_pool"></p> <p>Purgatory 组件用来缓存延时请求。比如设置了 ack=all 的 PRODUCE 请求, 需要等待所有 ISR 副本接收后才能返回。</p> <h2 id="控制类请求">控制类请求</h2> <p>控制类请求用来执行特定的 kafka 内部动作,比如负责更新 Leader/Follower/ISR 的 <code>LeaderAndIsr</code> 请求、负责勒令副本下线的 <code>StopReplica</code> 请求。</p> <p>kafka 将 控制类请求和数据类请求分类,分别创建了两套网络线程池和 IO 线程池</p> <h1 id="第二十五讲-重平衡过程">第二十五讲 重平衡过程</h1> <h2 id="通知触发">通知触发</h2> <p>重平衡通知是通过心跳来通知的, 协调者会将<code>REBALANCE_IN_PROCESS</code>包含在心跳响应中。</p> <h2 id="消费者组状态机">消费者组状态机</h2> <p>kafka 为消费者组定义了五种状态</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_1.webp" alt="all_status"></p> <p>状态机状态流转如下</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_2.webp" alt="status_transfer"></p> <h2 id="消费者端流程">消费者端流程</h2> <p>在消费者端,重平衡分为两个步骤:加入组和等待领导者消费者分配方案。这两个步骤 分别对应两类特定的请求:JoinGroup 和 SyncGroup。</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_3.webp" alt="join_group"></p> <p>在收到所有消费者的订阅消息后,消费者通常将第一个发送 JoinGroup 消息的成员 当做领导者,并将所有订阅信息发给领导者。领导者做出方案后,会发送 SyncGroup 请求,将方案发给协调者。其他成员也会发送空的 SyncGroup 请求。 协调者会将分配方案在 SyncAGroup 响应中发送。</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_4.webp" alt="sync_group"></p> <h2 id="协调者端流程">协调者端流程</h2> <h3 id="新成员入组">新成员入组</h3> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_5.webp" alt="new_member"></p> <h3 id="成员主动离组">成员主动离组</h3> <p>主动离组会发起 LeaveGroup</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_6.webp" alt="reduce_member"></p> <h3 id="成员崩溃">成员崩溃</h3> <p><img src="https://ppd0705.github.io/image/kafka_in_action/25_7.webp" alt="lost_member"></p> <p>重平衡开始后,协调者会给予成员一段缓冲时间 rebalance.timeout,要求成员必须在 这段时间快速上报自己的位移消息。</p> <h1 id="第二十七讲-高水位-和-leader-epoch">第二十七讲 高水位 和 Leader Epoch</h1> <h2 id="什么是高水位">什么是高水位</h2> <p>《Streaming System》 表述: 水位是一个单调增加且表征最早未完成工作( oldest work not yet completed)的时间戳</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/27_1.webp" alt="high_watermark"></p> <h2 id="高水位的作用">高水位的作用</h2> <ol> <li>定义消息可见性,即标识哪些消息可以被消费者消费</li> <li>帮助kafka 完成副本同步。</li> </ol> <p><img src="https://ppd0705.github.io/image/kafka_in_action/27_2.webp" alt="high_watermark"></p> <p>位移值等于高水位的消息也属于未提交的消息。</p> <p>同一个副本对象,其高水位值小于等于 LEO 值</p> <h2 id="高水位更新机制">高水位更新机制</h2> <p><img src="https://ppd0705.github.io/image/kafka_in_action/27_3.webp" alt="high_watermark_update"></p> <p>leader 副本 会保存 所有 follower 副本的 LEO 值,其作用是帮助 leader 副本确认其高水位,即整个分区高水位。</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/27_4.webp" alt="high_watermark_update_detail"></p> <h3 id="leader副本">leader副本</h3> <p>处理生产者请求:</p> <ol> <li>写入消息到本地磁盘</li> <li>更新分区高水位值: 获取保存的所有 远程副本 LEO 值, currentHW = max(crrentHW, min(LEO-1, &hellip;, LEO-n))</li> </ol> <p>处理 follower 副本拉取消息逻辑</p> <ol> <li>读取磁盘中的消息数据</li> <li>使用 follower 副本发送请求中 的位移值更新远程 副本的 LEO 值</li> <li>更新分区高水位值</li> </ol> <h3 id="follower副本">follower副本</h3> <ol> <li>写入消息到本地磁盘</li> <li>更新 LEO 值 currentLEO</li> <li>更新高水位值: 获取leader 发送的高水位值 currentHW, 更新高水位为 min(currentHW, currentLEO)</li> </ol> <h1 id="第二十八讲-主题管理">第二十八讲 主题管理</h1> <p>创建主题</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-topics.sh --bootstrap-server broker_host:port --create --topic my_topic_name --partitions <span class="m">1</span> --replication-factor <span class="m">1</span> </span></span></code></pre></td></tr></table> </div> </div><p>查询主题列表</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-topics.sh --bootstrap-server broker_host:port --list </span></span></code></pre></td></tr></table> </div> </div><p>查询单个主题详情</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-topics.sh --bootstrap-server broker_host:port --describe --topic &lt;topic_name&gt; </span></span></code></pre></td></tr></table> </div> </div><p>修改(增加)主题分区</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-topics.sh --bootstrap-server broker_host:port --alter --topic &lt;topic_name&gt; --partitions &lt;新分区数&gt; </span></span></code></pre></td></tr></table> </div> </div><p>修改主题单条消息最大字节数</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-configs.sh --zookeeper zookeeper_host:port --entity-type topics --entity-name &lt;topic_name&gt; --alter --add-config max.message.bytes<span class="o">=</span><span class="m">10485760</span> </span></span></code></pre></td></tr></table> </div> </div><p>修改主题带宽限速</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-configs.sh --zookeeper zookeeper_host:port --alter --add-config <span class="s1">&#39;leader.replication.throttled.rate=104857600,follower.replication.throttled.rate=104857600&#39;</span> --entity-type brokers --entity-name <span class="m">0</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">bin/kafka-configs.sh --zookeeper zookeeper_host:port --alter --add-config <span class="s1">&#39;leader.replication.throttled.replicas=*,follower.replication.throttled.replicas=*&#39;</span> --entity-type topics --entity-name <span class="nb">test</span> </span></span></code></pre></td></tr></table> </div> </div><p>删除主题</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-topics.sh --bootstrap-server broker_host:port --delete --topic &lt;topic_name&gt; </span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十九讲-动态配置">第二十九讲 动态配置</h1> <p>broker config 有三类参数</p> <ul> <li>read-only: 重启 broker 才会生效</li> <li>per-broker: 对应 broker 生效</li> <li>cluster-wide: 整个集群生效</li> </ul> <p>常用动态参数</p> <ol> <li>log.retention.ms</li> </ol> <p>修改日志留存时间</p> <ol start="2"> <li>num.io.threads 和 num.network.threads</li> </ol> <p>修改 IO 和网络线程数量</p> <ol start="3"> <li>num.replica.fetchers 修改 follower 拉取数据的线程数量</li> </ol> <h1 id="第三十讲-重设消费者组位移">第三十讲 重设消费者组位移</h1> <h2 id="策略类型">策略类型</h2> <p>kafka 支持 7 种策略</p> <p><img src="https://ppd0705.github.io/image/kafka_in_action/30_1.webp" alt="consumer_offset_strategy"></p> <h2 id="命令行设置">命令行设置</h2> <p>Earliest</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-earliest –execute </span></span></code></pre></td></tr></table> </div> </div><p>Latest</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-latest --execute </span></span></code></pre></td></tr></table> </div> </div><p>Current</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-current --execute </span></span></code></pre></td></tr></table> </div> </div><p>Specified-Offset</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-offset &lt;offset&gt; --execute </span></span></code></pre></td></tr></table> </div> </div><p>Shift-By-N</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --shift-by &lt;offset_N&gt; --execute </span></span></code></pre></td></tr></table> </div> </div><p>Datetime</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --to-datetime 2019-06-20T20:00:00.000 --execute </span></span></code></pre></td></tr></table> </div> </div><p>Duration</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --by-duration PT0H30M0S --execute </span></span></code></pre></td></tr></table> </div> </div><h1 id="第三十一讲-常见脚本汇总">第三十一讲 常见脚本汇总</h1> <p><code>kafka-dump-log</code> 查看 kafka 消息文件的内容,包括消息的各种元数据信息和消息本身</p> <p><code>kafka-log-dirs</code> 查看各个 Broker 磁盘占用情况</p> <h2 id="重点操作">重点操作</h2> <p>生成消息</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ bin/kafka-console-producer.sh --broker-list kafka-host:port --topic test-topic --request-required-acks -1 --producer-property compression.type<span class="o">=</span>lz4 </span></span><span class="line"><span class="cl">&gt; </span></span></code></pre></td></tr></table> </div> </div><p>消费消息</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ bin/kafka-console-consumer.sh --bootstrap-server kafka-host:port --topic test-topic --group test-group --from-beginning --consumer-property enable.auto.commit<span class="o">=</span><span class="nb">false</span> </span></span></code></pre></td></tr></table> </div> </div><p>查看文件</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">$ bin/kafka-dump-log.sh --files ../data_dir/kafka_1/test-topic-1/00000000000000000000.log </span></span><span class="line"><span class="cl">Dumping ../data_dir/kafka_1/test-topic-1/00000000000000000000.log </span></span><span class="line"><span class="cl">Starting offset: <span class="m">0</span> </span></span><span class="line"><span class="cl">baseOffset: <span class="m">0</span> lastOffset: <span class="m">14</span> count: <span class="m">15</span> baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: <span class="m">0</span> isTransactional: <span class="nb">false</span> isControl: <span class="nb">false</span> position: <span class="m">0</span> CreateTime: <span class="m">1561597044933</span> size: <span class="m">1237</span> magic: <span class="m">2</span> compresscodec: LZ4 crc: <span class="m">646766737</span> isvalid: <span class="nb">true</span> </span></span><span class="line"><span class="cl">baseOffset: <span class="m">15</span> lastOffset: <span class="m">29</span> count: <span class="m">15</span> baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: <span class="m">0</span> isTransactional: <span class="nb">false</span> isControl: <span class="nb">false</span> position: <span class="m">1237</span> CreateTime: <span class="m">1561597044934</span> size: <span class="m">1237</span> magic: <span class="m">2</span> compresscodec: LZ4 crc: <span class="m">3751986433</span> isvalid: <span class="nb">true</span> </span></span><span class="line"><span class="cl">...... </span></span></code></pre></td></tr></table> </div> </div><h1 id="相关链接">相关链接</h1> <h2 id="kafka管理和监控平台">kafka管理和监控平台</h2> <ul> <li><a href="https://github.com/smartloli/EFAK">A easy and high-performance monitoring system</a></li> <li><a href="https://github.com/xaecbd/KCenter">A unified platform for kafka cluster management</a></li> <li><a href="https://github.com/didi/LogiKM">一站式Apache Kafka集群指标监控与运维管控平台</a></li> </ul> supervisor 源码解读 https://ppd0705.github.io/post/learn_supervisor/ Sun, 27 Mar 2022 21:21:20 +0800 https://ppd0705.github.io/post/learn_supervisor/ <h1 id="简介">简介</h1> <p>Supervisor is a client/server system that allows its users to control a number of processes on UNIX-like operating systems.</p> <p>当前最新版本为 4.2.4,发布于 <a href="http://supervisord.org/changes.html#id1">2021-12-30</a></p> <p>第一个发布版本为 1.0.3,发布于 <a href="http://supervisord.org/changes.html#or-alpha-3-2004-05-26">2004-05-26</a></p> <p><img src="https://ppd0705.github.io/image/learn_supervisor/supervisor_release_log.png" alt="first_release"></p> <h1 id="主要模块">主要模块</h1> <h2 id="supervisorctl">supervisorctl</h2> <p>supervisorctl 是个命令行客户端,用户可以通过命令行输入指令来获取和变更进程状态等</p> <h3 id="命令列表">命令列表</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">supervisor&gt; <span class="nb">help</span> </span></span><span class="line"><span class="cl">default commands <span class="o">(</span><span class="nb">type</span> <span class="nb">help</span> &lt;topic&gt;<span class="o">)</span>: </span></span><span class="line"><span class="cl"><span class="o">=====================================</span> </span></span><span class="line"><span class="cl">add <span class="nb">exit</span> open reload restart start tail </span></span><span class="line"><span class="cl">avail <span class="nb">fg</span> pid remove shutdown status update </span></span><span class="line"><span class="cl">clear maintail quit reread signal stop version </span></span></code></pre></td></tr></table> </div> </div><h3 id="主流程">主流程</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># file: supervisor/supervisorctl.py</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Controller</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">Cmd</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">cmdloop</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">intro</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">line</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">stop</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">onecmd</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">onecmd</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">line</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">cmd</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="n">line</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">parseline</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">do_func</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_do_func</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">do_func</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">_get_do_func</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">func_name</span> <span class="o">=</span> <span class="s1">&#39;do_&#39;</span> <span class="o">+</span> <span class="n">cmd</span> </span></span><span class="line"><span class="cl"> <span class="n">func</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func_name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">default</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">func</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">do_start</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">supervisor</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ctl</span><span class="o">.</span><span class="n">get_supervisor</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">names</span> <span class="o">=</span> <span class="n">arg</span><span class="o">.</span><span class="n">split</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">names</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">result</span> <span class="o">=</span> <span class="n">supervisor</span><span class="o">.</span><span class="n">startProcess</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ctl</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1">: started&#39;</span> <span class="o">%</span> <span class="n">name</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="supervisord">supervisord</h2> <h3 id="主流程-1">主流程</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># file: supervisor/supervisord.py</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Supervisor</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">options</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="n">options</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">process_groups</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="c1"># process_groups struct like:</span> </span></span><span class="line"><span class="cl"> <span class="c1"># {</span> </span></span><span class="line"><span class="cl"> <span class="c1"># &#34;groupA&#34;: {</span> </span></span><span class="line"><span class="cl"> <span class="c1"># &#34;groupA&#34;: &lt;Subprocess instance&gt;,</span> </span></span><span class="line"><span class="cl"> <span class="c1"># },</span> </span></span><span class="line"><span class="cl"> <span class="c1"># &#34;groupB&#34;: {</span> </span></span><span class="line"><span class="cl"> <span class="c1"># &#34;programB1&#34;: &lt;Subprocess instance&gt;,</span> </span></span><span class="line"><span class="cl"> <span class="c1"># &#34;programB2&#34;: &lt;Subprocess instance&gt;,</span> </span></span><span class="line"><span class="cl"> <span class="c1"># },</span> </span></span><span class="line"><span class="cl"> <span class="c1"># }</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">runforever</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="c1"># 创建管理子进程的Subprocess类实例(实际子进程还没启动)</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">config</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">process_group_configs</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">add_process_group</span><span class="p">(</span><span class="n">config</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 启动 httpservers</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">openhttpservers</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 注册信号</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">setsignals</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 全局变量:包含所有socket对象</span> </span></span><span class="line"><span class="cl"> <span class="n">socket_map</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">get_socket_map</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="mi">1</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">combined_map</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="n">combined_map</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">socket_map</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 添加子进程标准输入输出相关的pipe</span> </span></span><span class="line"><span class="cl"> <span class="n">combined_map</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">get_process_map</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 注册文件描述符</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">fd</span><span class="p">,</span> <span class="n">dispatcher</span> <span class="ow">in</span> <span class="n">combined_map</span><span class="o">.</span><span class="n">items</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">readable</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">poller</span><span class="o">.</span><span class="n">register_readable</span><span class="p">(</span><span class="n">fd</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">writable</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">poller</span><span class="o">.</span><span class="n">register_writable</span><span class="p">(</span><span class="n">fd</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 读取可读写的文件描述符</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span><span class="p">,</span> <span class="n">w</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">poller</span><span class="o">.</span><span class="n">poll</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 处理对应事件</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">fd</span> <span class="ow">in</span> <span class="n">r</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">fd</span> <span class="ow">in</span> <span class="n">combined_map</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">dispatcher</span> <span class="o">=</span> <span class="n">combined_map</span><span class="p">[</span><span class="n">fd</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">dispatcher</span><span class="o">.</span><span class="n">handle_read_event</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">fd</span> <span class="ow">in</span> <span class="n">w</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">fd</span> <span class="ow">in</span> <span class="n">combined_map</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">dispatcher</span> <span class="o">=</span> <span class="n">combined_map</span><span class="p">[</span><span class="n">fd</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">dispatcher</span><span class="o">.</span><span class="n">handle_write_event</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">group</span> <span class="ow">in</span> <span class="n">pgroups</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="c1"># 检查进程状态,确定是否要启动或者停止操作</span> </span></span><span class="line"><span class="cl"> <span class="n">group</span><span class="o">.</span><span class="n">transition</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># waitpid(0, 1) 检查是否有进程停止</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">reap</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 信号处理: 是否要停止、重启、重新打开日志文件等</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">handle_signal</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 发送心跳事件</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">tick</span><span class="p">()</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="子进程实例">子进程实例</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">make_pipes</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stderr</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">pipes</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">stdin</span><span class="p">,</span> <span class="n">child_stdin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pipe</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stdin&#39;</span><span class="p">],</span> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;stdin&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">stdin</span><span class="p">,</span> <span class="n">child_stdin</span> </span></span><span class="line"><span class="cl"> <span class="n">stdout</span><span class="p">,</span> <span class="n">child_stdout</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pipe</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;stdout&#39;</span><span class="p">],</span> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stdout&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">stdout</span><span class="p">,</span> <span class="n">child_stdout</span> </span></span><span class="line"><span class="cl"> <span class="n">stderr</span><span class="p">,</span> <span class="n">child_stderr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pipe</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;stderr&#39;</span><span class="p">],</span> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stderr&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">stderr</span><span class="p">,</span> <span class="n">child_stderr</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">fd</span> <span class="ow">in</span> <span class="p">(</span><span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;stdout&#39;</span><span class="p">],</span> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;stderr&#39;</span><span class="p">],</span> <span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;stdin&#39;</span><span class="p">]):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">fd</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">flags</span> <span class="o">=</span> <span class="n">fcntl</span><span class="o">.</span><span class="n">fcntl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">fcntl</span><span class="o">.</span><span class="n">F_GETFL</span><span class="p">)</span> <span class="o">|</span> <span class="n">os</span><span class="o">.</span><span class="n">O_NDELAY</span> <span class="c1"># O_NDELAY is an alias for O_NONBLOCK</span> </span></span><span class="line"><span class="cl"> <span class="n">fcntl</span><span class="o">.</span><span class="n">fcntl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">fcntl</span><span class="o">.</span><span class="n">F_SETFL</span><span class="p">,</span> <span class="n">flags</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">pipes</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Subprocess</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">pid</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">config</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">config</span> <span class="o">=</span> <span class="n">config</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">dispatchers</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">pipes</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">ProcessStates</span><span class="o">.</span><span class="n">STOPPED</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">spawn</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">filename</span><span class="p">,</span> <span class="n">argv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_execv_args</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">pipes</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">make_dispatchers</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">pid</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">pid</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">_spawn_as_parent</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">_spawn_as_child</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">argv</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">_spawn_as_parent</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pid</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">pid</span> <span class="o">=</span> <span class="n">pid</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">options</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">close_child_pipes</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pipes</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">_spawn_as_child</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="n">argv</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">options</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">setpgrp</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 文件描述符重定向</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">dup2</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stdin&#39;</span><span class="p">],</span> <span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">dup2</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stdout&#39;</span><span class="p">],</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">redirect_stderr</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">dup2</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stdout&#39;</span><span class="p">],</span> <span class="mi">2</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">dup2</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pipes</span><span class="p">[</span><span class="s1">&#39;child_stderr&#39;</span><span class="p">],</span> <span class="mi">2</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">options</span><span class="o">.</span><span class="n">minfds</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">close_fd</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">.</span><span class="n">execve</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">argv</span><span class="p">,</span> <span class="n">env</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">stop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">kill</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">stopsignal</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">kill</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sig</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">change_state</span><span class="p">(</span><span class="n">ProcessStates</span><span class="o">.</span><span class="n">STOPPING</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">os</span><span class="o">.</span><span class="n">kill</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pid</span><span class="p">,</span> <span class="n">sig</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="http_server">http_server</h2> <p>支持基于 tcp socket 或者 unix domain socket 的 http server</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">supervisor_af_inet_http_server</span><span class="p">(</span><span class="n">supervisor_http_server</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ip</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">logger_object</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ip</span> <span class="o">=</span> <span class="n">ip</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">port</span> <span class="o">=</span> <span class="n">port</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">handlers</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">socket</span> <span class="o">=</span> <span class="n">sock</span> </span></span><span class="line"><span class="cl"> <span class="n">sock</span><span class="o">.</span><span class="n">setblocking</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">sock</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">ip</span><span class="p">,</span> <span class="n">port</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">listen</span> <span class="p">(</span><span class="mi">1024</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 注册到全局 socket 字典里</span> </span></span><span class="line"><span class="cl"> <span class="n">socket_map</span><span class="p">[</span><span class="n">sock</span><span class="o">.</span><span class="n">fileno</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">handle_read_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">handle_accept</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">handle_accept</span> <span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">conn</span><span class="p">,</span> <span class="n">addr</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">http_channel</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">conn</span><span class="p">,</span> <span class="n">addr</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">http_channel</span> <span class="p">(</span><span class="n">asynchat</span><span class="o">.</span><span class="n">async_chat</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span> <span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">server</span><span class="p">,</span> <span class="n">conn</span><span class="p">,</span> <span class="n">addr</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">socket</span> <span class="o">=</span> <span class="n">conn</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">setblocking</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">socket_map</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">fileno</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ac_in_buffer</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;&#39;</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;&#39;</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer_size</span> <span class="o">=</span> <span class="mi">512</span> </span></span><span class="line"><span class="cl"> <span class="n">terminator</span> <span class="o">=</span> <span class="s1">&#39;</span><span class="se">\r\n\r\n</span><span class="s1">&#39;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">handle_read_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">handle_read</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">handle_read</span> <span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">buffer_size</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 简化版 接收数据拼接 request, 实际逻辑更复杂</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ac_in_buffer</span> <span class="o">+=</span> <span class="n">data</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">terminator</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">ac_in_buffer</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">request</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">new_reqeust</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">handlers</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">h</span><span class="o">.</span><span class="n">match</span> <span class="p">(</span><span class="n">request</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">h</span><span class="o">.</span><span class="n">handle_request</span> <span class="p">(</span><span class="n">request</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">handle_write_event</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">handle_write</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">handle_write</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">obs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer_size</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">obs</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">refill_buffer</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer</span> </span></span><span class="line"><span class="cl"> <span class="n">num_sent</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">send</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer</span><span class="p">[:</span><span class="n">obs</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">num_sent</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ac_out_buffer</span><span class="p">[</span><span class="n">num_sent</span><span class="p">:]</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="xmlrpc_interface">xmlrpc_interface</h2> <h3 id="xmlrpc协议简介">xmlrpc协议简介</h3> <p>xmlrpc 是一种基于 http 通信、使用 XML 格式序列化的远程过程调用方式</p> <p>请求示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="err">POST /RPC2 HTTP/1.1 # 请求使用 POST 方法, path 为 /RPC2 </span></span></span><span class="line"><span class="cl"><span class="err">Host: localhost:9011 </span></span></span><span class="line"><span class="cl"><span class="err">Accept-Encoding: gzip </span></span></span><span class="line"><span class="cl"><span class="err">Content-Type: text/xml </span></span></span><span class="line"><span class="cl"><span class="err">User-Agent: Python-xmlrpc/3.8 </span></span></span><span class="line"><span class="cl"><span class="err">Content-Length: 176 </span></span></span><span class="line"><span class="cl"><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err">&lt;?xml version=&#39;1.0&#39;&gt; </span></span></span><span class="line"><span class="cl"><span class="err">&lt;methodCall&gt; </span></span></span><span class="line"><span class="cl"><span class="err"> &lt;methodName&gt;supervisor.stopProcess&lt;/methodName&gt; </span></span></span><span class="line"><span class="cl"><span class="err"> &lt;params&gt; </span></span></span><span class="line"><span class="cl"><span class="err"> &lt;param&gt; </span></span></span><span class="line"><span class="cl"><span class="err"> &lt;value&gt;&lt;string&gt;exits_123s&lt;/string&lt;/value&gt; </span></span></span><span class="line"><span class="cl"><span class="err"> &lt;/param&gt; </span></span></span><span class="line"><span class="cl"><span class="err"> &lt;/params&gt; </span></span></span><span class="line"><span class="cl"><span class="err">&lt;/methodCall&gt; </span></span></span></code></pre></td></tr></table> </div> </div><p>正常响应示例</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span> </span></span><span class="line"><span class="cl"><span class="n">Server</span><span class="o">:</span> <span class="l">Medusa/1.12</span> </span></span><span class="line"><span class="cl"><span class="n">Date</span><span class="o">:</span> <span class="l">Thu, 24 Mar 2022 09:02:51 GMT</span> </span></span><span class="line"><span class="cl"><span class="n">Content-Type</span><span class="o">:</span> <span class="l">text/xml</span> </span></span><span class="line"><span class="cl"><span class="n">Content-Length</span><span class="o">:</span> <span class="l">129</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#39;1.0&#39;?&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;methodResponse&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;params&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;param&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;value&gt;&lt;boolean&gt;</span>1<span class="nt">&lt;/boolean&gt;&lt;/value&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/param&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/params&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/methodResponse&gt;</span> </span></span></code></pre></td></tr></table> </div> </div><p>错误响应示例 (同样返回 http code 200 )</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span> </span></span><span class="line"><span class="cl"><span class="n">Server</span><span class="o">:</span> <span class="l">Medusa/1.12</span> </span></span><span class="line"><span class="cl"><span class="n">Date</span><span class="o">:</span> <span class="l">Thu, 24 Mar 2022 08:53:09 GMT</span> </span></span><span class="line"><span class="cl"><span class="n">Content-Type</span><span class="o">:</span> <span class="l">text/xml</span> </span></span><span class="line"><span class="cl"><span class="n">Content-Length</span><span class="o">:</span> <span class="l">279</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#39;1.0&#39;?&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;methodResponse&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;fault&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;value&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;struct&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;member&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;name&gt;</span>faultCode<span class="nt">&lt;/name&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;value&gt;&lt;int&gt;</span>70<span class="nt">&lt;/int&gt;&lt;/value&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/member&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;member&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;name&gt;</span>faultString<span class="nt">&lt;/name&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;value&gt;&lt;string&gt;</span>NOT_RUNNING: exits_123s<span class="nt">&lt;/string&gt;&lt;/value&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/member&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/struct&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/value&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/fault&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/methodResponse&gt;</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="supervisor中的实现">supervisor中的实现</h3> <p>服务端 handler 实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># file: supervisor/xmlrpcrpc.py</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">supervisor_xmlrpc_handler</span><span class="p">(</span><span class="n">xmlrpc_handler</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="s1">&#39;/RPC2&#39;</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">supervisord</span><span class="p">,</span> <span class="n">subinterfaces</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">rpcinterface</span> <span class="o">=</span> <span class="n">RootRPCInterface</span><span class="p">(</span><span class="n">subinterfaces</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">supervisord</span> <span class="o">=</span> <span class="n">supervisord</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">match</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">uri</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">continue_request</span> <span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">params</span><span class="p">,</span> <span class="n">method</span> <span class="o">=</span> <span class="n">xmlrpclib</span><span class="o">.</span><span class="n">loads</span> <span class="p">(</span><span class="n">data</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">call</span> <span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">request</span><span class="o">.</span><span class="n">push</span> <span class="p">(</span><span class="n">response</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="n">params</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">traverse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">rpcinterface</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span></code></pre></td></tr></table> </div> </div><p>接口实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># file: supervisor/rpcinterface.py</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SupervisorNamespaceRPCInterface</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">supervisord</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="bp">self</span><span class="o">.</span><span class="n">supervisord</span> <span class="o">=</span> <span class="n">supervisord</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">stopProcess</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">wait</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">process</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">supervisord</span><span class="o">.</span><span class="n">process_groups</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">process</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">RPCError</span><span class="p">(</span><span class="n">Faults</span><span class="o">.</span><span class="n">BAD_NAME</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">process</span><span class="o">.</span><span class="n">get_state</span><span class="p">()</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">RUNNING_STATES</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">RPCError</span><span class="p">(</span><span class="n">Faults</span><span class="o">.</span><span class="n">NOT_RUNNING</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">msg</span> <span class="o">=</span> <span class="n">process</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">msg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="n">RPCError</span><span class="p">(</span><span class="n">Faults</span><span class="o">.</span><span class="n">FAILED</span><span class="p">,</span> <span class="n">msg</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">True</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="event">event</h2> <h3 id="event-类型">event 类型</h3> <ul> <li>SUPERVISOR_STATE_CHANGE: supervisord 状态相关</li> <li>PROCESS_STATE: 子进程进程状态相关</li> <li>PROCESS_GROUP: 进程组相关</li> <li>TICK: 心跳相关</li> </ul> <h3 id="订阅实现逻辑">订阅实现逻辑</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">callbacks</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">subscribe</span><span class="p">(</span><span class="nb">type</span><span class="p">,</span> <span class="n">callback</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">callbacks</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="nb">type</span><span class="p">,</span> <span class="n">callback</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">unsubscribe</span><span class="p">(</span><span class="nb">type</span><span class="p">,</span> <span class="n">callback</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">callbacks</span><span class="o">.</span><span class="n">remove</span><span class="p">((</span><span class="nb">type</span><span class="p">,</span> <span class="n">callback</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="n">event</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nb">type</span><span class="p">,</span> <span class="n">callback</span> <span class="ow">in</span> <span class="n">callbacks</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="nb">type</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">callback</span><span class="p">(</span><span class="n">event</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="eventlistener">eventlistener</h3> <p>eventlistener 是一种特殊的子进程,通过标准输入输出和 supervisord 来获取 event</p> <ol> <li>向 stdout 写入 <code>READY\n</code> 向 supervisord 请求 event</li> <li>读取 supervisord 在 stdin 写入的 event</li> <li>向 stdout 写入 <code>RESULT 2\nOK</code> 向 supervisord 确认 event 已接收</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">write_stdout</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="c1"># only eventlistener protocol messages may be sent to stdout</span> </span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">write_stderr</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="mi">1</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="c1"># transition from ACKNOWLEDGED to READY</span> </span></span><span class="line"><span class="cl"> <span class="n">write_stdout</span><span class="p">(</span><span class="s1">&#39;READY</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># read event</span> </span></span><span class="line"><span class="cl"> <span class="n">line</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">headers</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">([</span> <span class="n">x</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;:&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">()</span> <span class="p">])</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">payload</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">headers</span><span class="p">[</span><span class="s1">&#39;len&#39;</span><span class="p">]))</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># transition from READY to ACKNOWLEDGED</span> </span></span><span class="line"><span class="cl"> <span class="n">write_stdout</span><span class="p">(</span><span class="s1">&#39;RESULT 2</span><span class="se">\n</span><span class="s1">OK&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">main</span><span class="p">()</span> </span></span></code></pre></td></tr></table> </div> </div><p>总结</p> <ol> <li>客户端和服务端通过 xmlrpc 通信</li> <li>主进程通过将 socket 和 pipe 设为非阻塞模式,然后通过轮询处理读写事件</li> <li>主进程和子进程之间通过信号和 pipe 通信</li> </ol> <h1 id="参考链接">参考链接</h1> <ul> <li><a href="https://github.com/Supervisor/supervisor">supervisor</a>: supervisor 源码</li> <li><a href="http://xmlrpc.com/spec.md">xmlrpc_specification</a>:xmlprc 协议文档</li> <li><a href="https://docs.python.org/3/library/xmlrpc.client.html">xmlrpc_python_stdlib</a>:xmlprc python标准库文档</li> <li><a href="https://github.com/ppd0705/supervisor-gateway">supervisor_gateway</a>:基于 eventlistener 和 xmlrpc 实现的提供 RESTful API 的 supervisor 中间件</li> </ul> [笔记]业务开发算法 50 讲 https://ppd0705.github.io/post/algo_in_action/ Mon, 14 Mar 2022 08:36:33 +0800 https://ppd0705.github.io/post/algo_in_action/ <h1 id="第零讲-git-diff-文本差分算法">第零讲 git diff 文本差分算法</h1> <h2 id="定义">定义</h2> <p>git diff 内置了多种算法 ,这里介绍的是其默认算法 Myers 差分算法。</p> <blockquote> <p>An edit script for A and B is a set of insertion and deletion commands that transform A into B.</p> </blockquote> <p>文本差分算法可以定义为用于求输入源文本到目标文本之间的编辑脚本算法。</p> <p>编辑脚本是其中一系列插入和删除操作的序列</p> <h2 id="评价指标">评价指标</h2> <p>有两个指标可以评价编辑脚本的优劣,分别是编辑脚本的长度、可读性</p> <h3 id="编辑脚本长度">编辑脚本长度</h3> <p>编辑脚本较短时,尽可能的保留了更多原序列中的元素。</p> <p>最短编辑脚本(SES Shortest Edit Script)和 最长公共子序列问题其实是对偶问题</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">源序列 </span></span><span class="line"><span class="cl">S = ABCABBA length m = 7 </span></span><span class="line"><span class="cl">目标序列 </span></span><span class="line"><span class="cl">T = CBABAC length n = 6 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">最长公共子序列(不唯一) </span></span><span class="line"><span class="cl">C = CBBA length LC = 4 </span></span></code></pre></td></tr></table> </div> </div><p>从上面的例子可以看出 SES 的长度等于 <code>m + n - 2 * LC</code></p> <h3 id="可读性">可读性</h3> <p>可读性可以总结为两点:</p> <ol> <li>尽可能保留整段文本,尽可能连续删除和插入,而不是彼此交叉</li> <li>大部分人习惯先看到原文本删除,再看到目标文本的插入</li> </ol> <p>所以总结算法逻辑就是:在最短的编辑脚本里,尽量找到删除在增加前面,且尽可能多的连续删除更多行的方式。</p> <h2 id="模型抽象">模型抽象</h2> <p>从源序列 S 到目标序列 T,有删除和插入两种操作,我们需要找到一种对这两个操作和相应变换状态的抽象方式。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">源序列 </span></span><span class="line"><span class="cl">S = ABCABBA length m = 7 </span></span><span class="line"><span class="cl">目标序列 </span></span><span class="line"><span class="cl">T = CBABAC length n = 6 </span></span></code></pre></td></tr></table> </div> </div><h3 id="转化成图搜索问题">转化成图搜索问题</h3> <p><img src="https://ppd0705.github.io/image/algo_in_action/0_1.webp" alt="graph_search"></p> <p>Myers 将问题转化为图搜索问题 ,其中横轴代表源序列,纵轴代表目标序列, 坐标 (0, 0) 代表初始状态,(m, n) 则对应了最终完整的从 S 到 T 的 编辑脚本 。 横向移动代表删除操作,纵向移动代表插入操作。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/0_2.webp" alt="graph_search2"></p> <p>如上图所示,从 (0, 0) 到 (3,4) 的路径表示字符串 ABC &ndash;&gt; CBAB 的一种编辑方式, 我们先插入 CB,然后删除 AB,再插入 AB,最后删除 C。</p> <p>这样文本差分问题就转化成了如何在这样的网格中找到仅允许向下和向右移动的一个从 (0, 0) 到 (m, n) 的路径,路径的长度就代表了总共需要的操作数。</p> <p>对于源序列和目标序列字符相等的坐标,如 (2, 0) 和 (3, 1)、(0, 2) 和 (2,4) 等, 这种斜线路径不耗费任何操作数。如下图为一条最短路径的例子:</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/0_3.webp" alt="graph_search3"></p> <h3 id="如何解决图搜索">如何解决图搜索</h3> <p><img src="https://ppd0705.github.io/image/algo_in_action/0_4.webp" alt="graph_search4"></p> <p>Myers 论文中有几个重要的概念</p> <ul> <li>D-path: 需要 D 步操作的路径称之为 D-Path</li> <li>snake: 一个横线或竖线之后紧跟着 n 条斜线所形成的的路径为 snake,snake 操作数为 1,且 snake 结尾坐标后继不能为斜线。</li> <li>line: 从左上到右下的 45 度的斜线,可以用 k = x - y 表示, 在 m * n 的网格中,k 的取值范围为 [-n, m]。</li> </ul> <p>Myers 论文中原图对上面的几个概念示意:</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/0_5.webp" alt="graph_search5"></p> <p>这样我们求解最短编辑脚本的目标就可以定义为找最短到达 (m, n) 的 D-path。</p> <p>所有的 D-path 都是 (D-1)-path 加一条 snake 构成,这样我们可以从 1-path 出发, 去搜索所有 2-path 最远能走到哪,然后一直递推到 D-path,第一次遇到终点时,就找到最短路径了。</p> <p>当有多条 D-path 时,我们如何选可读性最好的路径呢? 基于先看到删除再看到插入的原则,在考虑 D-path时,会从多条 (D-1)-path 中选出横坐标更大的路径。</p> <h2 id="代码实现">代码实现</h2> <p>我们使用一个二维数组 dp 来记录图上的搜索状态</p> <ul> <li>dp 第一个维度代表操作数,最大操作范围是我们最短编辑脚本的长度 m + n - 2 * LC</li> <li>dp 第二个维度代表 k-line 的行号,在 操作数为 d 时, 其取值范围是 [-d, d]</li> <li>dp 的值代表对应的 x 坐标 (相应可以推算出 y 坐标)</li> </ul> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"> | 0 1 2 3 4 5 </span></span><span class="line"><span class="cl">----+-------------------------------------- </span></span><span class="line"><span class="cl"> | </span></span><span class="line"><span class="cl"> 4 | 7,3 </span></span><span class="line"><span class="cl"> | / </span></span><span class="line"><span class="cl"> 3 | 5,2 </span></span><span class="line"><span class="cl"> | / </span></span><span class="line"><span class="cl"> 2 | [3,1] [7,5] </span></span><span class="line"><span class="cl"> | / \ / \ </span></span><span class="line"><span class="cl"> 1 | [1,0] [5,4] [7,6] </span></span><span class="line"><span class="cl"> | / \ \ </span></span><span class="line"><span class="cl"> 0 | [0,0] 2,2 5,5 </span></span><span class="line"><span class="cl"> | \ \ </span></span><span class="line"><span class="cl">-1 | 0,1 4,5 5,6 </span></span><span class="line"><span class="cl"> | \ / \ </span></span><span class="line"><span class="cl">-2 | 2,4 4,6 </span></span><span class="line"><span class="cl"> | \ </span></span><span class="line"><span class="cl">-3 | 3,6 </span></span></code></pre></td></tr></table> </div> </div><p>操作数为 d,行号为 k 的状态只能从相邻的两行 k-1 横线过来(x+1)或者 k+1 竖线过来(x不变)。</p> <p>因为整个数的结构是二叉的 ,奇数步数时必然处于奇数行号,偶数步数时必然处于偶数行号, 这是因为每进行一步 snake 时,只会有一次删除或插入操作,所以 k 的遍历步长是 2 。</p> <p>状态转移方程就是 dp[d][k] = max(dp[d-1][k-1]+1, dp[d-1][k+1]),即多个路径状选横坐标 x 最大的。</p> <p>由于操作数 d 的状态计算只依赖 d-1 的状态数组,所以可以压缩一下空间使用一维数组记录即可。</p> <p>伪代码</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">V[1]←0 </span></span><span class="line"><span class="cl">For D ← 0 to MAX Do </span></span><span class="line"><span class="cl"> For k ← −D to D in steps of 2 </span></span><span class="line"><span class="cl"> Do If k=−D or k≠D and V[k−1] &lt; V[k+1] Then </span></span><span class="line"><span class="cl"> x ← V[k+1] </span></span><span class="line"><span class="cl"> Else </span></span><span class="line"><span class="cl"> x ← V[k−1]+1 y ← x−k </span></span><span class="line"><span class="cl"> While x &lt; N and y &lt; M and a[x+1] = b[y+1] Do </span></span><span class="line"><span class="cl"> (x,y) ← (x+1,y+1) </span></span><span class="line"><span class="cl"> V[k] ← x </span></span><span class="line"><span class="cl"> If x ≥ N and y ≥ M Then </span></span><span class="line"><span class="cl"> Length of an SES is D </span></span><span class="line"><span class="cl"> Stop </span></span></code></pre></td></tr></table> </div> </div><p>python 版代码</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">git_diff</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">a</span> <span class="o">=</span> <span class="s2">&#34; &#34;</span> <span class="o">+</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"> <span class="n">b</span> <span class="o">=</span> <span class="s2">&#34; &#34;</span> <span class="o">+</span> <span class="n">b</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">d</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">break_out</span> <span class="o">=</span> <span class="kc">False</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">d</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="n">d</span><span class="p">,</span> <span class="n">d</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span> <span class="o">==</span> <span class="o">-</span><span class="n">d</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="n">v</span><span class="p">[</span><span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="n">k</span> <span class="o">==</span> <span class="n">d</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="n">v</span><span class="p">[</span><span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span><span class="o">+</span><span class="mi">1</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="n">v</span><span class="p">[</span><span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">k</span> <span class="o">+</span> <span class="mi">1</span><span class="p">],</span> <span class="n">v</span><span class="p">[</span><span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">k</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">a</span><span class="p">[</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">b</span><span class="p">[</span><span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">break_out</span> <span class="o">=</span> <span class="kc">True</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span> <span class="n">x</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">v</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="p">(</span><span class="n">k</span><span class="o">+</span><span class="n">d</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">break_out</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">d</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第一讲-动态数组-vector">第一讲 动态数组 vector</h1> <h2 id="数组与内存">数组与内存</h2> <p>静态数组是由相同类型的元素线性排列的数据结构,在计算机上会分配一段连续的内存, 对元素进行顺序存储。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/1_1.webp" alt="static_array"></p> <p>静态数组的插入和 删除的平均复杂度是 O(N)</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/1_2.webp" alt="array_insert"></p> <p>静态数组的元素个数是事先确定的</p> <h2 id="动态数组">动态数组</h2> <p>动态数组将扩容的逻辑分装起来,方便用户使用</p> <h3 id="vector-源码分析">Vector 源码分析</h3> <p>vector 中有三个关键指针</p> <ul> <li>_start: 指向 vector 第一个元素</li> <li>_finish: 指向 vector 最后一个元素</li> <li>_end: 指向 vector 预留容量的边界</li> </ul> <p><img src="https://ppd0705.github.io/image/algo_in_action/1_3.webp" alt="vector_pointers"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">_Tp</span><span class="p">,</span> <span class="k">class</span> <span class="nc">_Alloc</span> <span class="o">=</span> <span class="n">__STL_DEFAULT_ALLOCATOR</span><span class="p">(</span><span class="n">_Tp</span><span class="p">)</span> <span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">vector</span> <span class="o">:</span> <span class="k">protected</span> <span class="n">_Vector_base</span><span class="o">&lt;</span><span class="n">_Tp</span><span class="p">,</span> <span class="n">_Alloc</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="k">protected</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">_Tp</span><span class="o">*</span> <span class="n">_M_start</span><span class="p">;</span> <span class="c1">//表示目前使用空间的头 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">_Tp</span><span class="o">*</span> <span class="n">_M_finish</span><span class="p">;</span> <span class="c1">//表示目前使用空间的尾 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">_Tp</span><span class="o">*</span> <span class="n">_M_end_of_storage</span><span class="p">;</span> <span class="c1">//表示目前可用空间的尾 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><p>扩容实现源码</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">push_back</span><span class="p">(</span><span class="k">const</span> <span class="n">_Tp</span><span class="o">&amp;</span> <span class="n">__x</span><span class="p">)</span> <span class="p">{</span><span class="c1">//在最尾端插入元素 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">_M_finish</span> <span class="o">!=</span> <span class="n">_M_end_of_storage</span><span class="p">)</span> <span class="p">{</span><span class="c1">//若有可用的内存空间 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">construct</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">,</span> <span class="n">__x</span><span class="p">);</span><span class="c1">//构造对象 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">++</span><span class="n">_M_finish</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="c1">//若没有可用的内存空间,调用以下函数,把x插入到指定位置 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">_M_insert_aux</span><span class="p">(</span><span class="n">end</span><span class="p">(),</span> <span class="n">__x</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">_Tp</span><span class="p">,</span> <span class="k">class</span> <span class="nc">_Alloc</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> </span></span><span class="line"><span class="cl"> <span class="n">vector</span><span class="o">&lt;</span><span class="n">_Tp</span><span class="p">,</span> <span class="n">_Alloc</span><span class="o">&gt;::</span><span class="n">_M_insert_aux</span><span class="p">(</span><span class="n">iterator</span> <span class="n">__position</span><span class="p">,</span> <span class="k">const</span> <span class="n">_Tp</span><span class="o">&amp;</span> <span class="n">__x</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">_M_finish</span> <span class="o">!=</span> <span class="n">_M_end_of_storage</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">construct</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">_M_finish</span> <span class="o">-</span> <span class="mi">1</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">_M_finish</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">_Tp</span> <span class="n">__x_copy</span> <span class="o">=</span> <span class="n">__x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">copy_backward</span><span class="p">(</span><span class="n">__position</span><span class="p">,</span> <span class="n">_M_finish</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="n">_M_finish</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">__position</span> <span class="o">=</span> <span class="n">__x_copy</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">size_type</span> <span class="n">__old_size</span> <span class="o">=</span> <span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">size_type</span> <span class="n">__len</span> <span class="o">=</span> <span class="n">__old_size</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">2</span> <span class="o">*</span> <span class="nl">__old_size</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">iterator</span> <span class="n">__new_start</span> <span class="o">=</span> <span class="n">_M_allocate</span><span class="p">(</span><span class="n">__len</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">iterator</span> <span class="n">__new_finish</span> <span class="o">=</span> <span class="n">__new_start</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">__STL_TRY</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">__new_finish</span> <span class="o">=</span> <span class="n">uninitialized_copy</span><span class="p">(</span><span class="n">_M_start</span><span class="p">,</span> <span class="n">__position</span><span class="p">,</span> <span class="n">__new_start</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">construct</span><span class="p">(</span><span class="n">__new_finish</span><span class="p">,</span> <span class="n">__x</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">__new_finish</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">__new_finish</span> <span class="o">=</span> <span class="n">uninitialized_copy</span><span class="p">(</span><span class="n">__position</span><span class="p">,</span> <span class="n">_M_finish</span><span class="p">,</span> <span class="n">__new_finish</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">__STL_UNWIND</span><span class="p">((</span><span class="n">destroy</span><span class="p">(</span><span class="n">__new_start</span><span class="p">,</span><span class="n">__new_finish</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_deallocate</span><span class="p">(</span><span class="n">__new_start</span><span class="p">,</span><span class="n">__len</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">destroy</span><span class="p">(</span><span class="n">begin</span><span class="p">(),</span> <span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_deallocate</span><span class="p">(</span><span class="n">_M_start</span><span class="p">,</span> <span class="n">_M_end_of_storage</span> <span class="o">-</span> <span class="n">_M_start</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_start</span> <span class="o">=</span> <span class="n">__new_start</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_finish</span> <span class="o">=</span> <span class="n">__new_finish</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_end_of_storage</span> <span class="o">=</span> <span class="n">__new_start</span> <span class="o">+</span> <span class="n">__len</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><ul> <li>22-25 行,主要做的事情就是读取原有的 vector 大小 old_size,再从内存里申请一段新的空间,大小为 2*old_size,创建新的首尾指针并指向新的空间。</li> <li>26-31 行,将老空间里的数据逐一搬到新的空间里,并在最后添加新的元素。这样就完成了扩容的主要目的,这是一个 O(n) 复杂度的操作,因为你需要对原数组进行逐一的深拷贝。</li> <li>最后,在 32-38 行,我们需要做一些清理和收尾工作,释放掉老的数组空间和指针,将容器的首尾及容量指针都更新到对应的位置。</li> </ul> <p>假设插入 N 次, 那么总拷贝次数就是 1 加到 2 x 次方, x 是 logn x向上取整</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">1 + 2 + 4 + 8 + … + 2 ** x = 2 ** (x+1) − 1 </span></span></code></pre></td></tr></table> </div> </div><p>这样插入 N 次的复杂度为 O(N), 平均每次插入操作复杂度为 O(1).</p> <h1 id="第二讲-双向链表-list">第二讲 双向链表 list</h1> <h2 id="链表简介">链表简介</h2> <p>链表同样是一种序列式结构,但无需连续内存,使用通过指针相连的节点来存储。 每个节点分为两个部分,一个是数据域 data field,另外一个是 引用域 reference field。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/2_1.webp" alt="linked_list"></p> <p>因为通过 指针串联 ,所以在任何位置插入或者删除节点,只需要 O(1) 的复杂度。</p> <h2 id="链表种类">链表种类</h2> <p>链表通常有三种形式:单向链表、双向链表、循环链表</p> <h2 id="stl-list-实现">STL list 实现</h2> <p>list 是 STL 中的链表容器,实现了双向循环链表</p> <h3 id="节点实现">节点实现</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">__list_node</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">__list_node</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;*</span> <span class="n">next</span><span class="p">;</span> <span class="c1">// 前驱节点指针 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">__list_node</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;*</span> <span class="n">prev</span><span class="p">;</span> <span class="c1">// 后继节点指针 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">T</span> <span class="n">data</span><span class="p">;</span> <span class="c1">//存储数据 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="迭代器实现">迭代器实现</h3> <p>迭代器提供用于遍历的最重要的接口,支持的操作就是自增和增减</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">__list_iterator</span><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">__list_iterator</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">self</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">__list_node</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;*</span> <span class="n">link_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">link_type</span> <span class="n">ptr</span><span class="p">;</span> <span class="c1">//成员 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">__list_iterator</span><span class="p">(</span><span class="n">link_type</span> <span class="n">p</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">)</span><span class="o">:</span><span class="n">ptr</span><span class="p">(</span><span class="n">p</span><span class="p">){}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">T</span><span class="o">&amp;</span> <span class="k">operator</span> <span class="o">*</span><span class="p">(){</span><span class="k">return</span> <span class="n">ptr</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;}</span> </span></span><span class="line"><span class="cl"><span class="n">T</span><span class="o">*</span> <span class="k">operator</span> <span class="o">-&gt;</span><span class="p">(){</span><span class="k">return</span> <span class="o">&amp;</span><span class="p">(</span><span class="k">operator</span><span class="o">*</span><span class="p">());}</span> </span></span><span class="line"><span class="cl"><span class="c1">// 类似 ++x 返回next节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">self</span><span class="o">&amp;</span> <span class="k">operator</span><span class="o">++</span><span class="p">(){</span> </span></span><span class="line"><span class="cl"> <span class="n">ptr</span> <span class="o">=</span> <span class="n">ptr</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="c1">// 类似 x++ 返回当前节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">self</span> <span class="k">operator</span><span class="o">++</span><span class="p">(</span><span class="kt">int</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="n">self</span> <span class="n">tmp</span> <span class="o">=</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="o">++*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">tmp</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="c1">// 类似 --x 返回prev节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">self</span><span class="o">&amp;</span> <span class="k">operator</span><span class="o">--</span><span class="p">(){</span> </span></span><span class="line"><span class="cl"> <span class="n">ptr</span> <span class="o">=</span> <span class="n">ptr</span><span class="o">-&gt;</span><span class="n">prev</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="c1">// 类似 x-- 返回当前节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">self</span> <span class="k">operator</span><span class="o">--</span><span class="p">(</span><span class="kt">int</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="n">self</span> <span class="n">tmp</span> <span class="o">=</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="o">--*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">tmp</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="k">operator</span><span class="o">==</span><span class="p">(</span><span class="k">const</span> <span class="n">__list_iterator</span><span class="o">&amp;</span> <span class="n">rhs</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ptr</span> <span class="o">==</span> <span class="n">rhs</span><span class="p">.</span><span class="n">ptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="k">operator</span><span class="o">!=</span><span class="p">(</span><span class="k">const</span> <span class="n">__list_iterator</span><span class="o">&amp;</span> <span class="n">rhs</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">!</span><span class="p">(</span><span class="o">*</span><span class="k">this</span><span class="o">==</span><span class="n">rhs</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="数据结构实现">数据结构实现</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">list</span><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">protected</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">__list_node</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">list_node</span><span class="p">;</span> <span class="c1">// 显示定义list_node类型 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">typedef</span> <span class="n">allocator</span><span class="o">&lt;</span><span class="n">list_node</span><span class="o">&gt;</span> <span class="n">nodeAllocator</span><span class="p">;</span> <span class="c1">// 定义allocator类型 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">T</span> <span class="n">value_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">reference</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">value_type</span><span class="o">*</span> <span class="n">pointer</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">list_node</span><span class="o">*</span> <span class="n">link_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="k">const</span> <span class="n">value_type</span><span class="o">*</span> <span class="n">const_pointer</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">size_t</span> <span class="n">size_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">__list_iterator</span><span class="o">&lt;</span><span class="n">value_type</span><span class="o">&gt;</span> <span class="n">iterator</span><span class="p">;</span> <span class="c1">// 迭代器类型重写 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">link_type</span> <span class="n">node</span><span class="p">;</span> <span class="c1">// 只要一个指针,便可表示整个环状双向链表 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ...... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>每个链表,都会有一个虚拟节点,用于标记整个循环链表的首尾连接处,它是链表的开始,也是链表的结尾,</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/2_2.webp" alt="linked_list2"></p> <p>迭代示例代码:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">iterator</span> <span class="n">it</span><span class="o">=</span><span class="n">mylist</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">it</span><span class="o">!=</span> <span class="n">mylist</span><span class="p">.</span><span class="n">end</span><span class="p">();</span><span class="o">++</span><span class="n">it</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="sc">&#39; &#39;</span> <span class="o">&lt;&lt;</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="基本操作实现">基本操作实现</h3> <p>初始化</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">empty_initialize</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span> <span class="o">=</span> <span class="n">get_node</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">node</span><span class="p">;</span> <span class="c1">// next 指针指向自身 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">node</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="n">node</span><span class="p">;</span> <span class="c1">// prev 指针指向自身 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">link_type</span> <span class="nf">get_node</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nl">list_node_allocator</span><span class="p">:</span><span class="n">allocate</span><span class="p">();</span> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>插入</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">iterator</span> <span class="nf">insert</span><span class="p">(</span><span class="n">iterator</span> <span class="n">position</span><span class="p">,</span> <span class="k">const</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">lik_type</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">create_node</span><span class="p">(</span><span class="n">x</span><span class="p">);</span> <span class="c1">// 创建一个临时节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">tmp</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="p">;</span> <span class="c1">// 将该节点的后继指针指向当前位置的节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">tmp</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="o">-&gt;</span><span class="n">prev</span><span class="p">;</span> <span class="c1">// 将该节点的前驱指针指向当前位置的前驱节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">(</span><span class="n">link_type</span><span class="p">(</span><span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="o">-&gt;</span><span class="n">prev</span><span class="p">))</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">tmp</span><span class="p">;</span> <span class="c1">// 将前驱节点本来指向当前节点的后继指针改为指向该临时节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="n">tmp</span><span class="p">;</span> <span class="c1">// 同样,当前位置的前驱指针也要修改为指向该临时节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="n">tmp</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>删除</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="n">iterator</span> <span class="nf">erase</span><span class="p">(</span><span class="n">iterator</span> <span class="n">position</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">link_type</span> <span class="n">next_node</span> <span class="o">=</span> <span class="n">link_type</span><span class="p">(</span><span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">link_type</span> <span class="n">prev_node</span> <span class="o">=</span> <span class="n">link_type</span><span class="p">(</span><span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="o">-&gt;</span><span class="n">prev</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">prev_node</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">next_node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">next_node</span><span class="o">-&gt;</span><span class="n">prev</span> <span class="o">=</span> <span class="n">prev_node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">destroy_node</span><span class="p">(</span><span class="n">position</span><span class="p">.</span><span class="n">node</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">iterator</span><span class="p">(</span><span class="n">next_node</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>push / pop</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">pop_front</span><span class="p">()</span> <span class="p">{</span> <span class="n">erase</span><span class="p">(</span><span class="n">begin</span><span class="p">())</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">pop_back</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">iterator</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">end</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">erase</span><span class="p">(</span><span class="o">--</span><span class="n">tmp</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">push_back</span><span class="p">(</span><span class="k">const</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="n">insert</span><span class="p">(</span><span class="n">end</span><span class="p">(),</span> <span class="n">x</span><span class="p">);</span> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第三讲-双端队列">第三讲 双端队列</h1> <h2 id="队列">队列</h2> <p>队列是一种先进先出的序列式数据结构</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/3_1.webp" alt="queue"></p> <h2 id="双端队列">双端队列</h2> <p>双端队列可以在队列的两端都可以进行出队和入队操作</p> <h2 id="deque-实现">Deque 实现</h2> <h3 id="内存布局">内存布局</h3> <p>deque 的内存布局是由一段段连续的空间, 用另一个类似数组的东西将这些空间的地址信息拼接在一起组成的。 在首尾插入和删除数据的复杂度都是 O(1)</p> <p>deque 既不像 vector 那样每次扩容都需要付出复制和拷贝的高昂代价, 也不会像 list 那样每次插入一个新节点都需要申请一次内存。</p> <p>deque 使用 map 变量来管理真正用于存储队列元素的一段段连续线性空间</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/3_2.webp" alt="deque"></p> <p>可以认为 map 是一个数组,每个元素指向了一段缓冲区地址,而缓冲区对应了一段指定大小的连续内存空间,默认大小为 512 bytes</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">_Tp</span><span class="p">,</span> <span class="k">class</span> <span class="nc">_Alloc</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">_Deque_base</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="k">protected</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">_Tp</span><span class="o">**</span> <span class="n">_M_map</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">_M_map_size</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">iterator</span> <span class="n">_M_start</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">iterator</span> <span class="n">_M_finish</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="迭代器">迭代器</h3> <p>因为 deque 底层实质是分段连续空间,迭代器需要找到相邻的缓冲区以及当前所处位置</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">_Tp</span><span class="p">,</span> <span class="k">class</span> <span class="nc">_Ref</span><span class="p">,</span> <span class="k">class</span> <span class="nc">_Ptr</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">_Deque_iterator</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">_Deque_iterator</span><span class="o">&lt;</span><span class="n">_Tp</span><span class="p">,</span> <span class="n">_Tp</span><span class="o">&amp;</span><span class="p">,</span> <span class="n">_Tp</span><span class="o">*&gt;</span> <span class="n">iterator</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">_Deque_iterator</span><span class="o">&lt;</span><span class="n">_Tp</span><span class="p">,</span> <span class="k">const</span> <span class="n">_Tp</span><span class="o">&amp;</span><span class="p">,</span> <span class="k">const</span> <span class="n">_Tp</span><span class="o">*&gt;</span> <span class="n">const_iterator</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">static</span> <span class="n">size_t</span> <span class="nf">_S_buffer_size</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">__deque_buf_size</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">_Tp</span><span class="p">));</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">...</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">_Tp</span><span class="o">**</span> <span class="n">_Map_pointer</span><span class="p">;</span> <span class="c1">// 缓冲区指针 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">...</span> </span></span><span class="line"><span class="cl"> <span class="n">_Tp</span><span class="o">*</span> <span class="n">_M_cur</span><span class="p">;</span> <span class="c1">// 当前缓冲区的位置 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">_Tp</span><span class="o">*</span> <span class="n">_M_first</span><span class="p">;</span> <span class="c1">// 缓冲区的左边界线 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">_Tp</span><span class="o">*</span> <span class="n">_M_last</span><span class="p">;</span> <span class="c1">// 缓冲区的右边界 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">_Map_pointer</span> <span class="n">_M_node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">_Deque_iterator</span><span class="p">(</span><span class="n">_Tp</span><span class="o">*</span> <span class="n">__x</span><span class="p">,</span> <span class="n">_Map_pointer</span> <span class="n">__y</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">_M_cur</span><span class="p">(</span><span class="n">__x</span><span class="p">),</span> <span class="n">_M_first</span><span class="p">(</span><span class="o">*</span><span class="n">__y</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_last</span><span class="p">(</span><span class="o">*</span><span class="n">__y</span> <span class="o">+</span> <span class="n">_S_buffer_size</span><span class="p">()),</span> <span class="n">_M_node</span><span class="p">(</span><span class="n">__y</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">_Self</span><span class="o">&amp;</span> <span class="k">operator</span><span class="o">++</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">_M_cur</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">_M_cur</span> <span class="o">==</span> <span class="n">_M_last</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_set_node</span><span class="p">(</span><span class="n">_M_node</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_cur</span> <span class="o">=</span> <span class="n">_M_first</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">_M_set_node</span><span class="p">(</span><span class="n">_Map_pointer</span> <span class="n">__new_node</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_node</span> <span class="o">=</span> <span class="n">__new_node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_first</span> <span class="o">=</span> <span class="o">*</span><span class="n">__new_node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_last</span> <span class="o">=</span> <span class="n">_M_first</span> <span class="o">+</span> <span class="n">difference_type</span><span class="p">(</span><span class="n">_S_buffer_size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">_Self</span><span class="o">&amp;</span> <span class="k">operator</span><span class="o">--</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">_M_cur</span> <span class="o">==</span> <span class="n">_M_first</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_set_node</span><span class="p">(</span><span class="n">_M_node</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_cur</span> <span class="o">=</span> <span class="n">_M_last</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="o">--</span><span class="n">_M_cur</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="k">this</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span></code></pre></td></tr></table> </div> </div><h3 id="基础操作">基础操作</h3> <h4 id="push-操作">push 操作</h4> <ol> <li>如果当前缓冲区有空间,直接插入</li> <li>如果 map 有空间,则新建一个缓冲区存入map</li> <li>如果 map 空间不足,则分为两种情况 <ul> <li>map 使用率过半,就申请更大的空间,将老的 map 数据拷贝到新区域,map 指向的缓冲区不变</li> <li>map 使用率小于一半,将数据重新调整到 map 中间的位置</li> </ul> </li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">push_back</span><span class="p">(</span><span class="k">const</span> <span class="n">value_type</span><span class="o">&amp;</span> <span class="n">__t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_cur</span> <span class="o">!=</span> <span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_last</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">construct</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_cur</span><span class="p">,</span> <span class="n">__t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_cur</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_push_back_aux</span><span class="p">(</span><span class="n">__t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">_Tp</span><span class="p">,</span> <span class="k">class</span> <span class="nc">_Alloc</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">deque</span><span class="o">&lt;</span><span class="n">_Tp</span><span class="p">,</span><span class="n">_Alloc</span><span class="o">&gt;::</span><span class="n">_M_push_back_aux</span><span class="p">()</span> </span></span><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_reserve_map_at_back</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_node</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">=</span> <span class="n">_M_allocate_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">__STL_TRY</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">construct</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_cur</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_set_node</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_node</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_cur</span> <span class="o">=</span> <span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_first</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">__STL_UNWIND</span><span class="p">(</span><span class="n">_M_deallocate_node</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">_M_finish</span><span class="p">.</span><span class="n">_M_node</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h4 id="pop--操作">pop 操作</h4> <p>如果迭代器和缓冲区的首位置相同,则除了释放当前的内存,还需要释放掉整段缓冲区的内存。</p> <h1 id="第四讲-栈">第四讲 栈</h1> <p>栈区是有结构和固定大小的,区块按照次序存放,每个线程独占一个栈区,总大小也是事先确定的。</p> <h2 id="特性-lifo">特性: LIFO</h2> <p>栈只能从栈顶删除或者插入元素,所以保证了后进先出的特性</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/4_1.webp" alt="stack"></p> <h2 id="stl-实现">STL 实现</h2> <p>STL 中使用了 deque 来实现 stack ,这种封装模式称为适配器模式,stack 也被看做是一种 container adapter</p> <h3 id="数据结构定义">数据结构定义</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">_Tp</span><span class="p">,</span> <span class="k">typename</span> <span class="n">_Sequence</span> <span class="o">=</span> <span class="n">deque</span><span class="o">&lt;</span><span class="n">_Tp</span><span class="o">&gt;</span> <span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">stack</span> </span></span><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="k">typename</span> <span class="n">_Sequence</span><span class="o">::</span><span class="n">value_type</span> <span class="n">value_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="k">typename</span> <span class="n">_Sequence</span><span class="o">::</span><span class="n">reference</span> <span class="n">reference</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="k">typename</span> <span class="n">_Sequence</span><span class="o">::</span><span class="n">const_reference</span> <span class="n">const_reference</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="k">typename</span> <span class="n">_Sequence</span><span class="o">::</span><span class="n">size_type</span> <span class="n">size_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">typedef</span> <span class="n">_Sequence</span> <span class="n">container_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">protected</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">_Sequence</span> <span class="n">c</span><span class="p">;</span> <span class="c1">// stack 底层容器; 默认为 deque </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">reference</span> </span></span><span class="line"><span class="cl"> <span class="n">top</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">__glibcxx_requires_nonempty</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">c</span><span class="p">.</span><span class="n">back</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">push</span><span class="p">(</span><span class="k">const</span> <span class="n">value_type</span><span class="o">&amp;</span> <span class="n">__x</span><span class="p">)</span> <span class="p">{</span> <span class="n">c</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">__x</span><span class="p">);</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">pop</span><span class="p">()</span> <span class="p">{</span><span class="n">c</span><span class="p">.</span><span class="n">pop_back</span><span class="p">();}</span> </span></span><span class="line"><span class="cl"> <span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第五讲-hashmap">第五讲 HashMap</h1> <h2 id="散列">散列</h2> <p>散列函数的本质是将一个更大且可能不连续空间,映射到一个空间有限的数组里, 从而借用数组基于下标 O(1) 快速随机访问数组元素的能力。</p> <p>但设置一个合理的散列函数是个非常有挑战的事情,比如 26 进制的散列函数就有一个巨大的缺陷, 就是它所需的数组空间太大了,三位长度的元素就需要开一个接近 20000 (26^3)大小的计数数组。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/5_1.webp" alt="hashmap1"></p> <p>这时我们需要对哈希值进行 取模运算,但取模之后会遇到一个新问题:哈希碰撞。</p> <h2 id="jdk-实现">JDK 实现</h2> <h3 id="hash-方法">hash 方法</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="nf">hash</span><span class="o">(</span><span class="n">Object</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">h</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">(</span><span class="n">key</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="o">(</span><span class="n">h</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="na">hashCode</span><span class="o">())</span> <span class="o">^</span> <span class="o">(</span><span class="n">h</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">16</span><span class="o">);</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>String 类型的 hashCode 方法</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">int</span> <span class="nf">hashCode</span><span class="o">()</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">h</span> <span class="o">=</span> <span class="n">hash</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">h</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">hashIsZero</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">h</span> <span class="o">=</span> <span class="n">isLatin1</span><span class="o">()</span> <span class="o">?</span> <span class="n">StringLatin1</span><span class="o">.</span><span class="na">hashCode</span><span class="o">(</span><span class="n">value</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">StringUTF16</span><span class="o">.</span><span class="na">hashCode</span><span class="o">(</span><span class="n">value</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">h</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">hashIsZero</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">hash</span> <span class="o">=</span> <span class="n">h</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">h</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>StringUTF16 hashCode 实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">hashCode</span><span class="o">(</span><span class="kt">byte</span><span class="o">[]</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">h</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">value</span><span class="o">.</span><span class="na">length</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">h</span> <span class="o">=</span> <span class="mi">31</span> <span class="o">*</span> <span class="n">h</span> <span class="o">+</span> <span class="n">getChar</span><span class="o">(</span><span class="n">value</span><span class="o">,</span> <span class="n">i</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">h</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// like s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] </span></span></span></code></pre></td></tr></table> </div> </div><p>在哈希计算时倾向于用奇素数进行乘法运算,因为使用偶数时相当于进行移位运算,部分位的信息丢失可能增加哈希冲突概率。</p> <p>使用 31 是因为 31 * i 编译器可以自动转换成 (i &laquo; 5) - i</p> <h3 id="-h--16">^ h &raquo;&gt; 16</h3> <p><code>^ h &gt;&gt;&gt; 16</code> 把 高 16位的 信息叠加到低16位,这样在取模的时候就可以用到高位信息, 减少冲突概率。</p> <h3 id="table-实现">table 实现</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">transient</span> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;[]</span> <span class="n">table</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="kd">final</span> <span class="n">V</span> <span class="nf">putVal</span><span class="o">(</span><span class="kt">int</span> <span class="n">hash</span><span class="o">,</span> <span class="n">K</span> <span class="n">key</span><span class="o">,</span> <span class="n">V</span> <span class="n">value</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">onlyIfAbsent</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="kt">boolean</span> <span class="n">evict</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;[]</span> <span class="n">tab</span><span class="o">;</span> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;</span> <span class="n">p</span><span class="o">;</span> <span class="kt">int</span> <span class="n">n</span><span class="o">,</span> <span class="n">i</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">//在tab尚未初始化、或者对应槽位链表未初始化时,进行相应的初始化操作 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="o">((</span><span class="n">tab</span> <span class="o">=</span> <span class="n">table</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">(</span><span class="n">n</span> <span class="o">=</span> <span class="n">tab</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="o">(</span><span class="n">tab</span> <span class="o">=</span> <span class="n">resize</span><span class="o">()).</span><span class="na">length</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">((</span><span class="n">p</span> <span class="o">=</span> <span class="n">tab</span><span class="o">[</span><span class="n">i</span> <span class="o">=</span> <span class="o">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="o">&amp;</span> <span class="n">hash</span><span class="o">])</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">tab</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">(</span><span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;</span> <span class="n">e</span><span class="o">;</span> <span class="n">K</span> <span class="n">k</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 查找 key 对应的节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="o">(</span><span class="n">p</span><span class="o">.</span><span class="na">hash</span> <span class="o">==</span> <span class="n">hash</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="o">((</span><span class="n">k</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">key</span><span class="o">)</span> <span class="o">==</span> <span class="n">key</span> <span class="o">||</span> <span class="o">(</span><span class="n">key</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">key</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">k</span><span class="o">))))</span> </span></span><span class="line"><span class="cl"> <span class="n">e</span> <span class="o">=</span> <span class="n">p</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">p</span> <span class="k">instanceof</span> <span class="n">TreeNode</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">e</span> <span class="o">=</span> <span class="o">((</span><span class="n">TreeNode</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;)</span><span class="n">p</span><span class="o">).</span><span class="na">putTreeVal</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">tab</span><span class="o">,</span> <span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 遍历所有节点 依次查找 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">binCount</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="o">;</span> <span class="o">++</span><span class="n">binCount</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">next</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">p</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">(</span><span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">binCount</span> <span class="o">&gt;=</span> <span class="n">TREEIFY_THRESHOLD</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="c1">// -1 for 1st </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">treeifyBin</span><span class="o">(</span><span class="n">tab</span><span class="o">,</span> <span class="n">hash</span><span class="o">);</span> <span class="c1">// 链表转化成红黑树 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">break</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">==</span> <span class="n">hash</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="o">((</span><span class="n">k</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">key</span><span class="o">)</span> <span class="o">==</span> <span class="n">key</span> <span class="o">||</span> <span class="o">(</span><span class="n">key</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">key</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">k</span><span class="o">))))</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">p</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// existing mapping for key </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">V</span> <span class="n">oldValue</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">value</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(!</span><span class="n">onlyIfAbsent</span> <span class="o">||</span> <span class="n">oldValue</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">e</span><span class="o">.</span><span class="na">value</span> <span class="o">=</span> <span class="n">value</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">afterNodeAccess</span><span class="o">(</span><span class="n">e</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">oldValue</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span><span class="n">modCount</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(++</span><span class="n">size</span> <span class="o">&gt;</span> <span class="n">threshold</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">resize</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="n">afterNodeInsertion</span><span class="o">(</span><span class="n">evict</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>table 是经过散列之后映射到的内部连续数组,通过 hash 函数的计算,我们可以 基于数组的下标快速访问到 key 对应的元素, 元素存储的是 Node 类型。 Node 的具体实现可以是链表或者红黑树,当链表足够长时,会将链表转化成红黑树。</p> <p>当table 中元素数量达到阈值时,会触发扩容逻辑</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">final</span> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;[]</span> <span class="nf">resize</span><span class="o">()</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;[]</span> <span class="n">oldTab</span> <span class="o">=</span> <span class="n">table</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">oldCap</span> <span class="o">=</span> <span class="o">(</span><span class="n">oldTab</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">oldTab</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">oldThr</span> <span class="o">=</span> <span class="n">threshold</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">newCap</span><span class="o">,</span> <span class="n">newThr</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">oldCap</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">oldCap</span> <span class="o">&gt;=</span> <span class="n">MAXIMUM_CAPACITY</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">threshold</span> <span class="o">=</span> <span class="n">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">oldTab</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">if</span> <span class="o">((</span><span class="n">newCap</span> <span class="o">=</span> <span class="n">oldCap</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">MAXIMUM_CAPACITY</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="n">oldCap</span> <span class="o">&gt;=</span> <span class="n">DEFAULT_INITIAL_CAPACITY</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">newThr</span> <span class="o">=</span> <span class="n">oldThr</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="o">;</span> <span class="c1">// 翻倍扩容 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">oldThr</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="c1">// 初始化的时候 capacity 设置为初始化阈值 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">newCap</span> <span class="o">=</span> <span class="n">oldThr</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="o">{</span> <span class="c1">// 没有初始化 采用默认值 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">newCap</span> <span class="o">=</span> <span class="n">DEFAULT_INITIAL_CAPACITY</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">newThr</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)(</span><span class="n">DEFAULT_LOAD_FACTOR</span> <span class="o">*</span> <span class="n">DEFAULT_INITIAL_CAPACITY</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">newThr</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">float</span> <span class="n">ft</span> <span class="o">=</span> <span class="o">(</span><span class="kt">float</span><span class="o">)</span><span class="n">newCap</span> <span class="o">*</span> <span class="n">loadFactor</span><span class="o">;</span> <span class="c1">// 用容量乘负载因子表示扩容阈值 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">newThr</span> <span class="o">=</span> <span class="o">(</span><span class="n">newCap</span> <span class="o">&lt;</span> <span class="n">MAXIMUM_CAPACITY</span> <span class="o">&amp;&amp;</span> <span class="n">ft</span> <span class="o">&lt;</span> <span class="o">(</span><span class="kt">float</span><span class="o">)</span><span class="n">MAXIMUM_CAPACITY</span> <span class="o">?</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="kt">int</span><span class="o">)</span><span class="n">ft</span> <span class="o">:</span> <span class="n">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="n">threshold</span> <span class="o">=</span> <span class="n">newThr</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="nd">@SuppressWarnings</span><span class="o">({</span><span class="s">&#34;rawtypes&#34;</span><span class="o">,</span><span class="s">&#34;unchecked&#34;</span><span class="o">})</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;[]</span> <span class="n">newTab</span> <span class="o">=</span> <span class="o">(</span><span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;[])</span><span class="k">new</span> <span class="n">Node</span><span class="o">[</span><span class="n">newCap</span><span class="o">];</span> </span></span><span class="line"><span class="cl"> <span class="n">table</span> <span class="o">=</span> <span class="n">newTab</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">oldTab</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">oldCap</span><span class="o">;</span> <span class="o">++</span><span class="n">j</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">oldTab</span><span class="o">[</span><span class="n">j</span><span class="o">])</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">oldTab</span><span class="o">[</span><span class="n">j</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">next</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">newTab</span><span class="o">[</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">&amp;</span> <span class="o">(</span><span class="n">newCap</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)]</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="k">instanceof</span> <span class="n">TreeNode</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">((</span><span class="n">TreeNode</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;)</span><span class="n">e</span><span class="o">).</span><span class="na">split</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">newTab</span><span class="o">,</span> <span class="n">j</span><span class="o">,</span> <span class="n">oldCap</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 新扩容部分,标识为hi,原来的部分标识为lo </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// JDK 1.8 之后引入用于解决多线程死循环问题 可参考:https://stackoverflow.com/questions/35534906/java-hashmap-getobject-infinite-loop </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;</span> <span class="n">loHead</span> <span class="o">=</span> <span class="kc">null</span><span class="o">,</span> <span class="n">loTail</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;</span> <span class="n">hiHead</span> <span class="o">=</span> <span class="kc">null</span><span class="o">,</span> <span class="n">hiTail</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span><span class="o">&lt;</span><span class="n">K</span><span class="o">,</span><span class="n">V</span><span class="o">&gt;</span> <span class="n">next</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 整体操作就是将j所对应的链表拆成两个部分 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 分别放到 j 和 j + oldCap 的槽位 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">do</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">next</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">next</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">((</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">&amp;</span> <span class="n">oldCap</span><span class="o">)</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">loTail</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">loHead</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="n">loTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">loTail</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">hiTail</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">hiHead</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="n">hiTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">hiTail</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> <span class="k">while</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">next</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">loTail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">loTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">newTab</span><span class="o">[</span><span class="n">j</span><span class="o">]</span> <span class="o">=</span> <span class="n">loHead</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">hiTail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">hiTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">newTab</span><span class="o">[</span><span class="n">j</span> <span class="o">+</span> <span class="n">oldCap</span><span class="o">]</span> <span class="o">=</span> <span class="n">hiHead</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">newTab</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第六讲-红黑树">第六讲 红黑树</h1> <h2 id="二分查找树">二分查找树</h2> <p>每个节点的左节点,要么为空,要么比当前节点小;右节点要么为空,要么比当前节点大。</p> <p>当树的高度为 h 时,查找的时间复杂度为 O(h)</p> <h2 id="平衡二分查找树">平衡二分查找树</h2> <p>在一些情况下,二分查找树可能会退化成链表</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_1.webp" alt="binary_search_tree"></p> <p>平衡二分查找树的叶子节点高度差不超过 1</p> <h2 id="2-3-树">2-3 树</h2> <p>2-3 树是一种平衡查找树的实现,除了普通的 2 节点外,引入了 3 节点,这为自平衡增加了很大的灵活性。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_2.webp" alt="2_3_tree"></p> <p>3 节点在 2 节点的基础上增加了一个键,构成了一个有两个键和三条链的结构</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_3.webp" alt="2_3_tree"></p> <p>在插入过程中,如果遇到 2 节点,直接加个键将该节点升级为 3 节点就好。</p> <p>遇到 3 节点时,首先将元素放入该节点,使之成为 4 节点,然后将四节点的中间键提升到上层,</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_4.webp" alt="2_3_tree"></p> <h2 id="红黑树">红黑树</h2> <p>红黑树是采用标准的二叉查找树节点并附着颜色信息来表示 2-3 树的实现,每个 红色节点都和它的父节点一起构成了一个 3 节点。</p> <p>红黑树有如下几个约束:</p> <ol> <li>根节点为黑色</li> <li>相邻节点不能同时为红色</li> <li>每个节点到各个子节点的黑色节点数量相等</li> <li>红节点只能作为左节点存在(这是左偏红黑树的要求)</li> </ol> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_5.webp" alt="2_3_tree"></p> <p>一个 3 节点有两个键、三条链,那我们完全可以把一个以红节点为左子节点的黑节点和子节点一起看做一个 3 节点</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_6.webp" alt="2_3_tree"></p> <p>因为红节点只是 3 节点的一部分,那么对应到红黑树上,显然不会出现两个连续的红色节点。</p> <p>2-3 树上,每个节点到叶子节点的数量一定是一样的,且每个节点只包含一个黑色节点,那么 红黑树到叶子节点路径中的黑色节点数量也必然是一样的。</p> <h3 id="旋转操作">旋转操作</h3> <p>旋转的作用在于处理插入和删除时产生的右偏红节点或者两个连续的红节点。</p> <p>以左旋为例,本质就是将某个 3 节点从较小的键为根转移为较大的键为根</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_7.webp" alt="left_rotation"></p> <h3 id="2-节点插入">2 节点插入</h3> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_8.webp" alt="insert"></p> <p>当插入左边时,直接将 2 节点升为 3 节点即可</p> <p>当插入右边时,将 2 节点提升为不符合规则的 3 节点,然后进行一次左旋即可。</p> <h3 id="3-节点插入">3 节点插入</h3> <p>当插入右键的右侧时,将中间节点提升一层,并将左右节点变成黑色</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_9.webp" alt="insert"></p> <p>当插入中间或者左侧时,需要进行一到两次的旋转</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_10.webp" alt="insert"></p> <p>将三节点的左右节点变成黑色后,需要将中间键变成红色,这样当前子树到各个子节点路径的 黑色节点数量就不会变化了</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/6_11.webp" alt="insert"></p> <h1 id="第七讲-堆">第七讲 堆</h1> <h2 id="优先队列">优先队列</h2> <p>优先队列中的每一个元素,我们会赋予它一个优先级,优先级相同的元素遵循先进选出的原则 ,另有优先级高可以优先出队。</p> <p>优先队列可以有多种实现方式,如链表、红黑树,但对于出队时找到优先级最高的元素需求, 二叉堆是更好的选择。</p> <h2 id="二叉堆">二叉堆</h2> <p>二叉堆 binary heap 是建立在二叉树之上的,它有两个约束:</p> <ul> <li>二叉堆是一颗满二叉树</li> <li>二叉堆的每个节点和其子节点有偏序关系,大顶堆要求所有节点的值一定大于其左右子树的任何一个节点的值,小顶堆同理。</li> </ul> <h2 id="priorityqueue-实现">PriorityQueue 实现</h2> <p>以 JDK 14 的 PriorityQueue (简称 PQ)为例,分析基于堆的具体实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PriorityQueue</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;</span> <span class="kd">extends</span> <span class="n">AbstractQueue</span><span class="o">&lt;</span><span class="n">E</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="kd">implements</span> <span class="n">java</span><span class="o">.</span><span class="na">io</span><span class="o">.</span><span class="na">Serializable</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Priority queue represented as a balanced binary heap: the two </span></span></span><span class="line"><span class="cl"><span class="cm"> * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The </span></span></span><span class="line"><span class="cl"><span class="cm"> * priority queue is ordered by comparator, or by the elements&#39; </span></span></span><span class="line"><span class="cl"><span class="cm"> * natural ordering, if comparator is null: For each node n in the </span></span></span><span class="line"><span class="cl"><span class="cm"> * heap and each descendant d of n, n &lt;= d. The element with the </span></span></span><span class="line"><span class="cl"><span class="cm"> * lowest value is in queue[0], assuming the queue is nonempty. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> <span class="kd">transient</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">queue</span><span class="o">;</span> <span class="c1">// non-private to simplify nested class access </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * The number of elements in the priority queue. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">size</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Inserts the specified element into this priority queue. </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return {@code true} (as specified by {@link Queue#offer}) </span></span></span><span class="line"><span class="cl"><span class="cm"> * @throws ClassCastException if the specified element cannot be </span></span></span><span class="line"><span class="cl"><span class="cm"> * compared with elements currently in this priority queue </span></span></span><span class="line"><span class="cl"><span class="cm"> * according to the priority queue&#39;s ordering </span></span></span><span class="line"><span class="cl"><span class="cm"> * @throws NullPointerException if the specified element is null </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">offer</span><span class="o">(</span><span class="n">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="o">...</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Increases the capacity of the array. </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param minCapacity the desired minimum capacity </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">grow</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span> <span class="o">...</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="n">E</span> <span class="nf">peek</span><span class="o">()</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">(</span><span class="n">E</span><span class="o">)</span> <span class="n">queue</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Inserts item x at position k, maintaining heap invariant by </span></span></span><span class="line"><span class="cl"><span class="cm"> * promoting x up the tree until it is greater than or equal to </span></span></span><span class="line"><span class="cl"><span class="cm"> * its parent, or is the root. </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * To simplify and speed up coercions and comparisons, the </span></span></span><span class="line"><span class="cl"><span class="cm"> * Comparable and Comparator versions are separated into different </span></span></span><span class="line"><span class="cl"><span class="cm"> * methods that are otherwise identical. (Similarly for siftDown.) </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param k the position to fill </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param x the item to insert </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">siftUp</span><span class="o">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">,</span> <span class="n">E</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span> <span class="o">...</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">public</span> <span class="n">E</span> <span class="nf">poll</span><span class="o">()</span> <span class="o">{</span> <span class="o">...</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>可以看到使用 queue 数组来存放元素,节点 queue[k] 对应的左右节点分别为 queue[2k+1]、queue[2k+2]。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/7_1.webp" alt="binary_heap"></p> <h3 id="堆的操作">堆的操作</h3> <h4 id="插入">插入</h4> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Inserts the specified element into this priority queue. </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @return {@code true} (as specified by {@link Queue#offer}) </span></span></span><span class="line"><span class="cl"><span class="cm"> * @throws ClassCastException if the specified element cannot be </span></span></span><span class="line"><span class="cl"><span class="cm"> * compared with elements currently in this priority queue </span></span></span><span class="line"><span class="cl"><span class="cm"> * according to the priority queue&#39;s ordering </span></span></span><span class="line"><span class="cl"><span class="cm"> * @throws NullPointerException if the specified element is null </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">offer</span><span class="o">(</span><span class="n">E</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="n">NullPointerException</span><span class="o">();</span> </span></span><span class="line"><span class="cl"> <span class="n">modCount</span><span class="o">++;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">size</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="n">queue</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">grow</span><span class="o">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">siftUp</span><span class="o">(</span><span class="n">i</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">size</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>真正的插入操作是在 siftUp 函数中实现的。 首先把元素放在数组尾部,然后依次向上与父节点比较,如果满足条件则位置互换。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/7_2.webp" alt="sift_up"></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kt">void</span> <span class="nf">siftUp</span><span class="o">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">,</span> <span class="n">E</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">comparator</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">siftUpUsingComparator</span><span class="o">(</span><span class="n">k</span><span class="o">,</span> <span class="n">x</span><span class="o">,</span> <span class="n">queue</span><span class="o">,</span> <span class="n">comparator</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="n">siftUpComparable</span><span class="o">(</span><span class="n">k</span><span class="o">,</span> <span class="n">x</span><span class="o">,</span> <span class="n">queue</span><span class="o">);</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="nf">siftUpComparable</span><span class="o">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">,</span> <span class="n">T</span> <span class="n">x</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">es</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Comparable</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="n">T</span><span class="o">&gt;</span> <span class="n">key</span> <span class="o">=</span> <span class="o">(</span><span class="n">Comparable</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="n">T</span><span class="o">&gt;)</span> <span class="n">x</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="o">(</span><span class="n">k</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 计算父节点的下标 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">parent</span> <span class="o">=</span> <span class="o">(</span><span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">1</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">Object</span> <span class="n">e</span> <span class="o">=</span> <span class="n">es</span><span class="o">[</span><span class="n">parent</span><span class="o">];</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 比较当前节点和父节点的关系 如果当前节点优先级更高,我们可以直接结束比较 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">compareTo</span><span class="o">((</span><span class="n">T</span><span class="o">)</span> <span class="n">e</span><span class="o">)</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 交换节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">es</span><span class="o">[</span><span class="n">k</span><span class="o">]</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">k</span> <span class="o">=</span> <span class="n">parent</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="n">es</span><span class="o">[</span><span class="n">k</span><span class="o">]</span> <span class="o">=</span> <span class="n">key</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><h4 id="删除">删除</h4> <p>删除操作是将返回并删除堆顶元素</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">E</span> <span class="nf">poll</span><span class="o">()</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">es</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">E</span> <span class="n">result</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 取出堆顶元素 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="o">((</span><span class="n">result</span> <span class="o">=</span> <span class="o">(</span><span class="n">E</span><span class="o">)</span> <span class="o">((</span><span class="n">es</span> <span class="o">=</span> <span class="n">queue</span><span class="o">)[</span><span class="mi">0</span><span class="o">]))</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="n">modCount</span><span class="o">++;</span> </span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="kt">int</span> <span class="n">n</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 其实就是要将最后一个元素放到顶部 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kd">final</span> <span class="n">E</span> <span class="n">x</span> <span class="o">=</span> <span class="o">(</span><span class="n">E</span><span class="o">)</span> <span class="n">es</span><span class="o">[(</span><span class="n">n</span> <span class="o">=</span> <span class="o">--</span><span class="n">size</span><span class="o">)];</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 将最后一个元素置空 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">es</span><span class="o">[</span><span class="n">n</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 进行siftdown操作 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="o">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">final</span> <span class="n">Comparator</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="n">E</span><span class="o">&gt;</span> <span class="n">cmp</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">((</span><span class="n">cmp</span> <span class="o">=</span> <span class="n">comparator</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">siftDownComparable</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">x</span><span class="o">,</span> <span class="n">es</span><span class="o">,</span> <span class="n">n</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="n">siftDownUsingComparator</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">x</span><span class="o">,</span> <span class="n">es</span><span class="o">,</span> <span class="n">n</span><span class="o">,</span> <span class="n">cmp</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">result</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/algo_in_action/7_3.webp" alt="sift_down"></p> <p>将根节点删除后,我们将尾部的节点提到根节点,然后依次下沉比较交互</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="nf">siftDownComparable</span><span class="o">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">,</span> <span class="n">T</span> <span class="n">x</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">es</span><span class="o">,</span> <span class="kt">int</span> <span class="n">n</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// assert n &gt; 0; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">Comparable</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="n">T</span><span class="o">&gt;</span> <span class="n">key</span> <span class="o">=</span> <span class="o">(</span><span class="n">Comparable</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="n">T</span><span class="o">&gt;)</span><span class="n">x</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">half</span> <span class="o">=</span> <span class="n">n</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">1</span><span class="o">;</span> <span class="c1">// loop while a non-leaf </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">while</span> <span class="o">(</span><span class="n">k</span> <span class="o">&lt;</span> <span class="n">half</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">child</span> <span class="o">=</span> <span class="o">(</span><span class="n">k</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span> <span class="c1">// assume left child is least </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">Object</span> <span class="n">c</span> <span class="o">=</span> <span class="n">es</span><span class="o">[</span><span class="n">child</span><span class="o">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">right</span> <span class="o">=</span> <span class="n">child</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">right</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="o">((</span><span class="n">Comparable</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="n">T</span><span class="o">&gt;)</span> <span class="n">c</span><span class="o">).</span><span class="na">compareTo</span><span class="o">((</span><span class="n">T</span><span class="o">)</span> <span class="n">es</span><span class="o">[</span><span class="n">right</span><span class="o">])</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">c</span> <span class="o">=</span> <span class="n">es</span><span class="o">[</span><span class="n">child</span> <span class="o">=</span> <span class="n">right</span><span class="o">];</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">compareTo</span><span class="o">((</span><span class="n">T</span><span class="o">)</span> <span class="n">c</span><span class="o">)</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">es</span><span class="o">[</span><span class="n">k</span><span class="o">]</span> <span class="o">=</span> <span class="n">c</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="n">k</span> <span class="o">=</span> <span class="n">child</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="n">es</span><span class="o">[</span><span class="n">k</span><span class="o">]</span> <span class="o">=</span> <span class="n">key</span><span class="o">;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><h4 id="扩容">扩容</h4> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Increases the capacity of the array. </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param minCapacity the desired minimum capacity </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kt">void</span> <span class="nf">grow</span><span class="o">(</span><span class="kt">int</span> <span class="n">minCapacity</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">oldCapacity</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Double size if small; else grow by 50% </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">newCapacity</span> <span class="o">=</span> <span class="n">ArraysSupport</span><span class="o">.</span><span class="na">newLength</span><span class="o">(</span><span class="n">oldCapacity</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">minCapacity</span> <span class="o">-</span> <span class="n">oldCapacity</span><span class="o">,</span> <span class="cm">/* minimum growth */</span> </span></span><span class="line"><span class="cl"> <span class="n">oldCapacity</span> <span class="o">&lt;</span> <span class="mi">64</span> <span class="o">?</span> <span class="n">oldCapacity</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">:</span> <span class="n">oldCapacity</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="cm">/* preferred growth */</span><span class="o">);</span> </span></span><span class="line"><span class="cl"> <span class="n">queue</span> <span class="o">=</span> <span class="n">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">queue</span><span class="o">,</span> <span class="n">newCapacity</span><span class="o">);</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>和 vector 扩容思想类似,当原容量小于 64 时,扩容到 2 倍 +2 ,否则 1.5 倍。</p> <h1 id="第八讲-外部排序">第八讲 外部排序</h1> <h2 id="常见排序算法">常见排序算法</h2> <p><img src="https://ppd0705.github.io/image/algo_in_action/8_1.webp" alt="sort_algo"></p> <h2 id="外部排序">外部排序</h2> <p>外部排序是指借助外部存储来排序,相比内存,外部排序有更大的IO消耗。 常用的外部排序为归并排序。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/8_2.webp" alt="merge_sort"></p> <h2 id="运行时间">运行时间</h2> <p>每一层我们读取外层的数据总量是一样的,我们要做的是让归并的层次越低越好</p> <p>假设数据段为 n, 归并路数为 k,那么层数为 logk(n)。 但增加 k 的大小也会导致 从 k 个元素中选择 最小 的元素的代价变高。</p> <h2 id="败者树">败者树</h2> <p><img src="https://ppd0705.github.io/image/algo_in_action/8_3.webp" alt="tournament_tree"> <img src="https://ppd0705.github.io/image/algo_in_action/8_4.webp" alt="tournament_tree"></p> <p>败者树的思想是使用叶子节点存储待比较的元素,两两比较,在父节点存储失败者,然后 对获胜的元素两两比较,得到更上层的失败者。</p> <p>根节点之上有一个最终获胜者</p> <p>对于取出胜者并添加新元素的操作,只需要将新元素置于胜者的位置 ,再依次向上比较即可。</p> <p>败者树相比堆,Pop时只需要和上层的败者比较即可,不像堆一定要和左右子树比较两次。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/8_5.webp" alt="tournament_tree"></p> <p>相关算法题 - <a href="https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/xcdytv-by-ppd-2-sspr/">合并K个有序链表</a></p> <h1 id="第九讲-二分法">第九讲 二分法</h1> <h2 id="二分查找">二分查找</h2> <p>二分查找的核心是查找元素和比较有序排列</p> <h2 id="kafka">Kafka</h2> <p>Kafka 中所有的消息都是以&quot;日志&quot;的形式存储,消息按写入时间顺序,依次追加在许多日志文件中。 文件中每条消息会相对第一条消息有一个偏移量(类似自增ID)。</p> <p>Kafka 每个 topic 会有多个 partition,每个 partition下 的日志按照顺序分成一个个有序日志段。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/9_1.webp" alt="kafka_log"></p> <h3 id="消息查找">消息查找</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">00000000000000000000.log </span></span><span class="line"><span class="cl">00000000000000000000.index </span></span><span class="line"><span class="cl">00000000000000000000.timeindex </span></span><span class="line"><span class="cl">00000000000000000035.log </span></span><span class="line"><span class="cl">00000000000000000035.index </span></span><span class="line"><span class="cl">00000000000000000035.timeindex </span></span></code></pre></td></tr></table> </div> </div><p>上面为一个典型的日志文件存储情况:</p> <ul> <li>.log 文件存储的就是消息本身的日志文件</li> <li>.index 文件是索引文件</li> <li>.timeindex 文件是基于时间戳的索引文件</li> </ul> <p>.index 文件内容示例如下图</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/9_2.webp" alt="kafka_log2"></p> <p>kafka为了使用更小的内存空间,采用了稀疏索引。</p> <h3 id="源码实现">源码实现</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-scala" data-lang="scala"><span class="line"><span class="cl"> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Lookup lower and upper bounds for the given target. </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> <span class="k">private</span> <span class="k">def</span> <span class="n">indexSlotRangeFor</span><span class="o">(</span><span class="n">idx</span><span class="k">:</span> <span class="kt">ByteBuffer</span><span class="o">,</span> <span class="n">target</span><span class="k">:</span> <span class="kt">Long</span><span class="o">,</span> <span class="n">searchEntity</span><span class="k">:</span> <span class="kt">IndexSearchEntity</span><span class="o">)</span><span class="k">:</span> <span class="o">(</span><span class="kt">Int</span><span class="o">,</span> <span class="kt">Int</span><span class="o">)</span> <span class="k">=</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 检查index是否为空 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span><span class="o">(</span><span class="nc">_entries</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">(-</span><span class="mi">1</span><span class="o">,</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 二分搜索 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">def</span> <span class="n">binarySearch</span><span class="o">(</span><span class="n">begin</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">end</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span> <span class="k">:</span> <span class="o">(</span><span class="kt">Int</span><span class="o">,</span> <span class="kt">Int</span><span class="o">)</span> <span class="k">=</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">var</span> <span class="n">lo</span> <span class="k">=</span> <span class="n">begin</span> </span></span><span class="line"><span class="cl"> <span class="k">var</span> <span class="n">hi</span> <span class="k">=</span> <span class="n">end</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="o">(</span><span class="n">lo</span> <span class="o">&lt;</span> <span class="n">hi</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">val</span> <span class="n">mid</span> <span class="k">=</span> <span class="n">ceil</span><span class="o">(</span><span class="n">hi</span><span class="o">/</span><span class="mf">2.0</span> <span class="o">+</span> <span class="n">lo</span><span class="o">/</span><span class="mf">2.0</span><span class="o">).</span><span class="n">toInt</span> </span></span><span class="line"><span class="cl"> <span class="k">val</span> <span class="n">found</span> <span class="k">=</span> <span class="n">parseEntry</span><span class="o">(</span><span class="n">idx</span><span class="o">,</span> <span class="n">mid</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">val</span> <span class="n">compareResult</span> <span class="k">=</span> <span class="n">compareIndexEntry</span><span class="o">(</span><span class="n">found</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">searchEntity</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="o">(</span><span class="n">compareResult</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">hi</span> <span class="k">=</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="k">if</span><span class="o">(</span><span class="n">compareResult</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="n">lo</span> <span class="k">=</span> <span class="n">mid</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">(</span><span class="n">mid</span><span class="o">,</span> <span class="n">mid</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="n">lo</span><span class="o">,</span> <span class="k">if</span> <span class="o">(</span><span class="n">lo</span> <span class="o">==</span> <span class="nc">_entries</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="o">-</span><span class="mi">1</span> <span class="k">else</span> <span class="n">lo</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">val</span> <span class="n">firstHotEntry</span> <span class="k">=</span> <span class="nc">Math</span><span class="o">.</span><span class="n">max</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="nc">_entries</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="nc">_warmEntries</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 查询的目标offset是否在热区 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span><span class="o">(</span><span class="n">compareIndexEntry</span><span class="o">(</span><span class="n">parseEntry</span><span class="o">(</span><span class="n">idx</span><span class="o">,</span> <span class="n">firstHotEntry</span><span class="o">),</span> <span class="n">target</span><span class="o">,</span> <span class="n">searchEntity</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">binarySearch</span><span class="o">(</span><span class="n">firstHotEntry</span><span class="o">,</span> <span class="nc">_entries</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 查询的目标offset是否小于最小的offset </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span><span class="o">(</span><span class="n">compareIndexEntry</span><span class="o">(</span><span class="n">parseEntry</span><span class="o">(</span><span class="n">idx</span><span class="o">,</span> <span class="mi">0</span><span class="o">),</span> <span class="n">target</span><span class="o">,</span> <span class="n">searchEntity</span><span class="o">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">(-</span><span class="mi">1</span><span class="o">,</span> <span class="mi">0</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">binarySearch</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">firstHotEntry</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>Kafka 使用了 mmap 将磁盘文件和内存进行映射, 消息队列的特性决定了 大部分索引查询其实都是在日志比较靠近尾部的区域,将索引中最后 8KB 认为热区,查询是优先查热区,没有命中再查冷区,这样就可以减少缺页中断的次数。</p> <p>kafka 采用冷热二分查询的改进见这个<a href="https://issues.apache.org/jira/browse/KAFKA-6432">issue</a></p> <h1 id="第十讲-暴力搜索算法">第十讲 暴力搜索算法</h1> <p>BFS 和 DFS 是两种常见的搜索算法</p> <h1 id="第十一讲-字符串匹配">第十一讲 字符串匹配</h1> <h2 id="brute-force-算法">Brute-Force 算法</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cm">/* </span></span></span><span class="line"><span class="cl"><span class="cm"> * s: 主串 </span></span></span><span class="line"><span class="cl"><span class="cm"> * p:模式串 </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">match</span><span class="p">(</span><span class="n">string</span> <span class="n">s</span><span class="p">,</span> <span class="n">string</span> <span class="n">p</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ans</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">-</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">m</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="n">p</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">==</span> <span class="n">m</span><span class="p">)</span> <span class="n">ans</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">i</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ans</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="boyer-moore-算法">Boyer-Moore 算法</h2> <h3 id="坏字符规则">坏字符规则</h3> <p>BM 算法采用从后向前匹配的方式</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/11_1.webp" alt="1"></p> <p>如上图, 位置 6 匹配,那这里的 &ldquo;S&rdquo; 就是是个坏字符,遇到坏字符时, 如果在模式串中没有出现,则说明没有重叠,可以直接跳过这段。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/11_2.webp" alt="2"></p> <p>下一步匹配时,发现坏字符&quot;P&quot;,但 &ldquo;P&rdquo; 在模式串有出现,可以将模式串最后一次出现 &ldquo;P&quot;的位置对齐</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/11_3.webp" alt="3"></p> <p>每次发现坏字符时,我们需要向右移动移动 (失配位置 - 失配字符最右出现的下标)位,其中如果失配字符没有在模式串出现,则下标为 -1,</p> <h3 id="好后缀规则">好后缀规则</h3> <p><img src="https://ppd0705.github.io/image/algo_in_action/11_4.webp" alt="4"></p> <p>在 SIMPLE 和 EXAMPLE 的匹配中,我们发&quot;MPLE&quot;都匹配得上,那&quot;MPLE&quot;就称之为好后缀, 同样&quot;PLE&rdquo;、&ldquo;LE&rdquo;、&ldquo;E&rdquo; 都是好后缀。</p> <p>如果用坏字符规则,则应该将模式串移动(2 - (-1)) = 3 位,因为&quot;I&quot; 在模式串不存在。</p> <p>我们使用好后缀的规则: 找到好后缀在模式串中最右的匹配位置,总计向后移动(模式串字符串长度 - 1 - 好后缀在模式串上次出现的位置)位。 上图好后缀&quot;E&quot; 的计算值为 6 (7-1-0)位</p> <h2 id="bm-算法具体实现">BM 算法具体实现</h2> <h3 id="坏字符最右位置计算">坏字符最右位置计算</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_bc</span><span class="p">(</span><span class="n">pattern</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">bc</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span> <span class="c1"># 记录每个badchar最右出现的位置</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">char</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">bc</span><span class="p">[</span><span class="n">char</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">bc</span> </span></span></code></pre></td></tr></table> </div> </div><p>我们用 dict 来记录每个字符最右侧位置。工业级的实现会用 [0, 256] 的数组替代来提高性能。</p> <h3 id="好后缀计算">好后缀计算</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_gs</span><span class="p">(</span><span class="n">pattern</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">gs</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">gs</span><span class="p">[</span><span class="s1">&#39;&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># suf_len 用于标记后缀长度</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">suf_len</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)):</span> </span></span><span class="line"><span class="cl"> <span class="n">suffix</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="o">-</span> <span class="n">suf_len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:]</span> </span></span><span class="line"><span class="cl"> <span class="c1"># j 用于标记可用于匹配的位置</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="o">-</span> <span class="n">suf_len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">substr</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">[</span><span class="n">j</span><span class="p">:</span><span class="n">j</span> <span class="o">+</span> <span class="n">suf_len</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">suffix</span> <span class="o">==</span> <span class="n">substr</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">gs</span><span class="p">[</span><span class="n">suffix</span><span class="p">]</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="o">-</span> <span class="n">j</span> <span class="o">-</span> <span class="n">suf_len</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">suf_len</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)):</span> </span></span><span class="line"><span class="cl"> <span class="n">suffix</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="o">-</span> <span class="n">suf_len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">suffix</span> <span class="ow">in</span> <span class="n">gs</span><span class="p">:</span> <span class="k">continue</span> </span></span><span class="line"><span class="cl"> <span class="n">gs</span><span class="p">[</span><span class="n">suffix</span><span class="p">]</span> <span class="o">=</span> <span class="n">gs</span><span class="p">[</span><span class="n">suffix</span><span class="p">[</span><span class="mi">1</span><span class="p">:]]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">gs</span><span class="p">[</span><span class="s1">&#39;&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">gs</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="匹配">匹配</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">bm</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">bc</span><span class="p">,</span> <span class="n">gs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="c1"># i 用于标记当前模式串和主串哪个位置左对齐。</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="c1"># j 用于标记当前模式串匹配到哪个位置;从右往左遍历匹配。</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">j</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="c1"># 从右往左匹配每个位置</span> </span></span><span class="line"><span class="cl"> <span class="n">a</span> <span class="o">=</span> <span class="n">string</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">b</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">[</span><span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">:</span> <span class="c1"># 匹配的上,继续匹配前一位</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">=</span> <span class="n">j</span> <span class="o">-</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> <span class="c1"># 匹配不上,根据两个规则的预处理结果进行快速移动</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="nb">max</span><span class="p">(</span><span class="n">gs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">pattern</span><span class="p">[</span><span class="n">j</span><span class="p">:],</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)),</span> <span class="n">j</span> <span class="o">-</span> <span class="n">bc</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="n">j</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="c1"># 匹配成功返回匹配位置</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">j</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">i</span> </span></span><span class="line"><span class="cl"> <span class="c1"># 匹配失败返回 None</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">string</span> <span class="o">=</span> <span class="s1">&#39;here is a simple example &#39;</span> </span></span><span class="line"><span class="cl"> <span class="n">pattern</span> <span class="o">=</span> <span class="s1">&#39;example&#39;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">bc</span> <span class="o">=</span> <span class="n">get_bc</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="c1"># 坏字符表</span> </span></span><span class="line"><span class="cl"> <span class="n">gs</span> <span class="o">=</span> <span class="n">get_gs</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="c1"># 好后缀表</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">gs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="n">bm</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="n">pattern</span><span class="p">,</span> <span class="n">bc</span><span class="p">,</span> <span class="n">gs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十二讲-拓扑排序">第十二讲 拓扑排序</h1> <p><img src="https://ppd0705.github.io/image/algo_in_action/12_1.webp" alt="1"></p> <p>如上图的课表,部分课程学习有一些前置课程,用图的方式表示如下:</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/12_2.webp" alt="2"></p> <p>如果我们给出一个序列使得每个节点只出现一次,且保证如果存在路径 P 从 A 到 B, 那么 A 在序列中一定出现在 B 之前,满足这个条件的序列就被可认为满拓扑排序。</p> <p>拓扑排序是建立在有向无环图(DAG)的基础之上的.</p> <h2 id="khan-算法">khan 算法</h2> <p>khan 算法是一种基于贪心和广度优先思想的拓扑排序实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">findOrder</span><span class="p">(</span><span class="kt">int</span> <span class="n">numCourses</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;&amp;</span> <span class="n">prerequisites</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 邻接表数组 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">next</span><span class="p">(</span><span class="n">numCourses</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 入度表 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">pre</span><span class="p">(</span><span class="n">numCourses</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 标记是否遍历过 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">visited</span><span class="p">(</span><span class="n">numCourses</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 记录最终修读顺序 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">ans</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 构图 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">edge</span><span class="p">:</span> <span class="n">prerequisites</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="n">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">next</span><span class="p">[</span><span class="n">p</span><span class="p">].</span><span class="n">push_back</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">pre</span><span class="p">[</span><span class="n">n</span><span class="p">]</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">queue</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">q</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 所有没有先修课程的课程入队 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numCourses</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">pre</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">i</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// BFS搜索 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">while</span><span class="p">(</span><span class="o">!</span><span class="n">q</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">front</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">q</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">ans</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">visited</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 遍历所有以队首课程为先修课程的课程 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">n</span><span class="p">:</span> <span class="n">next</span><span class="p">[</span><span class="n">p</span><span class="p">])</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 由于队首课程已经被修读,所以当前课程入度-1 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">pre</span><span class="p">[</span><span class="n">n</span><span class="p">]</span><span class="o">--</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 如果该课程所有先修课程已经修完;将该课程入队 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">pre</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span><span class="n">q</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">n</span><span class="p">);}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 环路检测: 如果仍有课程没有修读;说明环路存在 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numCourses</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">visited</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="k">return</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ans</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="dfs">DFS</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span><span class="lnt">97 </span><span class="lnt">98 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// https://github.com/marcelklehr/toposort </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="cm">/** </span></span></span><span class="line"><span class="cl"><span class="cm"> * Topological sorting function </span></span></span><span class="line"><span class="cl"><span class="cm"> * </span></span></span><span class="line"><span class="cl"><span class="cm"> * @param {Array} edges </span></span></span><span class="line"><span class="cl"><span class="cm"> * @returns {Array} </span></span></span><span class="line"><span class="cl"><span class="cm"> */</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">edges</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">toposort</span><span class="p">(</span><span class="nx">uniqueNodes</span><span class="p">(</span><span class="nx">edges</span><span class="p">),</span> <span class="nx">edges</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">array</span> <span class="o">=</span> <span class="nx">toposort</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">toposort</span><span class="p">(</span><span class="nx">nodes</span><span class="p">,</span> <span class="nx">edges</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">cursor</span> <span class="o">=</span> <span class="nx">nodes</span><span class="p">.</span><span class="nx">length</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="nx">sorted</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">cursor</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="nx">visited</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="nx">i</span> <span class="o">=</span> <span class="nx">cursor</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Better data structures make algorithm much faster. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">,</span> <span class="nx">outgoingEdges</span> <span class="o">=</span> <span class="nx">makeOutgoingEdges</span><span class="p">(</span><span class="nx">edges</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="nx">nodesHash</span> <span class="o">=</span> <span class="nx">makeNodesHash</span><span class="p">(</span><span class="nx">nodes</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// check for unknown nodes </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">edges</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">edge</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">nodesHash</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">||</span> <span class="o">!</span><span class="nx">nodesHash</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Unknown node. There is an unknown node in the supplied edges.&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nx">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">visited</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span> <span class="nx">visit</span><span class="p">(</span><span class="nx">nodes</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">i</span><span class="p">,</span> <span class="k">new</span> <span class="nx">Set</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">sorted</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">visit</span><span class="p">(</span><span class="nx">node</span><span class="p">,</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">predecessors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="nx">predecessors</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">node</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">nodeRep</span> </span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">nodeRep</span> <span class="o">=</span> <span class="s2">&#34;, node was:&#34;</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">nodeRep</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Cyclic dependency&#39;</span> <span class="o">+</span> <span class="nx">nodeRep</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">nodesHash</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">node</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Found unknown node. Make sure to provided all involved nodes. Unknown node: &#39;</span><span class="o">+</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">node</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">visited</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nx">visited</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">outgoing</span> <span class="o">=</span> <span class="nx">outgoingEdges</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="o">||</span> <span class="k">new</span> <span class="nx">Set</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="nx">outgoing</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">outgoing</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="nx">outgoing</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">predecessors</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">do</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">child</span> <span class="o">=</span> <span class="nx">outgoing</span><span class="p">[</span><span class="o">--</span><span class="nx">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nx">visit</span><span class="p">(</span><span class="nx">child</span><span class="p">,</span> <span class="nx">nodesHash</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">child</span><span class="p">),</span> <span class="nx">predecessors</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="nx">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">predecessors</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">sorted</span><span class="p">[</span><span class="o">--</span><span class="nx">cursor</span><span class="p">]</span> <span class="o">=</span> <span class="nx">node</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">uniqueNodes</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Set</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">len</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">edge</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nx">res</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="nx">res</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">res</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">makeOutgoingEdges</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">edges</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">len</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">edge</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">edges</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span> <span class="nx">edges</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="k">new</span> <span class="nx">Set</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">edges</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span> <span class="nx">edges</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="k">new</span> <span class="nx">Set</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="nx">edges</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="nx">add</span><span class="p">(</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">edges</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">makeNodesHash</span><span class="p">(</span><span class="nx">arr</span><span class="p">){</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">len</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">len</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">res</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">res</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十三讲-哈夫曼树">第十三讲 哈夫曼树</h1> <p>HTTP2 使用 HPACK 压缩协议来了压缩 Header, 其主要包括三个部分</p> <ul> <li>静态表</li> <li>动态表</li> <li>哈夫曼编码</li> </ul> <p><img src="https://ppd0705.github.io/image/algo_in_action/13_1.webp" alt="1"></p> <h2 id="静态表">静态表</h2> <p>有 61 个 常用 字段(部分包含值)编码到了 1~61 的<a href="https://www.rfc-editor.org/rfc/rfc7541#appendix-A">索引表</a>里</p> <h2 id="动态表">动态表</h2> <p>静态表字段有限,允许通过通信的方式,在客户端和服务端之间维护一张动态的字典,用索引来代表值</p> <h2 id="哈夫曼编码">哈夫曼编码</h2> <p>限于静态表和动态表的大小,我们并不能压缩任意数据,于是引入哈夫曼编码来进一步提高压缩能力 。</p> <p>哈夫曼编码的主要思想就是让出现概率高的字符用短编码,概率低的用长编码; 同时为了避免变长编码产生歧义,所以不同的字符键不能成为对方的前缀。</p> <p>算法实现采用了贪心思想,用一颗二叉树来标记每个字符的编码方式,左分支代表 0, 右分支代表 1; 所有需要编码的字符对应一个叶子节点,根节点到该叶子节点的路径代表该字符的编码方式。</p> <p>编码示例,假设有 a、b、c、d、e、f 六个字符,分别出现的频率是 5、9、12、13、16、45。依次选取两个频率最小的节点合成一颗子树,用两个频率只和代表父节点的频率。</p> <p><img src="https://ppd0705.github.io/image/algo_in_action/13_2.webp" alt="2"></p> <p><img src="https://ppd0705.github.io/image/algo_in_action/13_3.webp" alt="3"></p> <p>最终对应编码为:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">f 0 </span></span><span class="line"><span class="cl">c 100 </span></span><span class="line"><span class="cl">d 101 </span></span><span class="line"><span class="cl">a 1100 </span></span><span class="line"><span class="cl">b 1101 </span></span><span class="line"><span class="cl">e 111 </span></span></code></pre></td></tr></table> </div> </div><p>代码实现可借助小顶堆</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">buildHuffmanTree</span><span class="p">(</span><span class="n">string</span> <span class="n">text</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 利用hashmap对字符串进行频率计数 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">unordered_map</span><span class="o">&lt;</span><span class="kt">char</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">freq</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">char</span> <span class="nl">ch</span><span class="p">:</span> <span class="n">text</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">freq</span><span class="p">[</span><span class="n">ch</span><span class="p">]</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 用堆去动态维护所有树中最小的两颗 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">priority_queue</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">*</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">*&gt;</span><span class="p">,</span> <span class="n">comp</span><span class="o">&gt;</span> <span class="n">pq</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 将所有的字符都初始化成为哈夫曼树的一个叶子节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 并推入优先队列 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="nl">pair</span><span class="p">:</span> <span class="n">freq</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">pq</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">getNode</span><span class="p">(</span><span class="n">pair</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">pair</span><span class="p">.</span><span class="n">second</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 每次取出最小的两个合并 直至优先队列只剩一个节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">while</span> <span class="p">(</span><span class="n">pq</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 最小的两个节点出队 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">Node</span> <span class="o">*</span><span class="n">left</span> <span class="o">=</span> <span class="n">pq</span><span class="p">.</span><span class="n">top</span><span class="p">();</span> <span class="n">pq</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">Node</span> <span class="o">*</span><span class="n">right</span> <span class="o">=</span> <span class="n">pq</span><span class="p">.</span><span class="n">top</span><span class="p">();</span> <span class="n">pq</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 建立一个内部节点,以这两个最小的树为左右节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">left</span><span class="o">-&gt;</span><span class="n">freq</span> <span class="o">+</span> <span class="n">right</span><span class="o">-&gt;</span><span class="n">freq</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pq</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">getNode</span><span class="p">(</span><span class="sc">&#39;\0&#39;</span><span class="p">,</span> <span class="n">sum</span><span class="p">,</span> <span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// 优先队列中最后一个元素为整棵树的根节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">Node</span><span class="o">*</span> <span class="n">root</span> <span class="o">=</span> <span class="n">pq</span><span class="p">.</span><span class="n">top</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> 2021年终总结 https://ppd0705.github.io/post/2021_summary/ Sun, 13 Feb 2022 11:43:41 +0800 https://ppd0705.github.io/post/2021_summary/ <p>其实从12月开始就在想要写总结了,一直拖延,然后就想农历新年再写,结果就拖到现在。支撑我写总结的原因是看到太多 真诚开放的年度总结了,向大家看齐,做一个真诚开放的自己。</p> <h1 id="工作">工作</h1> <p>我一直以来并没有清晰明了的职业规划,年初选择数字货币行业 offer 的一个很大外部原因是我转行当程序员之前就非常喜欢的 的程序员大佬在 2018 年就加入了这个行业,她公众号【嘀嗒嘀嗒】写的她自己经历的文章,看似简短,但让人常看常新,尤其是到了某些纠结迷茫的时候。</p> <p>转眼新工作就一年了,一开始对数字货币几乎一无所知。从注册各种交易所账户体验炒币开始,接着跟风推特转发抢空投,然后 在去中心化交易所交换各种币。区块链技术上也学了极客时间上的一门相关课程,然后找了一个高 star 开源的<a href="https://github.com/Jeiwan/blockchain_go">简易区块链实现</a> 照着实现了一遍,对区块链原理和运行有了进一步的了解, 做入职分享时<a href="https://github.com/ppd0705/my_document/blob/master/slide/Conflux.key">了解了一点 Conflux 公链</a>。</p> <p>我所在的小组负责交易所内部做市业务,交易所算我们的上游,而区块链又算交易所的上游。量化做市是为交易所提供流动性,简而言之就是 使用程序根据一定的策略逻辑调下单接口自动挂买卖单由此产生深度,这样其他用户就可以有对手盘可以交易。在市场行情不可控的前提下如何既提供流动性 又能保证不亏钱?这是一个难题!目前最主流的方式的通过对冲来控制风险和获取盈利,但是对冲太依赖对冲方了,很多时候找不到对冲方,有时也需要对冲更直接高效的方法。</p> <p>小组从年初的 2 人到年末的 4 人,总算有点组的样子了。不过作为独立的内部小组,也不对外提供服务,我们组在公司有点隐身的感觉。我们 没有产品、前端、测试等上下游配合,完全是自我在迭代,需求、实现和交付都自己来。 我们的技术栈看起来朴实无华,网站用经典的 flask + jquery + bootstrap 组合, 策略开发用 Python。 其实我好久没有写过网页了,仍记得学 css 时对自己写的页面深深地不忍直视,好在有框架,也好在是自己用;另外顺道了解了 Vue 和 Nuxt.js,引入了很香的 pyechart 做可视化。 策略方面,之前一份工作和量化数据和策略相关,但业务偏向于做一个通用的平台给别人来做策略研究。 直到自己真的来写实盘策略,发现实盘策略和模拟策略差别太大了,真实交易情况下要考虑手续费,下单可能不成交,下单可能超时但实际成交了,交易所可能临时维护等等。 另外策略属于比较机密的东西,而且不同的策略有不同的使用场景,真正优质的开源作品不多,让我收获最大的是 <a href="https://space.bilibili.com/479971824/">aioquant</a> 和 <a href="https://github.com/hummingbot/hummingbot">hummingbot</a>,感兴趣的同学可以看看。</p> <h1 id="生活">生活</h1> <p>处在数字货币 7 * 24 小时交易这样的金钱永不眠的环境下,人难免变得浮躁,尤其是持有了一些资产之后,面对价格的浮动,内心的贪婪和恐惧都赤裸裸地展现出来, 有一段时间,我会不停地刷手机上的行情软件,整个人变的无比焦虑,后面想清楚了自己是一个长期投资者,于是把行情软件都卸载了。 直到元旦节计算盈亏,算下来总体应该没有亏,检查个人账户,才发现账户资产几个月前被人盗走了,不知道助记词何时被泄露了!而作为去中心化服务,被盗欲诉无门,只能认栽!</p> <p>跑步仍是我的首选的长期爱好,总里程 400 公里,数字不大,但重要的是能让我养成早起健身的习惯,其中 5月份 又去参加了汶川半马,之前两次都是我一个人参加, 这次是带着爸爸和姐姐去参加,有家人在终点等我的感觉还是蛮好的,跑完步又带着爸爸和姐姐去了都江堰、青城山逛了逛,这算是我们三个人第一次一同旅行,还是蛮难得的。</p> <p>12 月份和同学延续了一年一见的惯例。不过我周中先去了永宁和泉州 ,体验闽南习俗;周末和同学(及和同学般的前同事)在苏州碰头,一年未见,大家都 感叹时间飞逝,每个人都在为各自的生活而前行。作为一个成年人,应该都体会到很多时候再见最多就是在朋友圈点赞见了,所以有时我感到自己能作为一个这样的中间的联结者而开心。</p> <p>今年看了20+ 本书,微信读书是在太方便了,纸质书越读越少,最推荐的书是《项塔兰》,看完在心里立了一个小目标:有机会去一次印度。</p> <p>英语学习现在迁移到了开言英语 APP 上,我还蛮喜欢真人英语视频对话这种模式的,每天大概 20 分钟的样子,不会感觉太大压力。</p> <p>尝试了一下画画,但开始体验并不是很好,就放弃了,或许以后有机会吧。</p> <h1 id="展望">展望</h1> <ul> <li>探索个人在区块链领域更多可能性:比如公链、智能合约</li> <li>学习算法、docker和K8S</li> <li>完结在学的C和操作系统课程</li> <li>跑步每周三次,参加一次半马</li> <li>每周英语练习</li> <li>带家人旅行一次(计划去四姑娘山)</li> </ul> [笔记]深入C语言和程序运行原理 https://ppd0705.github.io/post/learn_c/ Mon, 31 Jan 2022 17:04:50 +0800 https://ppd0705.github.io/post/learn_c/ <h1 id="第零讲-课前热身">第零讲 课前热身</h1> <h2 id="数据量单位">数据量单位</h2> <p>位(bit): 是最小存储单位,每个位可以存一个二进制码值得 0 或 1 字节(byte): 通常是由八个位组成的一个存储单元,是计算机最小的可寻址单位 位(word):是处理器使用的自然数据单位,处理器单个指令可以操作的最大的内存块 一般为一个字节大小</p> <h2 id="汇编语言">汇编语言</h2> <p>汇编语言(Assembly Language)是一种低级编程语言,和 CPU 架构相关。</p> <p>汇编语言使用助记符(Mnemonic)来表示每个低级的机器指令,不同的汇编指令可以使用不同的参数形式,以 mov 指令举例,有如下三种形式:</p> <ul> <li>mov r/m, r</li> <li>mov r, r/m</li> <li>mov r/m, imm</li> </ul> <p>指令参数中,r 表示 寄存器(register),m 表示内存(memory),imm 表示立即数(immediate)。 指令<code>mov ebx 1</code>的含义是将立即数 1 存到寄存器 ebx 中。 在x86指令集中,受限于 CPU 实现的复杂的,不存在将两个内存地址同时作为 src 和 dest 参数的指令。</p> <p>指令<code>mov ebx 1</code>对应的机器码为 <code>bb 01 00 00 00</code> 机器码由 OpCode 和 Immediata Data 两部分组成,OpCode 占用一个字节,mov 对应的指令是 0xb8,寄存器对应阈值为 0x3, 组合在一起即为 0xbb;由于 mov 传送 32 位数,所以立即数单独占用 4 个字节 (使用小端序)</p> <h2 id="寄存器">寄存器</h2> <p>寄存器可以简单理解为 CPU 提供的一组由位于芯片上的高速存储器硬件,拥有最快的数据访问速度和最低的延迟</p> <p>寄存器通常分为如下几类:</p> <ul> <li>统一寄存器:一般存放程序运行过程中产生的临时数据</li> <li>状态寄存器:一般存放之间执行结果相关的状态信息,如指令是否引起进位等</li> <li>系统寄存器:一般由操作系统使用,存放中断、CPU模式等信息</li> </ul> <p>x86-64 架构中定义了 16 个通用寄存器,每个寄存器可以存放4个指令字(每个指令字16字节)</p> <p>在汇编代码中,可以使用每个寄存器的不同别名来访问对应的低 8 位,低 16 位,低 32 位,以及完整的 64 位数据</p> <p><img src="https://ppd0705.github.io/image/learn_c/0_1.jpg" alt="register_alias"></p> <p>需要注意的是当重写寄存器的低32位数据时,高32位数据会被置零,可以使用如下代码进行对比。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">register</span> <span class="kt">long</span> <span class="n">num</span> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;rax&#34;</span><span class="p">)</span> <span class="o">=</span> <span class="mh">0x100000000</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;movl $0x1, %eax&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// asm(&#34;movw $0x1, %ax&#34;); </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%ld</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">num</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第一讲-一个c程序的完整生命周期">第一讲 一个C程序的完整生命周期</h1> <h2 id="c核心语法">C核心语法</h2> <p>实例程序如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt; </span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;assert.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdbool.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="cp">#define BOOL_TRUE 1 </span><span class="c1">// 定义用到的宏常量与宏函数; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define BOOL_FALSE 0 </span></span></span><span class="line"><span class="cl"><span class="cp">#define typename(x) _Generic((x), \ </span></span></span><span class="line"><span class="cl"><span class="cp"> unsigned short: &#34;unsigned short int&#34;, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> unsigned long: &#34;unsigned long int&#34;, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> default: &#34;unknown&#34;) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">enum</span> <span class="p">{</span> <span class="n">Host</span><span class="p">,</span> <span class="n">IP</span> <span class="p">}</span> <span class="n">IP_ADDR_TYPE</span><span class="p">;</span> <span class="c1">// 定义枚举类型 IP_ADDR_TYPE,用于表示联合中生效的字段; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> <span class="c1">// 定义结构 CONN; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">size_t</span> <span class="n">id</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">closed</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">IP_ADDR_TYPE</span> <span class="n">addr_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">union</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">host_name</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">ip</span><span class="p">[</span><span class="mi">24</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">CONN</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">inline</span> <span class="k">static</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="nf">findAddr</span><span class="p">(</span><span class="k">const</span> <span class="n">CONN</span><span class="o">*</span> <span class="n">pip</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 定义函数 findAddr,用于打印 CONN 对象的信息; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">assert</span><span class="p">(</span><span class="n">pip</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> <span class="c1">// 运行时断言,判断传入的 CONN 指针是否有效; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="n">pip</span><span class="o">-&gt;</span><span class="n">addr_type</span> <span class="o">==</span> <span class="n">Host</span> <span class="o">?</span> <span class="n">pip</span><span class="o">-&gt;</span><span class="nl">host_name</span> <span class="p">:</span> <span class="n">pip</span><span class="o">-&gt;</span><span class="n">ip</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span> <span class="c1">// 入口函数; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">static_assert</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">CONN</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mh">0x400</span><span class="p">,</span> <span class="s">&#34;the size of CONN object exceeds limit.&#34;</span><span class="p">);</span> <span class="c1">// 静态断言,判断 CONN 对象的大小是否符合要求; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="n">CONN</span> <span class="n">conns</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="c1">// 构造一个数组,包含三个 CONN 对象; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="n">BOOL_TRUE</span><span class="p">,</span> <span class="n">IP</span><span class="p">,</span> <span class="p">{</span> <span class="p">.</span><span class="n">ip</span> <span class="o">=</span> <span class="s">&#34;127.0.0.1&#34;</span> <span class="p">}</span> <span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="n">BOOL_FALSE</span><span class="p">,</span> <span class="n">IP</span><span class="p">,</span> <span class="p">{</span> <span class="p">.</span><span class="n">ip</span> <span class="o">=</span> <span class="s">&#34;192.168.1.1&#34;</span> <span class="p">}</span> <span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">8088</span><span class="p">,</span> <span class="n">BOOL_FALSE</span><span class="p">,</span> <span class="n">Host</span><span class="p">,</span> <span class="p">{</span> <span class="p">.</span><span class="n">host_name</span> <span class="o">=</span> <span class="s">&#34;http://localhost/&#34;</span> <span class="p">}</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">conns</span><span class="p">)</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">CONN</span><span class="p">));</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 遍历上述 CONN 数组,并打印其中的内容; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Port: %d</span><span class="se">\n</span><span class="s">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Host/Addr: %s</span><span class="se">\n</span><span class="s">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Internal type of `id` is: %s</span><span class="se">\n\n</span><span class="s">&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">conns</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">port</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nf">findAddr</span><span class="p">(</span><span class="o">&amp;</span><span class="n">conns</span><span class="p">[</span><span class="n">i</span><span class="p">]),</span> </span></span><span class="line"><span class="cl"> <span class="kr">typename</span><span class="p">(</span><span class="n">conns</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">id</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">EXIT_SUCCESS</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="入口函数">入口函数</h3> <p>使用 main 函数作为入口函数,返回值为 0 表示成功。</p> <h3 id="数组">数组</h3> <p>conns 即为数组</p> <h3 id="结果体和联合体">结果体和联合体</h3> <p>结果体中所有定义的字段对应内存连续排列 联合体中同时只有一个字段生效,分配的内存为最大字段占用内存</p> <h3 id="控制语句">控制语句</h3> <h3 id="指针">指针</h3> <h3 id="宏">宏</h3> <p>宏函数 typename 使用了 C11标准引入的 _Generic 关键字</p> <h3 id="断言">断言</h3> <p>断言分为静态断言和动态断言,静态断言在代码编译时进行检查</p> <h3 id="函数内联">函数内联</h3> <p>使用 inline 内联关键字,可以建议编译器将该函数的内部逻辑直接替换到函数的 调用位置处,以减少函数调用开销提升性能</p> <h2 id="c-语言编程范式">C 语言编程范式</h2> <p>C 语言属于命令式编程(Imperative Programing),这种范式更关注计算机完成任务所执行的具体步骤;与之对的另外一种编程范式为声明式编程(Declararive Programing),这种范式更倾向于表达计算逻辑。区别实例如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># 命令式 </span></span><span class="line"><span class="cl">#define ARR_LEN 5 </span></span><span class="line"><span class="cl">int main(void) { </span></span><span class="line"><span class="cl"> int arr[ARR_LEN] = { 1, 5, 10, 9, 0 }; </span></span><span class="line"><span class="cl"> for (int i = 0; i &lt; ARR_LEN; ++i) { </span></span><span class="line"><span class="cl"> if (arr[i] &gt; 7) { </span></span><span class="line"><span class="cl"> // save this element somewhere else. </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> return 0; </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"># 声明式 </span></span><span class="line"><span class="cl">let arr = [1, 5, 10, 9, 0] </span></span><span class="line"><span class="cl">let result = arr.filter(n =&gt; n &gt; 7) </span></span></code></pre></td></tr></table> </div> </div><h2 id="编译和运行">编译和运行</h2> <p><img src="https://ppd0705.github.io/image/learn_c/1_1.webp" alt="compile"></p> <ol> <li>代码预处理:编译器会移除所有注释信息、处理宏指令。</li> <li>编译优化:编译器优化源码,将代码编程成汇编格式。</li> <li>汇编:编译器将汇编代码编译成操作系统使用的某种对象格式。</li> <li>链接:编译器将依赖的对象文件进行整合,设置好程序中所有调用函数的正确地址,生成二进制执行文件。</li> </ol> <h1 id="第二讲-数据和量值">第二讲 数据和量值</h1> <h2 id="量值">量值</h2> <p>量值可以粗略地分为变量(Variable)和常量(constant)</p> <h3 id="变量">变量</h3> <p>C 语言变量类型占用的具体字节大小与程序运行所在的硬件体系结构紧密相关</p> <h3 id="常量">常量</h3> <p>在 C 语言中,通过内联方式直接写到源码中的字面量值一般被称为常量。</p> <p>使用 const 关键字修饰的变量更接近于只读变量,不具有”常量表达式“属性,因此无法用来表示定长数组大小或使用在 case 语句中。 常量表达式在编译时被求值,而只读变量在运行时才被确定</p> <h2 id="数据的存储形式">数据的存储形式</h2> <p>计算机内部使用 补码(Two&rsquo;s-complement)存放有符号整数,使用对应的二进制来存放无符号整数,使用 IEEE-754 标准来存放浮点数。</p> <h3 id="补码">补码</h3> <p>一个补码所表示的实际数值,由负权重位的值(最高位)和正权重为的值求和。 如 1101,是及对应的值为 -3(-8 + 5)。 由此也得出四位补码的最小值为 -8(1000),最大值为 7(0111)。</p> <h3 id="ieee-754">IEEE-754</h3> <h2 id="数据的存储位置">数据的存储位置</h2> <p><img src="https://ppd0705.github.io/image/learn_c/2_1.webp" alt="data_store_position"></p> <p>应用程序被正常加载前,需要将应用程序代码机器相关依赖数据映射到内存的某个位置,这段内存称之为进程的VAS(Virtual Address Space 虚拟地址空间).</p> <p>初始化的全局变量和静态变量和应用程序具有同样的生命周期,其值通常会被存放到进程VAS内的 .data 中。</p> <p>局部变量存放于寄存器或者 VAS 的栈中</p> <p>通过 malloc 创建的内存存放于 VAS 的堆中</p> <p>未初始化的全局变量和静态变量存放于 VAS 的 .bss 中</p> <p>常量会按照数据的大小和类型存放于 VAS的 .text (通常存放代码) 或 .rodata (通常存放只读数据) 中</p> <p><img src="https://ppd0705.github.io/image/learn_c/2_2.webp" alt="const_data_store_position"></p> <h1 id="第三讲-运算符">第三讲 运算符</h1> <p>运算符(operator)、表达式(expression)和语句(statement)是组成 C 语言程序的三个基本的语法结构,且一般依次呈包含关系。</p> <h2 id="运算符分类">运算符分类</h2> <p><img src="https://ppd0705.github.io/image/learn_c/3_2.webp" alt="operator_class"></p> <h2 id="算数关系为赋值运算">算数、关系、为、赋值运算</h2> <p>这四类运算符经过编译后,可以直接对应到由目标平台上相应的机器指令组成的简单计算逻辑。</p> <p>拿下图的 foo 函数举例</p> <p><img src="https://ppd0705.github.io/image/learn_c/3_2.webp" alt="foo"></p> <p><code>DWORD PRT [rbp-8]</code> 指 将寄存器 rbp 的值减去 8 得到的结果作为起始地址,然后操作一个 大小为 DWORD 的空间。intel体系中 WORD 表示 16 位,DWROD 表示 32 位,QWORD 表示 64 位。</p> <p><code>int arithmetic = x + y;</code>对应的汇编代码中,前两行代码为分别从栈内存中将变量 x 和 y 存入 寄存器 edx 和 eax,接着通过 add 指令计算两者之和,最后通过 mov 指令将寄存器 eax 的值移到 局部变量 arithmetic 对应的栈内存中。</p> <p>FLAGS 寄存器是一组用于反映程序当前运行状态的标志寄存器,详情如下</p> <p><img src="https://ppd0705.github.io/image/learn_c/3_3.webp" alt="flags_register"></p> <p>cmp 指令会在 CPU 内部对两个操作数进行隐式减法运算,然后设置 FLAGS寄存器状态。</p> <h2 id="逻辑运算符">逻辑运算符</h2> <p>以与运算符 &amp;&amp; 举例,示例代码如下</p> <p><img src="https://ppd0705.github.io/image/learn_c/3_4.webp" alt="and_operator"></p> <p>je 指令会判断 ZF 标志位是否为 0, 如果是则跳转到指定地址</p> <p>与运算的高级编辑优化,编译器会采用 test、setne、movzx来实现, 这种方式减少了对栈内存即条件跳转指令的使用,是的程序减少了 访问内存时产生的延迟,以及由于分支预测失败导致的CPU周期延迟。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-as" data-lang="as"><span class="line"><span class="cl"><span class="nx">test</span> <span class="nx">edi</span><span class="o">,</span> <span class="nx">edi</span> <span class="o">;</span> <span class="nx">edi</span> <span class="o">&lt;-</span> <span class="nx">x</span><span class="p">.</span> </span></span><span class="line"><span class="cl"><span class="nx">setne</span> <span class="nx">al</span> </span></span><span class="line"><span class="cl"><span class="nx">test</span> <span class="nx">esi</span><span class="o">,</span> <span class="nx">esi</span> <span class="o">;</span> <span class="nx">esi</span> <span class="o">&lt;-</span> <span class="nx">y</span><span class="p">.</span> </span></span><span class="line"><span class="cl"><span class="nx">setne</span> <span class="nx">sil</span> </span></span><span class="line"><span class="cl"><span class="nx">movzx</span> <span class="nx">esi</span><span class="o">,</span> <span class="nx">sil</span> </span></span><span class="line"><span class="cl"><span class="nx">and</span> <span class="nx">esi</span><span class="o">,</span> <span class="nx">eax</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="成员访问运算符">成员访问运算符</h2> <p><img src="https://ppd0705.github.io/image/learn_c/3_5.webp" alt="address_operator"></p> <p><code>int* n_ptr = &amp;n;</code>对应的汇编指令中首先 lea 指令将寄存器 rbp 中的值减去 16 后,存放到 rax 寄存器,即将n在站上的地址存放到 rax 寄存器;然后将 rax 寄存器的值存到 变量 n_ptr对应的栈内存的存储位置。</p> <p><code>int m = *n_ptr;</code>对应的汇编指令中首先将 n_ptr的值传送到 寄存器 ra;随后将 rax 的值作为地址,将该地址上的值以 DWORD(即 int)形式传送到 eax 寄存器;最后将 eax 寄存器中的结果只传送到 变量 m 在栈内存的存储位置。</p> <h2 id="其他运算符">其他运算符</h2> <p>这里介绍 <code>sizeof</code> 和 <code>(type)</code>,示例如下:</p> <p><img src="https://ppd0705.github.io/image/learn_c/3_6.webp" alt="other_operator"></p> <p><code>size_t n = sizeof(int)</code> 的汇编指令可以看出直接将结果4存到了 n 对应的栈内存。</p> <p><code>short f = (short) n;</code>的汇编指令首先将变量 n 的值移到 rax 寄存器,然后将其中低 16位的数据(ax)地道 f 所在的内存趋于</p> <h1 id="第四讲-控制逻辑">第四讲 控制逻辑</h1> <h2 id="表达式">表达式</h2> <p>表达式(expression)是由一系列运算符和操作数(operand)组成的一种语法结构。</p> <p>对表达式的求值过程,实际上就是根据运算符的优先级和结合性,来对表达式和它所包含的子表达式进行递归求值的过程。</p> <p>可以使用<code>clang -Xclang -ast-dump -fsyntax-only main.c</code> 命令得到 C 程序对应的 AST 结构</p> <h2 id="语句">语句</h2> <p>语句(statement)是用来描述程序的基本构建块。</p> <p>语句可以包含或者不含表达式;语句在执行时不返回任何结果;语句以分号结尾。</p> <p>语句分为复合语句、表达式语句、选择语句、迭代语句、跳转语句。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 复合语句; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">;</span> <span class="c1">// 表达式语句; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">sum</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 复合语句; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">sum</span> <span class="o">=</span> <span class="o">-</span><span class="n">sum</span><span class="p">;</span> <span class="c1">// 表达式语句; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">sum</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="选择语句">选择语句</h3> <p><code>if...else</code> 语句示例如下:</p> <p><img src="https://ppd0705.github.io/image/learn_c/4_1.webp" alt="if_else_statement"></p> <p><code>switch...case</code>语句示例如下:</p> <p><img src="https://ppd0705.github.io/image/learn_c/4_2.webp" alt="switch_case_statement"></p> <h3 id="迭代语句">迭代语句</h3> <p>迭代语句主要包含 <code>do...while</code>、<code>for</code>、<code>while</code> 三种形式,u示例如下: <img src="https://ppd0705.github.io/image/learn_c/4_3.webp" alt="do_while_statement"></p> <h3 id="跳转语句">跳转语句</h3> <p>跳转语句主要包含 <code>break</code>、<code>continue</code>、<code>return</code>、<code>goto</code> 四种形式</p> <h1 id="第五讲-函数调用上">第五讲 函数调用(上)</h1> <h2 id="快速回顾">快速回顾</h2> <p>借用函数,可以将一个程序的实现过程拆分为多个子步骤,并以结构化的方式来构建程序。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;tgmath.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">y</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">Point</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">,</span> <span class="n">Point</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">int</span><span class="p">(</span><span class="n">handler</span><span class="p">)(</span><span class="kt">int</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="nf">handler</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">+</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">+</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">y</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">handler</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="nf">sqrt</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">Point</span> <span class="n">p</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="mi">10</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">p</span><span class="p">,</span> <span class="n">handler</span><span class="p">));</span> <span class="c1">// 5. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>函数类型的参数默认以指针传递,可以省略表明指针类型的<code>*</code>符号</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">,</span> <span class="n">Point</span><span class="o">*</span> <span class="n">p</span><span class="p">,</span> <span class="kt">int</span><span class="p">(</span><span class="o">*</span><span class="n">handler</span><span class="p">)(</span><span class="kt">int</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span><span class="o">*</span><span class="n">handler</span><span class="p">)(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">+</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">+</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">y</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="c-函数的调用约定">C 函数的调用约定</h2> <p>我们将编译器实现函数调用所遵循的一系列规则称之为函数的调用约定(Calling Convention)</p> <p>调用约定并非 C 语言标准的一部分,不同平台有不同的标准, Unix系统使用 System V AMD64 ABI (简称 SysV)的调用约定</p> <p>SysV 函数调用示例如下:</p> <p><img src="https://ppd0705.github.io/image/learn_c/5_1.webp" alt="sysv_calling_convention"></p> <p>函数调用是通过 call 指令来完成,每个函数执行完毕后通过 ret 指令 来退出函数的执行,并转移代码执行流程到之前函数调用指令的下一条指令上</p> <p><img src="https://ppd0705.github.io/image/learn_c/5_2.webp" alt="call_and_ret"></p> <h2 id="参数约定">参数约定</h2> <p>对于整形和指针类型的实参,需要分别使用寄存器 rdi、rsi、rdx、rcx、r8、r9,按函数定义是参数从左到右的顺序进行传值。如果参数超过 6 个, 则余下参数通过栈内存进行传送,多出来的参数从右到左入栈</p> <p>对于浮点数,使用 xmm0 到 xmm7 共 8 个寄存器进行存储。 对于更宽的值,也可能使用 ymm 与 zmm 寄存器来代替 xmm 寄存器。</p> <h2 id="返回值传递">返回值传递</h2> <p>整数类型返回值,小于 64 位,使用 rax 寄存器(32 位的别名为 eax ),小于 128 位,使用 rax和 rdx 分别返回低 64 位和高 64 位</p> <p>浮点数会使用 xmm、ymm、zmm 寄存器。</p> <h2 id="寄存器使用">寄存器使用</h2> <p>对于 rbx、rbp、rsp 和 r12 ~ r15 寄存器,若函数需要使用它们, 需要使用前暂存,退出前恢复。</p> <h2 id="堆栈清理">堆栈清理</h2> <p>每个函数在结束前,需要清理自身的堆栈,可以通过 leave 指令完成</p> <h2 id="其他约定">其他约定</h2> <ul> <li>函数在 call 调用前, 需要保证栈顶地址值 16 字节对齐</li> <li>从栈顶向上保留128 字节作为 “Red Zone”</li> <li>系统调用使用寄存器 rdi、rsi、rdx、r10、r8 和 r9 传递参数</li> </ul> <p>Red Zone 是位于栈顶向上的一段固定长度的内存段,这块区域可以被调用函数栈中的 “叶子” 函数(即不再调取其他函数的函数)使用,这样在需要额外栈内存时,就能省去调整栈内存大小的过程。</p> <h2 id="保存函数调用信息的栈帧">保存函数调用信息的栈帧</h2> <p>我们将栈内存数据块称之为帧栈,帧栈存放有返回地址、实参 局部地址、返回值和暂存的寄存器值。</p> <p>在进程内存中,栈内存是从高向低增长的,即栈底位于高地址处,栈顶位于 低地址处。</p> <p>rsp 寄存器又称之为 Stack Pointer,其存放着栈顶地址,即其决定了 栈内存大小,通过减小其存储的值,就能扩大栈内存。</p> <p><img src="https://ppd0705.github.io/image/learn_c/5_3.webp" alt="stack_memory"></p> <p>bar 函数第一行指令 <code>push rbp</code> 会将当前 rbp 寄存器的值存到栈中, rbp 寄存器 又称为 Frame Pointer, 通常来存储调用前的栈高度,即 rsp的旧值,以便进行帧栈寻址,并在退出前将栈中数据恢复到调用前的状态; 第二行指令<code>mov rbp, rsp</code>便是将 rsp 的值保存到 rbp 中; 第三行指令<code>mov eax, 10</code> 将结果存入 eax 寄存器; 第四行指令<code>pop rbp</code> 恢复 rbp 寄存器的值; 第五行指令<code>ret</code> 将程序的执行转移到函数调用前。</p> <p>main 函数第 29 行指令<code>sub rsp, 16</code>减小 rsp 的值,将栈空间扩大 16 字节; 第 30、31 行指令分别将 1 和 2 存入 rbp-4 和 rbp-8 的位置; 第 34、35 行指令,借助 push, 将 8 和 7 存入 rbp-12 、 rbp-16的位置。</p> <p>leave 指令会恢复 rsp的值来“清理”栈数据,并恢复 rbp的值。 等效于: <code>mov rsp, rbp; pop rbp</code>; 与之对应的 enter 指令等效于 <code>push rbp; mov rpb, rsp</code></p> <p><img src="https://ppd0705.github.io/image/learn_c/5_4.webp" alt="main_func_memory"></p> <h1 id="第六讲-函数调用下">第六讲 函数调用(下)</h1> <h2 id="函数参数求值顺序">函数参数求值顺序</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d %d %d&#34;</span><span class="p">,</span> <span class="n">n</span><span class="o">++</span><span class="p">,</span> <span class="n">n</span><span class="o">++</span><span class="p">,</span> <span class="n">n</span><span class="o">++</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>如上示例程序,C 函数参数的求值顺序并没有被明确规定,有的编译器按从左到右计算,有的从左到右计算。</p> <p>为了程序健壮性,不要编写需要依赖特定函数参数求值顺序的代码。</p> <h2 id="尾递归调用优化tail-call-optimization">尾递归调用优化(tail-call optimization)</h2> <p><img src="https://ppd0705.github.io/image/learn_c/6_1.webp" alt="factorial_function"></p> <p>函数调用过程所需数据是以帧栈的形式存到到进程的栈内存中,而栈内存的清理工作是在函数准备ret 返回前,通过 leave指令进行。 对于正常的递归函数,由于函数不断调用自己,产生的帧栈越来越多,可能导致内存溢出;另外函数调用会创建和销毁帧栈,这也是消耗性能的。</p> <p>尾递归调用优化是指在一定条件下,编译器直接利用跳转指令代替函数调用指令,来模拟函数调用过程,这样便可省去函数调用帧栈的不断创建和销毁, 而且也只使用了有限的栈内存。</p> <p>尾递归调用优化一个前提条件是:递归语句必须是在函数返回前的最后一条语句。在这种情况下,编译器才能确定函数的返回值没有被上一个帧栈所使用。</p> <p><img src="https://ppd0705.github.io/image/learn_c/6_2.webp" alt="factorial_function2"></p> <p>上面的函数开启最高编译优化等级”-O3“后,会使用尾递归优化, 在执行 ret 前,会判断寄存器 edi 的值是否为0 (ZF=1),来决定调转</p> <h2 id="kr-函数声明">K&amp;R 函数声明</h2> <p>K&amp;R 函数声明可能不知道函数的参数列表,如下所示:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">add</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="nf">add</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="c1">// ? </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">add</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>这样导致参数是一个随机值,不建议使用。</p> <h1 id="第七讲-枚举结构和联合是如何实现的">第七讲 枚举、结构和联合是如何实现的?</h1> <h2 id="枚举-enumeration">枚举 Enumeration</h2> <p><img src="https://ppd0705.github.io/image/learn_c/7_1.webp" alt="enum"></p> <p>从汇编代码可以看出自定义枚举类型都是以 int 类型存储的,枚举值 Mon 在底层是由 0 表示。</p> <p>C 标准直接将枚举值当做整数进行处理,这样导致foo 函数在调用时,实际上允许传入任何可以被隐式转换为 int 类型的值。</p> <h2 id="结构-struct">结构 Struct</h2> <p>结构和数组类型,都是使用连续的内存存放数据,不过结构可以存放不同的类型的数据。</p> <p><img src="https://ppd0705.github.io/image/learn_c/7_2.webp" alt="struct"></p> <p>本质上,结构只是对内部所包含的各类数据的一个封装,因此只需要它分装的这些数据放在连续的内存装即可。</p> <p><img src="https://ppd0705.github.io/image/learn_c/7_3.webp" alt="struct_stack_memory"></p> <p>如上图的栈内存所示,字符转指针 p 位于 [rbp-32] 处,占用 8 个字节,字符 c 位于 [rbp-24] 处,占用 1 个字节,长整形 x 位于 [rbp-16]处,占用 8 个字节。</p> <h3 id="内存数据对齐">内存数据对齐</h3> <p>自然对齐是指被操作数据所在的地址为该数据大小的整数倍,当内存中的数据 满足自然对齐时,CPU通常能够以最高的效率进行数据操作。</p> <h3 id="填充字节">填充字节</h3> <p>某些情况即使结果对象各个数据成员都满足自然对齐的要求,额外的填充字节也会被添加,如下示例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">Foo</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span> <span class="c1">// 8 bytes. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">char</span> <span class="n">c</span><span class="p">;</span> <span class="c1">// 1 bytes. </span></span></span><span class="line"><span class="cl"><span class="c1">// (padding): 7 bytes. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div><p>之所以会这样,因为编译器想保证:当结构体被连续存放时,前一个对象结束的位置正好可以满足后一个对象的起始位置时自然对齐的要求。 这样也就要求结构对象本身的大小必须是内部最大成员大小的整数倍。</p> <h2 id="联合-union">联合 Union</h2> <p>联合和结构语法类型,只要把 关键字从 struct 改为 union。 顾名思义,联合就是所有字段共同使用一个内存区域。</p> <p>联合对象的大小与内部定义最大成员的大小相同。</p> <p>对一个单独的联合对象来说,哪个字段在生效我们无从得知,所以需要一个标签字段来配合指明正在生效的字段,这样模式叫做“Tagged Union”.</p> <p><img src="https://ppd0705.github.io/image/learn_c/7_4.webp" alt="union"></p> <h1 id="第八讲-指针是如何灵活使用内存的">第八讲 指针是如何灵活使用内存的</h1> <h2 id="指针的基本使用">指针的基本使用</h2> <p>通过 类型说明符加 * 符号可以定义一个指向该数据类型的指针。 通过 const 关键字可以限制指针变量的行为</p> <p><img src="https://ppd0705.github.io/image/learn_c/8_1.webp" alt="pointer_basic_usage"></p> <h2 id="指针和数组">指针和数组</h2> <p><img src="https://ppd0705.github.io/image/learn_c/8_2.webp" alt="pointer_and_array"></p> <p>从上图看出,数据中的元素被分配在连续的栈内存中。</p> <p>当 arr 作为实参传入函数 sum 后,实际传入的是一个指向 int 类型的指针,有关 arr 的大小和类型都全部丢失,这种情况称之为”数组的退化“。</p> <h2 id="指针的其他运算">指针的其他运算</h2> <h3 id="算术运算">算术运算</h3> <p><img src="https://ppd0705.github.io/image/learn_c/8_3.webp" alt="pointer_math_operation"></p> <p>当我们对指针进行加减运算时,编译器是以当前指针所指向值对应的某个固定 长度为单位,对指针中存放的地址值进行相应调整的。</p> <h3 id="关系运算">关系运算</h3> <p>大多数情况,编译器会使用 cmp 和 setg 等指令来判断关系运算两侧操作数的大小</p> <h2 id="堆内存指针">堆内存指针</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define N 5 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">arr</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 分配用于存放 N 个整数的堆内存; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">int</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o">*</span> <span class="n">N</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 将数组 arr 中的元素复制到分配的堆内存中; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">memcpy</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">arr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o">*</span> <span class="n">N</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 通过指针遍历堆空间中的数据; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">p</span> <span class="o">+</span> <span class="n">i</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 释放先前分配的堆空间,让操作系统可以回收内存; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">free</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>在 VAS 中,堆内存位于栈内存的下方,堆内存是从低地址向高地址逐渐增长</p> <p><img src="https://ppd0705.github.io/image/learn_c/8_4.webp" alt="heap_memory"></p> <p>堆内存可以动态创建,可以保持和程序相同的生命周期。 另外和全局变量、静态变量这种将值完全暴露给所有程序代码相比, 使用堆内存可以将数据的使用限制在所需的最小范围内,加强了程序对内存的精细化管理程度。</p> <h1 id="第九讲-预处理器">第九讲 预处理器</h1> <h2 id="预处理流程">预处理流程</h2> <p>预处理流程如下:</p> <ol> <li>删除代码注释</li> <li>处理宏定义 #define,进行展开和替换</li> <li>处理条件预编译 #if、#elif,仅保留符合条件的代码</li> <li>处理文件包含预编译指令 #include,将被包含的文件内容插入到指令所在位置</li> <li>处理其他可识别的预处理指令如 #program</li> <li>添加其他具有辅助性功能的注释信息</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#pragma GCC warning &#34;Just FYI!&#34; </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdbool.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define PI 3.14 </span></span></span><span class="line"><span class="cl"><span class="cp">#define SQUARE(x) (x * x) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#if defined PI </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="c1">// Some specific calculations. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="kt">double</span> <span class="n">area</span> <span class="o">=</span> <span class="nf">SQUARE</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">*</span> <span class="n">PI</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">bool</span> <span class="n">isAreaGT100</span> <span class="o">=</span> <span class="n">area</span> <span class="o">&gt;</span> <span class="mf">100.0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>使用<code>gcc -O0 -Wall -E exmaple.c -o example.I</code> 得到预处理后的代码如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp"># 1 &#34;macro.c&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"># 1 &#34;&lt;built-in&gt;&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"># 1 &#34;&lt;command-line&gt;&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"># 31 &#34;&lt;command-line&gt;&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"># 1 &#34;/usr/include</span><span class="cpf">/stdc-predef.h&#34; 1 3 4</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"># 32 &#34;&lt;command-line&gt;&#34; 2 </span></span></span><span class="line"><span class="cl"><span class="cp"># 1 &#34;macro.c&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="cp"># 1 &#34;/usr/lib/gcc/x86_64-redhat-linux/8/include</span><span class="cpf">/stdbool.h&#34; 1 3 4</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"># 3 &#34;macro.c&#34; 2 </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">double</span> <span class="n">area</span> <span class="o">=</span> <span class="p">(</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">5</span><span class="p">)</span> <span class="o">*</span> <span class="mf">3.14</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> </span></span><span class="line"><span class="cl"><span class="cp"># 8 &#34;macro.c&#34; 3 4 </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="n">_Bool</span> </span></span><span class="line"><span class="cl"><span class="cp"># 8 &#34;macro.c&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="n">isAreaGT100</span> <span class="o">=</span> <span class="n">area</span> <span class="o">&gt;</span> <span class="mf">100.0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="宏函数常用技巧">宏函数常用技巧</h2> <h3 id="为返回值添加括号">为返回值添加括号</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// #define FOO(x) 1 + x * x </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">define</span> <span class="nf">FOO</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="mi">3</span> <span class="o">*</span> <span class="nf">FOO</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="为参数添加括号">为参数添加括号</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// #define FOO(x) (1 + x * x) </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">define</span> <span class="nf">FOO</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">x</span><span class="p">))</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="mi">3</span> <span class="o">*</span> <span class="nf">FOO</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="警惕多次副作用">警惕多次副作用</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define FOO(x) (1 + (x) * (x)) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="nf">FOO</span><span class="p">(</span><span class="o">++</span><span class="n">i</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="定义完备的多语句宏函数">定义完备的多语句宏函数</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// #define SAY() printf(&#34;Hello, &#34;); printf(&#34;world!&#34;) </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"><span class="cp">#define SAY() do { printf(&#34;Hello, &#34;); printf(&#34;world!&#34;)} while (0) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">input</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">scanf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">input</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">SAY</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十讲-字符字符串与数学计算">第十讲 字符、字符串与数学计算</h1> <p><img src="https://ppd0705.github.io/image/learn_c/10_1.webp" alt="c_std_library"></p> <h2 id="字符和字符串">字符和字符串</h2> <p>在 C 语言中,字符用单引号表示,字符串用双引号表示</p> <h3 id="字符">字符</h3> <p>C 语言保证 char 类型只占用一个字节大小。</p> <p>大多数情况编译器会选择 char 类型视为有符号整数类型。</p> <h3 id="字符串">字符串</h3> <p>字符串可以使用指针或者数组形式表示。</p> <p>连续出现的字符串之间如果仅有空格分隔,则会将它们视为一个整体。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// read-only string. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">char</span> <span class="n">strA</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;Hello, geek!&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">strB</span> <span class="o">=</span> <span class="s">&#34;Hello&#34;</span> <span class="s">&#34;, geek!&#34;</span><span class="p">;</span> </span></span></code></pre></td></tr></table> </div> </div><p>上述字符串在内存的布局如下:</p> <p><img src="https://ppd0705.github.io/image/learn_c/10_2.webp" alt="string_memory_layout"></p> <p>字符串被存放在连续的内存段上,且每个字符串最后都以一个空字符作为终止符。</p> <p>使用数组和指针形式定义的字符串,其底层的数据引用方式会有所区别。 其中数组方式会将字符串从 .rodata 中拷贝到其他位置(比如栈内存),因此修改这些这些数据不会改变原始的 .rodata 中的副本;而使用指针形式时指针会直接引用位于 .rodata 中的字符串数据,因此通过指针修改字符串的值会影响相同指向的指针字符串。</p> <h2 id="字符库函数">字符库函数</h2> <h3 id="统计字符串长度">统计字符串长度</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="n">str</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;Hi&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">str</span><span class="p">));</span> <span class="c1">// 2. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>strlen 函数不会计入字符串多余的终止符。</p> <h3 id="拼接字符串">拼接字符串</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define STRLEN 14 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">strA</span><span class="p">[</span><span class="n">STRLEN</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#34;Hello,&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">strB</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34; world!&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">strncat</span><span class="p">(</span><span class="n">strA</span><span class="p">,</span> <span class="n">strB</span><span class="p">,</span> <span class="n">STRLEN</span> <span class="o">-</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">strA</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">strA</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>strncat 可控制被拼接字符串的长度</p> <h3 id="拷贝字符串">拷贝字符串</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">strA</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;aaaaaa&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">strB</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;bbbbbbb&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">strncpy</span><span class="p">(</span><span class="n">strA</span><span class="p">,</span> <span class="n">strB</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">strA</span><span class="p">)));</span> <span class="c1">// &#34;bbbbbb&#34;. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="格式化字符串">格式化字符串</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define LEN 128 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">dest</span><span class="p">[</span><span class="n">LEN</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="n">strA</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;Hello, &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">sprintf</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="s">&#34;%sworld!&#34;</span><span class="p">,</span> <span class="n">strA</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">dest</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>sprintf 会将格式化后的字符串保存到第一个参数传入的数组中</p> <h3 id="字符的判断和转换">字符的判断和转换</h3> <p>ctype.h 中包含众多用于字符的函数</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;ctype.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">c</span> <span class="o">=</span> <span class="sc">&#39;a&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">isalnum</span><span class="p">(</span><span class="n">c</span><span class="p">));</span> <span class="c1">// 1. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">isalpha</span><span class="p">(</span><span class="n">c</span><span class="p">));</span> <span class="c1">// 1. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">isblank</span><span class="p">(</span><span class="n">c</span><span class="p">));</span> <span class="c1">// 0. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">isdigit</span><span class="p">(</span><span class="n">c</span><span class="p">));</span> <span class="c1">// 0. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%c</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">toupper</span><span class="p">(</span><span class="n">c</span><span class="p">));</span> <span class="c1">// &#39;A&#39;. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="数学运算库函数">数学运算库函数</h2> <p>math.h 和 stdlib.h 包含了众多数学运算函数</p> <p>thmath.h 里面提供了泛型办的数学运算函数,大体思路是通过宏来判断参数类型进而转换到正确类型的底层函数去,详情参考<a href="https://git.musl-libc.org/cgit/musl/tree/include/tgmath.h">实现</a></p> <h1 id="第十一讲-io-标准库">第十一讲 IO 标准库</h1> <p>C 语言采用标准库 stdio 的方式,提供对 I/O 相关接口的支持</p> <h2 id="基本使用">基本使用</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Enter some characters:</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">FILE</span><span class="o">*</span> <span class="n">fp</span> <span class="o">=</span> <span class="nf">fopen</span><span class="p">(</span><span class="s">&#34;./temp.txt&#34;</span><span class="p">,</span> <span class="s">&#34;w+&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">fp</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">ch</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nf">scanf</span><span class="p">(</span><span class="s">&#34;%c&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">ch</span> <span class="o">==</span> <span class="sc">&#39;z&#39;</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">putc</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">perror</span><span class="p">(</span><span class="s">&#34;File open failed.&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">fclose</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><ul> <li>通过 printf 将指定文本传送到标准输出流 stdout</li> <li>通过 fopen 打开指定的文件,并将其与一个特定的文件 IO 流关联</li> <li>通过 perror 将指定错误信息传送到标准错误流 stderr</li> <li>通过 scanf 从 标准输入流 stdin 读取输入的信息</li> <li>通过 putc 将 字符写入指定文件</li> </ul> <h2 id="接口级别">接口级别</h2> <p>IO 接口一般分为两个级别:</p> <ul> <li>标准 IO:ISO C 标准定义的一些列接口,实现与具体操作系统无关</li> <li>低级 IO:使用具体操作系统相关的一系列底层接口来提供相应的 IO 能力</li> </ul> <p>上面的代码使用低级 IO 接口实现如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="n">str</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;Enter some characters:</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">STDOUT_FILENO</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">str</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="s">&#34;./temp.txt&#34;</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_CREAT</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">ch</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nf">read</span><span class="p">(</span><span class="n">STDIN_FILENO</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">ch</span> <span class="o">==</span> <span class="sc">&#39;z&#39;</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ch</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="n">errMsg</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;File open failed.&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">STDERR_FILENO</span><span class="p">,</span> <span class="n">errMsg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">errMsg</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>和低级 IO 相比,标准 IO 会为我们提供带缓冲的输入和输出操作。</p> <h2 id="低级-io-背后的系统调用">低级 IO 背后的系统调用</h2> <p>低级 IO 接口通过系统调用来完成相应的 IO 操作,与调用用户函数不同使用 call 指令 不同,在 x86-64 平台,通过 syscall 指令来执行一个系统调用函数。</p> <p>每个系统调用函数有一个唯一整形 ID, open 函数的 ID 为 2。 SyxV 调用约定使用寄存器 rdi、rsi、rdx、r10、r8、r9 来进行实参的传递,寄存器 rax 用于存放系统调用对应的 ID,并接收系统调用的结果。</p> <p>上面的代码使用汇编改造如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="n">str</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;Enter some characters:</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">STDOUT_FILENO</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">str</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">fileName</span> <span class="o">=</span> <span class="s">&#34;./temp.txt&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Call to `open` starts: </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// const int fd = open(&#34;./temp.txt&#34;, O_RDWR | O_CREAT); </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">volatile</span> <span class="kt">int</span> <span class="n">fd</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">asm</span><span class="p">(</span><span class="s">&#34;mov $2, %%rax</span><span class="se">\n\t</span><span class="s">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mov %0, %%rdi</span><span class="se">\n\t</span><span class="s">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mov $66, %%rsi</span><span class="se">\n\t</span><span class="s">&#34;</span> <span class="c1">// 2 | 64 -&gt; 66; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s">&#34;syscall</span><span class="se">\n\t</span><span class="s">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;mov %%rax, %1</span><span class="se">\n\t</span><span class="s">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="s">&#34;=m&#34;</span> <span class="p">(</span><span class="n">fileName</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="s">&#34;m&#34;</span> <span class="p">(</span><span class="n">fd</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Call ended. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">ch</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="nf">read</span><span class="p">(</span><span class="n">STDIN_FILENO</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">ch</span> <span class="o">==</span> <span class="sc">&#39;z&#39;</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ch</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span> <span class="n">errMsg</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;File open failed.&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">write</span><span class="p">(</span><span class="n">STDERR_FILENO</span><span class="p">,</span> <span class="n">errMsg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">errMsg</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>上面的代码中,将 ID 2 存入 寄存器 rax, 将 fileName 首地址存入寄存器 rdi,将 文件操作模式 66 存入寄存器 rsi,调用结果 从 寄存器 rax 存入 局部变量 fd。</p> <h1 id="第十二讲-非本地跳转和可变参数实现原理">第十二讲 非本地跳转和可变参数实现原理</h1> <h2 id="本地跳转">本地跳转</h2> <p><img src="https://ppd0705.github.io/image/learn_c/12_1.webp" alt="local_jump"></p> <p>本地跳转一般指由 goto 语句完成的程序执行流的转移过程</p> <h2 id="setjmp-和-longjmp-函数">setjmp 和 longjmp 函数</h2> <p><img src="https://ppd0705.github.io/image/learn_c/12_2.webp" alt="long_jump"></p> <p>如上图所示,在调用 longjmp 之后,程序会调到 call setjmp 下一条指令, 这种跳转为我们提供了一种可以暂存函数调用状态并在未来某个时刻再恢复的能力。</p> <h3 id="运作原理">运作原理</h3> <p>setjmp 函数在执行时,会将程序此刻的调用环境信息存储在由其第一个参数指定的 jmp_buf 类型的对象中,并同时将 0 作为结果返回,后续当程序执行到 longjmp 时, 该函数便回从同一个 jmp_buf 对象中再次恢复之前保存的函数调用上下文,通过这种方式,程序的执行流程得到了重置。</p> <p>SysV 调用约定,属于 Callee-saved 类型的寄存器信息需要在 call 指令调用时,由被调用函数 caller 负责保存和恢复。</p> <h3 id="自定义实现">自定义实现</h3> <p>setjmp 汇编如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="na">.global</span> <span class="no">setjmp</span> <span class="c1"># 将 setjmp 暴露给链接器 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="na">.intel_syntax</span> <span class="no">noprefix</span> <span class="c1"># 使用汇编语法 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nl">setjmp:</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="p">],</span> <span class="no">rbx</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x8</span><span class="p">],</span> <span class="no">rbp</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x10</span><span class="p">],</span> <span class="no">r12</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x18</span><span class="p">],</span> <span class="no">r13</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x20</span><span class="p">],</span> <span class="no">r14</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x28</span><span class="p">],</span> <span class="no">r15</span> </span></span><span class="line"><span class="cl"> <span class="nf">lea</span> <span class="no">rdx</span><span class="p">,</span> <span class="p">[</span><span class="no">rsp</span><span class="err">+</span><span class="mi">0x8</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x30</span><span class="p">],</span> <span class="no">rdx</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">rdx</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rsp</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x38</span><span class="p">],</span> <span class="no">rdx</span> </span></span><span class="line"><span class="cl"> <span class="nf">xor</span> <span class="no">eax</span><span class="p">,</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="nf">ret</span> </span></span></code></pre></td></tr></table> </div> </div><p>由 SysV 规定,寄存器 rdi 接收第一个实参,所以 rdi 保存着 jmp_buf 的首地址, jmp_buf 可以看做是一个具有足够大小的字节数组。</p> <p>在 4~9 行我们将寄存器 rdx、rbp、r12、r13、r14、r15 的值进行保存, 在 10~11 行我们将 setjmp 调用之前的 rsp 寄存器进行了暂存, 在 12~13 行我们将 setjmp 调用后的地址进行暂存,这个地址将由 longjmp 使用</p> <p>longjmp 汇编如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="na">.global</span> <span class="no">longjmp</span> </span></span><span class="line"><span class="cl"><span class="na">.intel_syntax</span> <span class="no">noprefix</span> </span></span><span class="line"><span class="cl"><span class="nl">longjmp:</span> </span></span><span class="line"><span class="cl"> <span class="nf">xor</span> <span class="no">eax</span><span class="p">,</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="nf">cmp</span> <span class="no">esi</span><span class="p">,</span> <span class="mi">0x1</span> </span></span><span class="line"><span class="cl"> <span class="nf">adc</span> <span class="no">eax</span><span class="p">,</span> <span class="no">esi</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">rbx</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">rbp</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x8</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">r12</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x10</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">r13</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x18</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">r14</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x20</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">r15</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x28</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">rsp</span><span class="p">,</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x30</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">jmp</span> <span class="no">QWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="err">+</span><span class="mi">0x38</span><span class="p">]</span> </span></span></code></pre></td></tr></table> </div> </div><p>在 4~5 行我们将对 long_jmp 的第二个参数做处理,如果实参为 0,将其改为 1,这样做是为了能够通过寄存器 rax 中的值区分当前代码是在 setjmp 函数调用后首次执行的,还是 long_jmp 恢复后执行的</p> <p>将汇编代码编译成对象文件</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gcc -c setjmp.s -o setjmp.o </span></span><span class="line"><span class="cl">gcc -c longjmp.s -o longjmp.o </span></span></code></pre></td></tr></table> </div> </div><p>完整代码</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdnoreturn.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// 定义 jmp_buf 类型; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="kt">long</span> <span class="n">jmp_buf</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="c1">// 提供函数原型; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">setjmp</span><span class="p">(</span><span class="n">jmp_buf</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="n">noreturn</span> <span class="kt">void</span> <span class="nf">longjmp</span><span class="p">(</span><span class="n">jmp_buf</span><span class="p">,</span> <span class="kt">int</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="c1">// 原始 C 示例程序代码; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">jmp_buf</span> <span class="n">jb</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">noreturn</span> <span class="kt">void</span> <span class="nf">inspect</span><span class="p">(</span><span class="kt">char</span> <span class="n">val</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">putchar</span><span class="p">(</span><span class="n">val</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">longjmp</span><span class="p">(</span><span class="n">jb</span><span class="p">,</span> <span class="n">val</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">volatile</span> <span class="kt">char</span> <span class="n">count</span> <span class="o">=</span> <span class="sc">&#39;A&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">setjmp</span><span class="p">(</span><span class="n">jb</span><span class="p">)</span> <span class="o">&lt;</span> <span class="sc">&#39;J&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">inspect</span><span class="p">(</span><span class="n">count</span><span class="o">++</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>编译、测试</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gcc main.c setjmp.o longjmp.o -o main <span class="o">&amp;&amp;</span> ./main </span></span></code></pre></td></tr></table> </div> </div><p>通常非本地跳转主要用来实现异常处理、协程等功能</p> <p>使用非本地调整实现的 try &hellip; catch 可以参考<a href="http://groups.di.unipi.it/~nids/docs/longjump_try_trow_catch.html">longjump_try_trow_catch</a></p> <h2 id="可变参数">可变参数</h2> <h3 id="基本使用-1">基本使用</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdarg.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">void</span> <span class="nf">print_sum</span><span class="p">(</span><span class="kt">int</span> <span class="n">count</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">va_list</span> <span class="n">ap</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">va_start</span><span class="p">(</span><span class="n">ap</span><span class="p">,</span> <span class="n">count</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">+=</span> <span class="nf">va_arg</span><span class="p">(</span><span class="n">ap</span><span class="p">,</span> <span class="kt">int</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">va_end</span><span class="p">(</span><span class="n">ap</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">print_sum</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="运作原理-1">运作原理</h3> <p><a href="https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf">SysV ABI</a> 规定了可变参数列表的实现要求</p> <p>首先实参将会按类型按顺序依次存入寄存器, 接着一块名为 Register Save Area 的 栈内存被构建,每个通过寄存器传入的实参值都会按照 rdi、rsi、rdx、rcx、r8、r9、xmm0~15 的寄存器先后顺序拷贝到 RSA 中, 接下来创建 结构体 va_list</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">gp_offset</span><span class="p">;</span> <span class="c1">// 下一个整型数据相较于 RSA 的偏移; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">fp_offset</span><span class="p">;</span> <span class="c1">// 下一个浮点数据相较于 RSA 的偏移; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">void</span> <span class="o">*</span><span class="n">overflow_arg_area</span><span class="p">;</span> <span class="c1">// 指向使用栈进行传递的数据; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">void</span> <span class="o">*</span><span class="n">reg_save_area</span><span class="p">;</span> <span class="c1">// 指向 RSA 的指针; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="n">va_list</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span> </span></span></code></pre></td></tr></table> </div> </div><p><code>va_start(ap, count)</code> 语句对 ap 进行了初始化,reg_save_area 指向了 RSA 起始位置,gp_offset 设置为 从 RSA 中读整数实参相对 reg_save_area 的偏移</p> <p><code>va_arg(ap, int)</code> 语句根据 va_list 及提取参数的类型从 RSA 中取出相应的数据值</p> <p><code>va_end(ap)</code> 释放了 ap</p> <h1 id="第十三讲-c-并发编程">第十三讲 C 并发编程</h1> <p>C11 标准加入了 thread.h 和 stdatomic.h 标准库,提供了一套通用的并发编程接口</p> <h2 id="进程-vs-线程">进程 vs 线程</h2> <p>默认情况,操作系统会为每一个运行的程序创建一个相应的进程,作为程序的运行实例。 进程中包含一系列运行时信息,比如 VAS、PID、 处理器上下文(如通用寄存器和指令寄存器的值),进程状态分配调度相关资源。这些信息被放在 <code>进程控制块 PCB</code> 数据结构中。</p> <p>相比进程,线程提供了更细粒度的运行单元,线程在共享程序运行资源的情况下,负责程序某个子任务的具体执行过程。线程的状态信息被放在 <code>线程控制块 TCB</code> 数据结构中</p> <p><img src="https://ppd0705.github.io/image/learn_c/13_1.webp" alt="thread1"></p> <h2 id="线程的基本控制">线程的基本控制</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">thrd_t</span> <span class="n">id</span> <span class="o">=</span> <span class="nf">thrd_current</span><span class="p">();</span> <span class="c1">// 返回该函数运行所在线程的标识符; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">((</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span><span class="p">)</span> <span class="n">arg</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">thrd_success</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#ifndef __STDC_NO_THREADS__ </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="kt">thrd_t</span> <span class="kr">thread</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">result</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 创建一个线程; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="kr">thread</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="s">&#34;Hello C11 thread with id: %lu.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">thrd_join</span><span class="p">(</span><span class="kr">thread</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">result</span><span class="p">)</span> <span class="o">==</span> <span class="n">thrd_success</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 等待其他线程退出; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Thread returns %d at the end.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>其他线程控制函数如下表:</p> <p><img src="https://ppd0705.github.io/image/learn_c/13_2.webp" alt="thread2"></p> <h2 id="数据竞争">数据竞争</h2> <p>数据竞争 Data Race 是指在一个多线程的环境中,有两个及以上的线程同一时间对同一块内存的数据进行了非原子操作,示例如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_COUNT 20 </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_LOOP 100000000 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">long</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 全局变量,用来记录线程的累加值; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_LOOP</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span><span class="o">++</span><span class="p">;</span> <span class="c1">// 在线程中递增全局变量的值; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Thread %d terminates.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="p">((</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span> <span class="n">data</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">thrd_success</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#ifndef __STDC_NO_THREADS__ </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="kt">int</span> <span class="n">ids</span><span class="p">[</span><span class="n">THREAD_COUNT</span><span class="p">];</span> <span class="c1">// 用于存放线程序号的数组; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">thrd_t</span> <span class="n">threads</span><span class="p">[</span><span class="n">THREAD_COUNT</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ids</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">run</span><span class="p">,</span> <span class="n">ids</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span> <span class="c1">// 创建 THREAD_COUNT 个线程; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_join</span><span class="p">(</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">);</span> <span class="c1">// 让当前线程等待其他线程执行完毕; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Counter value is: %ld.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">counter</span><span class="p">);</span> <span class="c1">// 输出 counter 变量最终结果; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p><code>counter++</code> 语句可能会编译为如下几条机器指令</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nf">mov</span> <span class="no">eax</span><span class="p">,</span> <span class="no">DWORD</span> <span class="no">PTR</span> <span class="no">counter</span><span class="p">[</span><span class="no">rip</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="nf">add</span> <span class="no">eax</span><span class="p">,</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"><span class="nf">mov</span> <span class="no">DWORD</span> <span class="no">PTR</span> <span class="no">counter</span><span class="p">[</span><span class="no">rip</span><span class="p">],</span> <span class="no">eax</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="竞态条件">竞态条件</h2> <p>竞态条件 Race Condition 是指由于程序中某些事件的发生时机和顺序不一致,从而影响正确性的一种缺陷</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdatomic.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_COUNT 10 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">atomic_int</span> <span class="n">accountA</span> <span class="o">=</span> <span class="mi">100000000</span><span class="p">;</span> <span class="c1">// 转出账户初始金额; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">atomic_int</span> <span class="n">accountB</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 转入账户初始金额; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">_amount</span> <span class="o">=</span> <span class="o">*</span><span class="p">((</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span> <span class="n">v</span><span class="p">);</span> <span class="c1">// 获得当前线程的转移金额; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span><span class="p">(;;)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 首先判断转出账户金额是否足够,不够则直接退出; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="n">accountA</span> <span class="o">&lt;</span> <span class="n">_amount</span><span class="p">)</span> <span class="k">return</span> <span class="n">thrd_error</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">atomic_fetch_add</span><span class="p">(</span><span class="o">&amp;</span><span class="n">accountB</span><span class="p">,</span> <span class="n">_amount</span><span class="p">);</span> <span class="c1">// 将金额累加到转入账户; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">atomic_fetch_sub</span><span class="p">(</span><span class="o">&amp;</span><span class="n">accountA</span><span class="p">,</span> <span class="n">_amount</span><span class="p">);</span> <span class="c1">// 将金额从转出账户中扣除; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#if !defined(__STDC_NO_THREADS__) &amp;&amp; !defined(__STDC_NO_ATOMICS__) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="kt">thrd_t</span> <span class="n">threads</span><span class="p">[</span><span class="n">THREAD_COUNT</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="nf">srand</span><span class="p">(</span><span class="nf">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">amount</span> <span class="o">=</span> <span class="nf">rand</span><span class="p">()</span> <span class="o">%</span> <span class="mi">50</span><span class="p">;</span> <span class="c1">// 为每一个线程生成一个随机转移金额; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">run</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">amount</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_join</span><span class="p">(</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;A: %d</span><span class="se">\n</span><span class="s">B: %d&#34;</span><span class="p">,</span> <span class="n">accountA</span><span class="p">,</span> <span class="n">accountB</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>由于加钱和减钱的操作是分开的,可能导致错误的结果</p> <h2 id="指令重排">指令重排</h2> <p>现代编译器和处理器会采用指令重排来进一步提升程序的运行效率。 这种技术会在不影响程序可观测执行结果的情况下,对生成的机器指令或实际执行顺序进行适当的重排。</p> <p>对于编译器来说,其表象是源码中的语句出现顺序和汇编代码实现的顺序不一致。</p> <p>对于处理器来说,则是程序在真正执行时产生副作用的顺序(如变量赋值),与汇编代码指令出现顺序不一致</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdatomic.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#if !defined(__STDC_NO_ATOMICS__) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">atomic_int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// !变量 y 的值可能被优先更新! </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">observe</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">y</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">)</span> <span class="p">;</span> <span class="c1">// 忙等待; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span> <span class="c1">// 只在 x 被更新后打印; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#if !defined(__STDC_NO_THREADS__) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="kt">thrd_t</span> <span class="n">threadA</span><span class="p">,</span> <span class="n">threadB</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">threadA</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">threadB</span><span class="p">,</span> <span class="n">observe</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_join</span><span class="p">(</span><span class="n">threadA</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_join</span><span class="p">(</span><span class="n">threadB</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十四讲-如何协调线程">第十四讲 如何协调线程</h1> <h2 id="互斥量">互斥量</h2> <p>互斥量 mtx_t_init 初始化有三种模式</p> <ul> <li>mtx_plain: 普通模式</li> <li>mtx_recursive: 可重入模式</li> <li>mtx_timed:有超时限制,超过后互斥失效</li> </ul> <p>另外两个互斥相关函数</p> <ul> <li>mtx_trylock: 加锁或者直接返回</li> <li>call_once:仅调用一次</li> </ul> <h2 id="原子操作">原子操作</h2> <p>stdaotmic.h 文件中 提供了一些原子操作</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdatomic.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_COUNT 10 </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_LOOP 100000000 </span></span></span><span class="line"><span class="cl"><span class="cp">#if !defined(__STDC_NO_ATOMICS__) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">_Atomic</span> <span class="kt">long</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 定义一个原子类型全局变量,用来记录线程的累加值; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_LOOP</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">atomic_fetch_add_explicit</span><span class="p">(</span><span class="o">&amp;</span><span class="n">counter</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">memory_order_relaxed</span><span class="p">);</span> <span class="c1">// 使用原子加法操作; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Thread %d terminates.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">*</span><span class="p">((</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span> <span class="n">data</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">thrd_success</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#if !defined(__STDC_NO_THREADS__) || !defined(__STDC_NO_ATOMICS__) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="kt">int</span> <span class="n">ids</span><span class="p">[</span><span class="n">THREAD_COUNT</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">thrd_t</span> <span class="n">threads</span><span class="p">[</span><span class="n">THREAD_COUNT</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ids</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">run</span><span class="p">,</span> <span class="n">ids</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_join</span><span class="p">(</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Counter value is: %ld.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">counter</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>通过 <code>_Atomic</code> 声明一个院子类型变量,使用 atomic_fetch_add_explicit 来进行原子变量的加法操作,内存顺序有三种:</p> <p><img src="https://ppd0705.github.io/image/learn_c/14_1.webp" alt="memory_order"></p> <p>更多原子操作相关函数如下:</p> <p><img src="https://ppd0705.github.io/image/learn_c/14_2.webp" alt="atomic_functions"></p> <h2 id="条件变量">条件变量</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">mtx_t</span> <span class="n">mutex</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">cnd_t</span> <span class="n">cond</span><span class="p">;</span> <span class="c1">// 定义一个条件变量; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">done</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">mtx_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">done</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">cnd_signal</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cond</span><span class="p">);</span> <span class="c1">// 通知等待中的线程; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">mtx_unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">thrd_success</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#ifndef __STDC_NO_THREADS__ </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="nf">mtx_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">,</span> <span class="n">mtx_plain</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">cnd_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cond</span><span class="p">);</span> <span class="c1">// 初始化条件变量; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">thrd_t</span> <span class="kr">thread</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="kr">thread</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">mtx_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">done</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">cnd_wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cond</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span> <span class="c1">// 让当前线程进入等待队列; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">mtx_unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;The value of done is: %d&#34;</span><span class="p">,</span> <span class="n">done</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">mtx_destroy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">cnd_destroy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cond</span><span class="p">);</span> <span class="c1">// 销毁条件变量; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>条件变量提供了线程间通知能力,某个线程可以在完成了某件事后,通知并唤醒等待线程。</p> <h2 id="本地变量">本地变量</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;threads.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdatomic.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_COUNT 10 </span></span></span><span class="line"><span class="cl"><span class="cp">#define THREAD_LOOP 10000 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">_Thread_local</span> <span class="kt">int</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 定义线程本地变量; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">run</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_LOOP</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// 更新当前线程所属的 counter 变量值; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="n">counter</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="k">const</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">thrd_t</span> <span class="n">threads</span><span class="p">[</span><span class="n">THREAD_COUNT</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">run</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">THREAD_COUNT</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">thrd_join</span><span class="p">(</span><span class="n">threads</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">result</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">sum</span> <span class="o">+=</span> <span class="n">result</span><span class="p">;</span> <span class="c1">// 累加每个线程的计算值; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;The value of count is %d.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">sum</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>线程本地变量的值仅能够在某个线程的生存期内可用,变量的实际存储空间会在线程开始时分配,线程结束时回收。</p> <p><img src="https://ppd0705.github.io/image/learn_c/14_3.webp" alt="thread_local_variable"></p> <h1 id="第十五讲-信号">第十五讲 信号</h1> <h2 id="什么是信号">什么是信号</h2> <p>信号实际上是一种可以用来传递特定消息的机制,操作系统将程序运行过程中发生的各类特殊情况转发给程序,并按照其指定的逻辑进行处理。</p> <p>信号的产生是一个随机的过程,所以程序需要提前”告诉“操作系统,信号到来时,应该如何处理。这就是一种典型的异步事件处理方式。</p> <h2 id="信号与软中断">信号与软中断</h2> <p>信号是一种软中断,当特定事件发生时,操作系统会将对应的信号值发送给相关程序,通常情况下,如果对应程序并未设置自定义的信号处理程序,则操作系统会执行默认信号处理程序。整个程序处理过程中,存在着 CPU 从用户态到信号处理程序的执行流程转移。</p> <h2 id="c-代码样例">C 代码样例</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;signal.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">void</span> <span class="nf">sigHandler</span><span class="p">(</span><span class="kt">int</span> <span class="n">sig</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Signal %d catched!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">sig</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">exit</span><span class="p">(</span><span class="n">sig</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">signal</span><span class="p">(</span><span class="n">SIGFPE</span><span class="p">,</span> <span class="n">sigHandler</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="n">x</span> <span class="o">/</span> <span class="n">y</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>C 标准库提供了 6 种不同类型的信号</p> <p><img src="https://ppd0705.github.io/image/learn_c/15_1.webp" alt="signal_num"></p> <p>信号处理函数的原型为 <code>void (*handler) (int)</code>,即接受一个整形的信号值,不返回任何内容。</p> <p>除零异常的信号交互逻辑如下:</p> <ol> <li>CPU 执行触发指令 idiv</li> <li>发现除零异常,CPU 暂停当前程序运行,并将控制权转交给操作系统</li> <li>操作系统将信号 SIGFPE 发送给出错的程序</li> <li>操作系统根据情况执行相应的信号处理程序</li> <li>信号处理程序执行完毕后,如果程序未退出,则将程序执行恢复到之前的中断点,即 CPU 会重新执行 idiv 指令</li> </ol> <h2 id="可重入函数">可重入函数</h2> <p>当程序在执行函数 A 时收到了某个信号,信号处理函数中也对函数 A 发起了调用,这样可能会影响之前还未完成调用的函数 A 执行状态。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;signal.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define BUF_SIZE 16 </span><span class="c1">// 全局静态数组大小; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define FORMAT_NUM_(N) &#34; $&#34;#N </span></span></span><span class="line"><span class="cl"><span class="cp">#define FORMAT_NUM(N) FORMAT_NUM_(N) </span></span></span><span class="line"><span class="cl"><span class="cp">#define RAISE_EXP_false_ASM() </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// 调用 raise 函数向当前程序发送信号; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define RAISE_EXP_true_ASM() \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;movl $4, %%edi\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;call raise\n\t&#34; </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// 内联汇编实现; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define INLINE_ASM(ID, HAS_EXP) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;mov %0, %%r8\n\t&#34; </span><span class="cm">/* 复制传入的字符串数据到全局静态数组 */</span><span class="cp"> \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;testq %%rsi, %%rsi\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;je .L1&#34; #ID &#34;\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;xorl %%eax, %%eax\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;.L3&#34; #ID &#34;:\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;movzbl (%%rdi,%%rax), %%ecx\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;movb %%cl, (%%r8,%%rax)\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;addq $1, %%rax\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;cmpq %%rsi, %%rax\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;jne .L3&#34; #ID &#34;\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;.L1&#34; #ID &#34;:\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> RAISE_EXP_##HAS_EXP##_ASM() </span><span class="cm">/* 选择性调用 raise 函数 */</span><span class="cp"> \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;mov $1, %%rax\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;mov $1, %%rdi\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;mov %0, %%rsi\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;mov&#34; FORMAT_NUM(BUF_SIZE) &#34;, %%rdx\n\t&#34; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> &#34;syscall\n\t&#34; </span><span class="cm">/* 触发系统调用,打印内容 */</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="n">BUF_SIZE</span><span class="p">];</span> <span class="c1">// 用于保存字符的全局静态数组; </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">print_with_exp</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 会引起信号中断的版本; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">asm</span><span class="p">(</span><span class="nf">INLINE_ASM</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="nb">true</span><span class="p">)</span> <span class="o">::</span> <span class="s">&#34;g&#34;</span> <span class="p">(</span><span class="n">buf</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">print_normal</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 正常的版本; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">asm</span><span class="p">(</span><span class="nf">INLINE_ASM</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="nb">false</span><span class="p">)</span> <span class="o">::</span> <span class="s">&#34;g&#34;</span> <span class="p">(</span><span class="n">buf</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">sigHandler</span><span class="p">(</span><span class="kt">int</span> <span class="n">sig</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str</span> <span class="o">=</span> <span class="s">&#34;Hello&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">print_normal</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">str</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">signal</span><span class="p">(</span><span class="n">SIGILL</span><span class="p">,</span> <span class="n">sigHandler</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str</span> <span class="o">=</span> <span class="s">&#34;, world!&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">print_with_exp</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">str</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/learn_c/15_2.webp" alt="reentrant_function"></p> <p>不受中断和重新调用影响的函数称之为可重入函数</p> <h2 id="多线程信号处理">多线程信号处理</h2> <p>C 语言没有对并发编程的信号处理做规范,所以多线程应用中使用 signal 和 raise 函数会产生未定义的行为</p> <h1 id="第十六讲-日期时间与实用函数">第十六讲 日期、时间与实用函数</h1> <h2 id="日期与时间">日期与时间</h2> <h3 id="日历时间">日历时间</h3> <p>使用 time_t 类型表示,其代表从 1970-01-01: 00:00:00 到当前时间秒数</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">time_t</span> <span class="n">currTime</span> <span class="o">=</span> <span class="nf">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">currTime</span> <span class="o">!=</span> <span class="p">(</span><span class="kt">time_t</span><span class="p">)</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;the current timestamp is: %ld(s)</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">currTime</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;local time is: %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">asctime</span><span class="p">(</span><span class="nf">localtime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">currTime</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;UTC time is: %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">asctime</span><span class="p">(</span><span class="nf">gmtime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">currTime</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="处理器时间">处理器时间</h3> <p>处理器时间即 CPU 资源被调度以支持程序在某段时间内正常运作所花费的时间。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">clock_t</span> <span class="n">startTime</span> <span class="o">=</span> <span class="nf">clock</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10000000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="kt">clock_t</span> <span class="n">endTime</span> <span class="o">=</span> <span class="nf">clock</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Consumed CPU time is:%fs</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">double</span><span class="p">)(</span><span class="n">endTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="p">)</span> <span class="o">/</span> <span class="n">CLOCKS_PER_SEC</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="字符串到数值的转换">字符串到数值的转换</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 一次性字符串到数值转换; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">strA</span> <span class="o">=</span> <span class="s">&#34;1.0&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%f</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">atof</span><span class="p">(</span><span class="n">strA</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 带溢出检查的转换函数,执行后会保存不能被转换部分的地址; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">strB</span> <span class="o">=</span> <span class="s">&#34;200000000000000000000000000000.0&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span><span class="o">*</span> <span class="n">end</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">num</span> <span class="o">=</span> <span class="nf">strtol</span><span class="p">(</span><span class="n">strB</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">end</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">==</span> <span class="n">ERANGE</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 判断转换结果是否发生溢出; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Range error, got: &#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">errno</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%f</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">num</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="生成随机数">生成随机数</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt; </span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt; </span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;time.h&gt; </span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">srand</span><span class="p">(</span><span class="nf">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">));</span> <span class="c1">// 初始化随机数种子; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">while</span> <span class="p">(</span><span class="nf">getchar</span><span class="p">()</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="nf">rand</span><span class="p">()</span> <span class="o">%</span> <span class="mi">10</span><span class="p">);</span> <span class="c1">// 生成并打印 0-9 的随机数; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h3 id="动态内存管理">动态内存管理</h3> <p>除了 malloc 和 free 函数之外,C 标准库也提供了另外的一些函数</p> <p><img src="https://ppd0705.github.io/image/learn_c/16_1.webp" alt="alloc_function"></p> <h3 id="进程控制">进程控制</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">void</span> <span class="nf">exitHandler</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">getenv</span><span class="p">(</span><span class="s">&#34;PATH&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">atexit</span><span class="p">(</span><span class="n">exitHandler</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">exit</span><span class="p">(</span><span class="n">EXIT_SUCCESS</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/learn_c/16_2.webp" alt="process_control"></p> <h1 id="第十七讲-断言错误处理和对齐">第十七讲 断言、错误处理和对齐</h1> <h2 id="断言-1">断言</h2> <p>断言分为静态断言和动态断言</p> <p>一般我们在程序运行前使用静态断言,来检查它所需要满足的一系列要求</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;assert.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">double</span> <span class="nf">sqrt</span><span class="p">(</span><span class="kt">double</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 检查函数使用时传入的参数; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">assert</span><span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="mf">0.0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 检查程序的编译要求; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">static_assert</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">4</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;Integer should have at least 4 bytes length.&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>定义 <code>NDEBUG</code> 宏可关闭断言功能</p> <h2 id="错误处理">错误处理</h2> <p>在 C 语言中,名为 errno 的预处理宏会被展开为一个 init 类型的可修改全局左值</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;tgmath.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">sqrt</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span> <span class="c1">// &#34;Numerical argument out of domain&#34;. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>可以通过 strerror 函数获取当前 errno 对应的可读文本</p> <h2 id="对齐">对齐</h2> <p>可以使用 _Alignas 来根据自身需要为数据指定特殊的对齐要求,stdalign.h 有 对应的宏 alignas</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdalign.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#if __alignas_is_defined == 1 &amp;&amp; __alignof_is_defined == 1 </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="nf">alignas</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;The alignment of n is %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">alignof</span><span class="p">(</span><span class="n">n</span><span class="p">));</span> <span class="c1">// &#34;The alignment of n is 1024&#34;. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;The address of n is: %p</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n</span><span class="p">);</span> <span class="c1">// &#34;The address of n is: 0x7ffe80658c00&#34;. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#endif </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第十八讲-极致优化上">第十八讲 极致优化(上)</h1> <h2 id="高速缓存">高速缓存</h2> <p>CPU 芯片上有 L1、L2、L3 三个不同级别的高速缓存</p> <p>高速缓存之所以能提升性能,一个重要的前提在于局部性原理</p> <ul> <li>时间局部性:被引用过一次的内存位置接下来可能会被再次引用</li> <li>空间局部性:如果一个内存位置被引用了,那附近的内存也可能会被引用</li> </ul> <h2 id="内联">内联</h2> <p>通过内联关键字 inline ,可以建议编译器,将某个方法的实现内联到它的实际调用处。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">static</span> <span class="kr">inline</span> <span class="kt">int</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">10</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">v</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Output is: %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>上述代码对应的汇编代码如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nl">.LC0:</span> </span></span><span class="line"><span class="cl"> <span class="na">.string</span> <span class="s">&#34;Output is: %d\n&#34;</span> </span></span><span class="line"><span class="cl"><span class="nl">main:</span> </span></span><span class="line"><span class="cl"> <span class="nf">sub</span> <span class="no">rsp</span><span class="p">,</span> <span class="mi">8</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">esi</span><span class="p">,</span> <span class="mi">10</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">edi</span><span class="p">,</span> <span class="no">OFFSET</span> <span class="no">FLAT</span><span class="p">:.</span><span class="no">LC0</span> </span></span><span class="line"><span class="cl"> <span class="nf">xor</span> <span class="no">eax</span><span class="p">,</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="nf">call</span> <span class="no">printf</span> </span></span><span class="line"><span class="cl"> <span class="nf">xor</span> <span class="no">eax</span><span class="p">,</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="nf">add</span> <span class="no">rsp</span><span class="p">,</span> <span class="mi">8</span> </span></span><span class="line"><span class="cl"> <span class="nf">ret</span> </span></span></code></pre></td></tr></table> </div> </div><p>通过内联,程序不再需要使用 call 指令来调用 foo 函数,好处在于节省 call 指令执行时需要进行的函数帧栈创建和销毁过程。坏处是导致可执行二进制文件增大</p> <h2 id="restrict关键字">restrict关键字</h2> <p>restrict 关键字用于指针,表明该指针是访问对应数据的唯一方式</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span><span class="o">*</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">y</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="kr">restrict</span> <span class="n">z</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">x</span> <span class="o">+=</span> <span class="o">*</span><span class="n">z</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">y</span> <span class="o">+=</span> <span class="o">*</span><span class="n">z</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">20</span><span class="p">,</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">30</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">foo</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">y</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">z</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d %d %d&#34;</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>对应的汇编代码如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nl">foo:</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">eax</span><span class="p">,</span> <span class="no">DWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdx</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nf">add</span> <span class="no">DWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rdi</span><span class="p">],</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="nf">add</span> <span class="no">DWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">rsi</span><span class="p">],</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="nf">ret</span> </span></span></code></pre></td></tr></table> </div> </div><p>使用restrict 关键字后,在为指针 y 进行 值累加前,编译器不会再重复性地从内存中读取指针 z 对应的值</p> <h2 id="消除不需要的内存引用">消除不需要的内存引用</h2> <p>原代码</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#define LEN 1024 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="n">data</span><span class="p">[</span><span class="n">LEN</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="p">...</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span><span class="o">*</span> <span class="n">dest</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">dest</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LEN</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">dest</span> <span class="o">=</span> <span class="o">*</span><span class="n">dest</span> <span class="o">*</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>优化代码</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#define LEN 3 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="n">data</span><span class="p">[</span><span class="n">LEN</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span><span class="o">*</span> <span class="n">dest</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">register</span> <span class="kt">int</span> <span class="n">acc</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LEN</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">acc</span> <span class="o">=</span> <span class="n">acc</span> <span class="o">*</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">dest</span> <span class="o">=</span> <span class="n">acc</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>优化代码主要做了两件事情:</p> <ol> <li>将用于存放临时积累值的 <code>*dest</code> 替换为局部变量</li> <li>为局部变量添加 register 关键字,建议编译器将该值存放在寄存器中</li> </ol> <h1 id="第十九讲-极致优化下">第十九讲 极致优化(下)</h1> <h2 id="循环展开loop-unrolling">循环展开(Loop Unrolling)</h2> <p>现在 CPU 为了进一步提升指令的执行效率,通常会将单一的机器指令再进行拆分,以达到并行的目的。对于一个基本的五级 RISC 流水线来说, CPU 会将指令的执行细分为指令提取 (IF)、指令译码(ID)、指令执行(EX)、内存访问(MEM)和寄存器写回(WB)。</p> <p>在第一条机器指令经过了指令提取后,即使该指令没有完全执行完毕,CPU 也可以立即开始处理下一条机器指令。因此从宏观上来看,机器指令的执行由串行变成了并行。 当五个节点全部执行完毕后,CPU 会更新指令指针(PC)</p> <p><img src="https://ppd0705.github.io/image/learn_c/19_1.webp" alt="instructions"></p> <p>示例代码如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp"># 原代码 </span></span></span><span class="line"><span class="cl"><span class="cp">#define LEN 4096 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="n">data</span><span class="p">[</span><span class="n">LEN</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="p">...</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">acc</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LEN</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">acc</span> <span class="o">=</span> <span class="n">acc</span> <span class="o">*</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">acc</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="cp"># 展开后的代码 </span></span></span><span class="line"><span class="cl"><span class="cp">#define LEN 4096 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="n">data</span><span class="p">[</span><span class="n">LEN</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="p">...</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">limit</span> <span class="o">=</span> <span class="n">LEN</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">acc0</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">acc1</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">limit</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 2x2 loop unrolling. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">acc0</span> <span class="o">=</span> <span class="n">acc0</span> <span class="o">*</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">acc1</span> <span class="o">=</span> <span class="n">acc1</span> <span class="o">*</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LEN</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Finish any remaining elements. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">acc0</span> <span class="o">=</span> <span class="n">acc0</span> <span class="o">*</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">acc0</span> <span class="o">*</span> <span class="n">acc1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>我们将 循环结果应用了 2 * 2 (步长为2,2个独立累积值变量)循环展开。</p> <p>不过这样导致代码量增加和可读性下降,大部分情况下我们不需要手动改变代码来做循环展开。</p> <h2 id="优先使用条件传送指令">优先使用条件传送指令</h2> <p>CPU 中存在着条件传送指令、条件分支指令等,CPU 执行条件分支指令会对分支进行预测,并提现执行分支指令,如果预测的跳转位置发生错误,就会将状态重置为发生跳转前分支所处的状态,并取出正确方向上的指令,开始重新处理。 条件传送指令没有分支预测,所以没有这部分损失。</p> <p>示例代码如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#define LEN 1024 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span><span class="o">*</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LEN</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#define LEN 16 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span><span class="kt">int</span><span class="o">*</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LEN</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">min</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">?</span> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">:</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">max</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">?</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">:</span> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">min</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">max</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>上面示例通过增加几次比较和复制操作,来避免分支预测失败代码的惩罚。</p> <h2 id="使用更高的编译优化等级">使用更高的编译优化等级</h2> <p>gcc 可以指定更高的优化等级来优化</p> <p><img src="https://ppd0705.github.io/image/learn_c/19_2.webp" alt="gcc_options"></p> <h2 id="尾递归优化">尾递归优化</h2> <p>尾递归优化通过将函数的递归调用优化为循环结构,减少了 call 指令的调用次数,进而减少了帧栈的创建和销毁过程,提升了程序的执行性能。</p> <h1 id="第二十讲-编码规范">第二十讲 编码规范</h1> <p>我们以 GNU 编码规范为基准</p> <h2 id="格式">格式</h2> <ul> <li>每行字符数量保持在79个以内</li> <li>函数定义是开始花括号“{”位于行首</li> <li>函数命中位于行首</li> <li>参数过多时,将超过限制的参数放到函数名下一行,并与第一个参数开头对齐</li> </ul> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> </span></span><span class="line"><span class="cl"><span class="nf">lots_of_args</span> <span class="p">(</span><span class="kt">int</span> <span class="n">an_integer</span><span class="p">,</span> <span class="kt">long</span> <span class="n">a_long</span><span class="p">,</span> <span class="kt">short</span> <span class="n">a_short</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kt">double</span> <span class="n">a_double</span><span class="p">,</span> <span class="kt">float</span> <span class="n">a_float</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">foo</span> </span></span><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">bar</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">;</span> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>更多规范参考 GNU 旗下的格式化工具 indent</p> <h2 id="注释">注释</h2> <p>文件层面,main 函数所在文件应以描述程序的基本用途的注释作为开头,而其他源文件应以文件名和描述该源文件基本功能的注释作为开头</p> <p>函数层面,需要添加用于描述函数基本功能、参数类型、参数用途、参数可能取值和返回值含义等内容注释信息</p> <h2 id="语法约定">语法约定</h2> <ul> <li>显示地为所有使用到的值标注类型,尤其是直接使用在表达式中的数字字面量值</li> <li>外部函数和后续才会使函数的声明,应该被放置在当前源文件处开头统一的地方,或放到单独的文件中</li> <li>将同类型的多个变量放在同一行声明</li> <li>使用嵌套的流程控制语句(如 if 语句)时,总是将内部嵌套的逻辑包裹在大括号中。</li> <li>尽量避免在 if 语句 条件判断处做赋值操作(while 可以这样做)</li> </ul> <h2 id="命名">命名</h2> <p>变量名一般采用下划线命名法</p> <p>尽量使用小写字母、下划线和数字来组成变量名,把大写字母留给宏常量和枚举常量。</p> <h2 id="国际化">国际化</h2> <p>可以使用 GNU gettext 库,将消息翻译成各种语言</p> <h1 id="第二十一讲-自动化测试">第二十一讲 自动化测试</h1> <h2 id="单元测试">单元测试</h2> <p>单元测试就是对组成程序的基本单元(也可称为模块)进行功能正确性验证,它的目标是 隔离程序的每个部分,并单独验证这些部分能够按照预期正常工作。</p> <p>对于 C 程序来说,这里的单元通常为函数</p> <p>通常使用 Cunit 进行单元测试</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">maxi</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 被测试函数; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="n">y</span><span class="p">)</span> <span class="o">?</span> <span class="nl">x</span> <span class="p">:</span> <span class="n">y</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">test_maxi</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 测试用例; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">CU_ASSERT</span><span class="p">(</span><span class="nf">maxi</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">CU_ASSERT</span><span class="p">(</span><span class="nf">maxi</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">CU_ASSERT</span><span class="p">(</span><span class="nf">maxi</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="c1">// ... </span></span></span></code></pre></td></tr></table> </div> </div><h2 id="集成测试">集成测试</h2> <p>检测不同单元或模块整合在一起时,他们是否也可以很好地协同工作,这类测试称之为集成测试 Integration Testing,这类测试通常需要使用数据库,网络连接等真实的外部资源。</p> <h2 id="功能测试">功能测试</h2> <p>功能测试 Functional Testing 的目的和集成测试十分类似,但功能测试对测试结果的正确性要求可能会更加严格,需要满足业务需求中的相应规定。</p> <h2 id="性能测试">性能测试</h2> <p>通常可以使用运行时间和内存使用率这两个指标来作为程序运行性能的度量单位</p> <p>可以使用 perf 命令行工具来进行性能测试</p> <h1 id="第二十二讲-结构化编译">第二十二讲 结构化编译</h1> <h2 id="如何组织代码结构">如何组织代码结构</h2> <p>对于小型项目,可以简单地将 .h 文件和 .c文件分别放在 include 目录 和 src 目录,当项目变大时,可以将源文件按功能进行更细分的划分。</p> <p>两种参考目录结构如下图</p> <p><img src="https://ppd0705.github.io/image/learn_c/22_1.webp" alt="project_layouts"></p> <h2 id="如何组织编译流程">如何组织编译流程</h2> <p>一个 简单的 C 项目如下图 <img src="https://ppd0705.github.io/image/learn_c/22_2.webp" alt="project_example"></p> <p>可以用如下命令进行编译, 使用<code>-I</code>选项指定查找头文件需要搜索的目录,使用<code>-l</code>指定 链接运行时依赖的库</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gcc src/main.c src/mod.c -I./include -lm -o bin/main </span></span></code></pre></td></tr></table> </div> </div><h2 id="使用-makefile-进行结构化编译">使用 Makefile 进行结构化编译</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># 用于控制编译细节的自定义宏; </span></span><span class="line"><span class="cl">CC = gcc </span></span><span class="line"><span class="cl">CFLAGS = -I./include </span></span><span class="line"><span class="cl">LDFLAGS = -lm </span></span><span class="line"><span class="cl">TARGET_FILE = bin/main </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"># 描述各个目标的详细编译步骤; </span></span><span class="line"><span class="cl">$(TARGET_FILE): $(patsubst src/%.c,src/%.o,$(wildcard src/*.c)) </span></span><span class="line"><span class="cl"> $(CC) $^ $(LDFLAGS) -o $@ </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">src/%.o: src/%.c include/%.h </span></span><span class="line"><span class="cl"> $(CC) $&lt; $(CFLAGS) -c -o $@ </span></span></code></pre></td></tr></table> </div> </div><h2 id="使用-cmake-进行跨平台自动化构建">使用 CMake 进行跨平台自动化构建</h2> <p>CMake(Cross-platform Make)会根据所在的平台,生成相应的“平台本地构建项目”,比如在 Unix 系统上,会生成相应的 Makefie 文件,在 Windows 会生成对应的 Visula Studio 工程文件。</p> <p>CMake 的配置信息存在 CMakeLists.txt 文件中。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-cmake" data-lang="cmake"><span class="line"><span class="cl"><span class="nb">cmake_minimum_required</span><span class="p">(</span><span class="s">VERSION</span> <span class="s">3.10</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置项目名称; </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">project</span><span class="p">(</span><span class="s">Test</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置二进制目标文件名称; </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">set</span><span class="p">(</span><span class="s">TARGET_FILE</span> <span class="s2">&#34;main&#34;</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 添加源文件目录; </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">aux_source_directory</span><span class="p">(</span><span class="s">./src</span> <span class="s">DIR_SRCS</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置二进制目标文件的依赖; </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">add_executable</span><span class="p">(</span><span class="o">${</span><span class="nv">TARGET_FILE</span><span class="o">}</span> <span class="o">${</span><span class="nv">DIR_SRCS</span><span class="o">}</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置头文件查找目录; </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">target_include_directories</span><span class="p">(</span><span class="o">${</span><span class="nv">TARGET_FILE</span><span class="o">}</span> <span class="s">PUBLIC</span> <span class="s2">&#34;${PROJECT_SOURCE_DIR}/include&#34;</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 设置需要链接的库; </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">target_link_libraries</span><span class="p">(</span><span class="o">${</span><span class="nv">TARGET_FILE</span><span class="o">}</span> <span class="s">PUBLIC</span> <span class="s">m</span><span class="p">)</span><span class="err"> </span></span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十三讲-高性能-http-server-实战上">第二十三讲 高性能 HTTP Server 实战(上)</h1> <p>我们将会创建一个叫 FivSev 的服务,接收路径 “/?num={pos}”的 GET 请求,返回斐波那契数列对应位置的数值。</p> <p>大体流程如下图</p> <p><img src="https://ppd0705.github.io/image/learn_c/23_1.webp" alt="tcp_server"></p> <p>部分优化措施</p> <ul> <li>使用线程池利用多核CPU</li> <li>使用尾递归调用优化</li> <li>使用条件变量避免忙等待</li> </ul> <h1 id="第二十四讲-高性能-http-server-实战下">第二十四讲 高性能 HTTP Server 实战(下)</h1> <h2 id="项目基本结构">项目基本结构</h2> <p>项目仓库地址: <a href="https://github.com/Becavalier/tiny-http-echo-server/tree/geektime">https://github.com/Becavalier/tiny-http-echo-server/tree/geektime</a></p> <p><img src="https://ppd0705.github.io/image/learn_c/24_1.webp" alt="project_layout"></p> <h2 id="处理用户参数">处理用户参数</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// libs/structs.h#L9-L11 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">threadCount</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">serverSettings</span><span class="p">;</span> </span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// libs/helpers.h#L67-L86 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">setupServerSettings</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">,</span> <span class="n">serverSettings</span><span class="o">*</span> <span class="n">ss</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">argc</span><span class="o">--</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// process key. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">keyHead</span> <span class="o">=</span> <span class="n">argv</span><span class="p">[</span><span class="n">argc</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">keyPos</span> <span class="o">=</span> <span class="nf">strchr</span><span class="p">(</span><span class="n">keyHead</span><span class="p">,</span> <span class="sc">&#39;=&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">size_t</span> <span class="n">keyLen</span> <span class="o">=</span> <span class="n">keyPos</span> <span class="o">-</span> <span class="n">keyHead</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">key</span><span class="p">[</span><span class="n">keyLen</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="nf">wrapStrFromPTR</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">keyLen</span><span class="p">,</span> <span class="n">keyHead</span><span class="p">,</span> <span class="n">keyPos</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="c1">// process value. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">valHead</span> <span class="o">=</span> <span class="n">keyHead</span> <span class="o">+</span> <span class="n">keyLen</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">valPos</span> <span class="o">=</span> <span class="nf">strchr</span><span class="p">(</span><span class="n">valHead</span><span class="p">,</span> <span class="sc">&#39;\0&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">size_t</span> <span class="n">valLen</span> <span class="o">=</span> <span class="n">valPos</span> <span class="o">-</span> <span class="n">valHead</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">val</span><span class="p">[</span><span class="n">valLen</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">valHead</span> <span class="o">&lt;=</span> <span class="n">valPos</span><span class="p">;</span> <span class="n">valHead</span><span class="o">++</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">val</span><span class="p">[</span><span class="n">i</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="n">valHead</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">strcmp</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="s">&#34;thread_count&#34;</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ss</span><span class="o">-&gt;</span><span class="n">threadCount</span> <span class="o">=</span> <span class="nf">atoi</span><span class="p">(</span><span class="n">val</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="tcp-server">TCP Server</h2> <h3 id="监听请求">监听请求</h3> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// src/main.c#L70-95 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="n">serverFd</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">sockaddr_in</span> <span class="n">address</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">addrLen</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">address</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// establish a socket. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">((</span><span class="n">serverFd</span> <span class="o">=</span> <span class="nf">socket</span><span class="p">(</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">SOCK_STREAM</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">bzero</span><span class="p">(</span><span class="o">&amp;</span><span class="n">address</span><span class="p">,</span> <span class="n">addrLen</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="n">address</span><span class="p">.</span><span class="n">sin_family</span> <span class="o">=</span> <span class="n">AF_INET</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="n">address</span><span class="p">.</span><span class="n">sin_addr</span><span class="p">.</span><span class="n">s_addr</span> <span class="o">=</span> <span class="n">INADDR_ANY</span><span class="p">;</span> <span class="c1">// -&gt; 0.0.0.0. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">address</span><span class="p">.</span><span class="n">sin_port</span> <span class="o">=</span> <span class="nf">htons</span><span class="p">(</span><span class="n">PORT</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// assigns specified address to the socket. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">bind</span><span class="p">(</span><span class="n">serverFd</span><span class="p">,</span> <span class="p">(</span><span class="n">sockaddr</span><span class="o">*</span><span class="p">)</span> <span class="o">&amp;</span><span class="n">address</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">address</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// mark the socket as a passive socket. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nf">listen</span><span class="p">(</span><span class="n">serverFd</span><span class="p">,</span> <span class="n">MAX_LISTEN_CONN</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="解析请求">解析请求</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// libs/helpers.c#L37-L65 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">retrieveGETQueryIntValByKey</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">req</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// extract uri; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">uriHead</span> <span class="o">=</span> <span class="nf">strchr</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">uriTail</span> <span class="o">=</span> <span class="nf">strchr</span><span class="p">(</span><span class="n">uriHead</span><span class="p">,</span> <span class="sc">&#39; &#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">size_t</span> <span class="n">uriLen</span> <span class="o">=</span> <span class="n">uriTail</span> <span class="o">-</span> <span class="n">uriHead</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">strUri</span><span class="p">[</span><span class="n">uriLen</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="nf">wrapStrFromPTR</span><span class="p">(</span><span class="n">strUri</span><span class="p">,</span> <span class="n">uriLen</span><span class="p">,</span> <span class="n">uriHead</span><span class="p">,</span> <span class="n">uriTail</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// parse uri; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">UriUriA</span> <span class="n">uri</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">UriQueryListA</span><span class="o">*</span> <span class="n">queryList</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">itemCount</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">errorPos</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">uriParseSingleUriA</span><span class="p">(</span><span class="o">&amp;</span><span class="n">uri</span><span class="p">,</span> <span class="n">strUri</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">errorPos</span><span class="p">)</span> <span class="o">==</span> <span class="n">URI_SUCCESS</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">uriDissectQueryMallocA</span><span class="p">(</span><span class="o">&amp;</span><span class="n">queryList</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">itemCount</span><span class="p">,</span> <span class="n">uri</span><span class="p">.</span><span class="n">query</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">uri</span><span class="p">.</span><span class="n">query</span><span class="p">.</span><span class="n">afterLast</span><span class="p">)</span> <span class="o">==</span> <span class="n">URI_SUCCESS</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">itemCount</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">strcmp</span><span class="p">(</span><span class="n">queryList</span><span class="o">-&gt;</span><span class="n">key</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">result</span> <span class="o">=</span> <span class="nf">atoi</span><span class="p">(</span><span class="n">queryList</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">queryList</span> <span class="o">=</span> <span class="n">queryList</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">uriFreeQueryListA</span><span class="p">(</span><span class="n">queryList</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">result</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="计算斐波那契数列">计算斐波那契数列</h2> <p>有递归和尾递归优化版本</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// libs/helpers.c#L8-L20 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">__calcFibTCO</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">y</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">__calcFibTCO</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">__calcFibRecursion</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">__calcFibRecursion</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="nf">__calcFibRecursion</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">calcFibonacci</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// return __calcFibTCO(n, 0, 1); // TCO version. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nf">__calcFibRecursion</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="c1">// recursion version. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十五讲-可执行二进制文件">第二十五讲 可执行二进制文件</h1> <h2 id="elf-文件格式">ELF 文件格式</h2> <p>Unix 最常使用的是ELF(executabke and linkable format)文件格式</p> <p>通常可执行文件的组织方式分为3部分:header、section、segment</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// elf.c </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="nf">main</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str</span> <span class="o">=</span> <span class="s">&#34;Hello, world!&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%s&#34;</span><span class="p">,</span> <span class="n">str</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>通过 file 命令查看文件格式信息</p> <p><img src="https://ppd0705.github.io/image/learn_c/25_1.webp" alt="file_info"></p> <h2 id="elf-头">ELF 头</h2> <p>通过<code>readelf -h</code>可以查看文件头部内容</p> <p><img src="https://ppd0705.github.io/image/learn_c/25_2.webp" alt="elf_head"></p> <h2 id="section-头">Section 头</h2> <p>Section 用于存放可执行文件中按照功能分类好的数据,各个 Section 相关信息存放在对应的 Section 头部中,众多连续的 Section 头便组成了 Section 头表</p> <p>通过 ELF 头信息可以得到总共有 30 个 Section 头,第一个头位于文件开始偏移的 15512 字节</p> <p>通过<code>readelf -S</code> 查看 Section 头部信息</p> <p><img src="https://ppd0705.github.io/image/learn_c/25_3.webp" alt="section_head"></p> <p>.text 主要存放程序对应的机器代码,.rodata 存放只读常量,.data 存放已经初始化的全局变量或者静态变量,.bss 中coffee初始值为 0 的全局或者静态变量。</p> <p>使用 <code>objdump -s</code> 查看 某个 Section 的完整内容</p> <p><img src="https://ppd0705.github.io/image/learn_c/25_4.webp" alt="section_rodata"></p> <h2 id="program-头">Program 头</h2> <p>除了由 Section 组成的静态视图外,众多的 Segment 组成了可执行文件的动态图。</p> <p>Segment 指定了应用程序在实际运行时,应该如何在进程的 VAS 内部组织数据。</p> <p>使用<code>readelf -l</code> 查看 Segment 情况</p> <p><img src="https://ppd0705.github.io/image/learn_c/25_5.webp" alt="section_segment"></p> <p>Segment 对应的头部称之为 Program 头</p> <p>其中 “LOAD” 类型的 Segment 会在运行时载入到进程的 VAS 中, 而其余的 Segment 主要用于辅助程序的正常运行(如动态链接)。</p> <p>通常 Segment 和 Section 之间有一定的对应关系,上图中 第一个 LOAD 类型的 Segment 便包含 .text 和 .rodata 等 Section, 第二个 LOAD Segment 包含 .data Section。这样印证了 两个 LOAD 对应的 FLags 分别 为 RE 和 RW。</p> <p>另外第一个 LOAD 的 Offset 为 0 ,这意味着执行时会除了将对应的 Sections 加载到内存外,文件 ELF 头也会加载到内存中。</p> <p><img src="https://ppd0705.github.io/image/learn_c/25_6.webp" alt="head_view"></p> <h2 id="elf-编程">ELF 编程</h2> <p>可以使用 elf.h 库对 ELF 格式的应用编程</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;elf.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">print_elf_type</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">type_enum</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">type_enum</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">ET_REL</span><span class="p">:</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;A relocatable file.&#34;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">ET_DYN</span><span class="p">:</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;A shared object file.&#34;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">ET_NONE</span><span class="p">:</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;An unknown type.&#34;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">ET_EXEC</span><span class="p">:</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;An executable file.&#34;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">ET_CORE</span><span class="p">:</span> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;A core file.&#34;</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">Elf64_Ehdr</span> <span class="n">elf_header</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">FILE</span><span class="o">*</span> <span class="n">fp</span> <span class="o">=</span> <span class="nf">fopen</span><span class="p">(</span><span class="s">&#34;./elf&#34;</span><span class="p">,</span> <span class="s">&#34;r&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">fread</span><span class="p">(</span><span class="o">&amp;</span><span class="n">elf_header</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">Elf64_Ehdr</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">print_elf_type</span><span class="p">(</span><span class="n">elf_header</span><span class="p">.</span><span class="n">e_type</span><span class="p">);</span> <span class="c1">// &#34;An executable file.&#34; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">fclose</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="elf-文件类型">ELF 文件类型</h2> <p><img src="https://ppd0705.github.io/image/learn_c/25_7.webp" alt="elf_file_type"></p> <h1 id="第二十六讲-进程如何使用操作系统内存">第二十六讲 进程如何使用操作系统内存</h1> <h2 id="计算机内部缓存系统">计算机内部缓存系统</h2> <p>缓存由 L1、L2 等高速缓存和内存组成</p> <h2 id="虚拟内存机制">虚拟内存机制</h2> <p>虚拟内存 Virtual Memory 是操作系统在物理内存的基础上进行了一层抽象,以帮助运行于其上的应用程序合理分配内存</p> <p><img src="https://ppd0705.github.io/image/learn_c/26_1.webp" alt="retrieve_data"></p> <p>CPU 会借助内存管理单元 MMU 将虚拟地址翻译成物理地址,然后再进行实际数据的获取。</p> <p>每一个应用进程都有独立的虚拟地址空间 VAS,从进程的角度看,它可以独享整个计算机上的内存。在64 位 Linux 系统中,与应用代码相关的 Segment 会从 VAS 的固定地址 0x400000 处开始加载,而Section 内容将在满足一定对齐要求的条件下按顺序加载到高地址方向的虚拟内存中。</p> <p>0x0 到 0x400000 (4K) 的虚拟地址不会使用,这样空指针可以触发异常</p> <p>可以通过<code>cat /proc/&lt;pid&gt;/maps</code> 查看某个运行中的进程的 VAS 布局情况。</p> <p><img src="https://ppd0705.github.io/image/learn_c/26_2.webp" alt="vas_map"></p> <h2 id="vas-数据布局">VAS 数据布局</h2> <p><img src="https://ppd0705.github.io/image/learn_c/26_3.webp" alt="vas_detail"></p> <p>VAS 数据按地址有低到高,可以分为如下几个部分:</p> <ul> <li>LOAD Segments: 包括与代码相关的 Text Segment(.text, .rodata) 位于最低地址处,紧接着为包含已初始化和未初始化数据的Data Segment(.data, .bss)</li> <li>堆: 堆内存,向高地址方向增长</li> <li>共享库数据:包含各类 .so 共享库相关的数据,程序会在运行时通过动态连接器来完成对它们的加载和处理</li> <li>栈:栈内存,向低地址方向增长</li> <li>用于系统调用加速的内核数据:该部分数据主要提供了用户进程可以直接和内核交互的接口,其中 [vvar] 包含只读的内核数据,另外 [vdso] 和 [vsyscall] 则包含用于辅助操作系统加速用户进程执行某些系统调用过程的信息。</li> <li>其他内核数据</li> </ul> <h2 id="页表">页表</h2> <p>每个进程有一个独立的页表结构来维护 VAS 中的虚拟页在对应物理内存中的映射状态。</p> <p>页表本身维护在物理内存中,其内部由多个 PTE (Page Table Entry) 组成,每个虚拟页对应一个 PTE 。</p> <p>CPU、MMU、页表、物理内存和磁盘五者之间的协作关系如下图:</p> <p><img src="https://ppd0705.github.io/image/learn_c/26_4.webp" alt="PTE"></p> <h2 id="多级页表">多级页表</h2> <p><img src="https://ppd0705.github.io/image/learn_c/26_5.webp" alt="multi_level_page_table"></p> <p>多级页表节省空间的最重要的两个因素是:</p> <ol> <li>当一级页表某个PTE 没有实际映射时,其对应的二级页表便不会被创建</li> <li>只有一级页表才需要常驻内存,二级页表仅在需要时创建或者从磁盘调入</li> </ol> <h2 id="tlb">TLB</h2> <p>TLB (Translation Lookaside Buffer) 是 MMU 的一部分,用来加快虚拟地址查 PTE 的过程。可以简单将 TLB 理解为一个具有 N 行 M 列的矩阵,MMU 会从 虚拟地址中提取用于查询表项的索引和标记,这两个值可以定位到具体单元格,如果单元格有值,则可以与虚拟地址中的其他信息一起组成最终的物理地址;否则仍需要通过主机查询页表的方式获取物理地址。</p> <h1 id="第二十七讲-静态链接">第二十七讲 静态链接</h1> <p>示例代码如下</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// main.c </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define LEN 2 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">extern</span> <span class="kt">int</span> <span class="n">sharedArr</span><span class="p">[</span><span class="n">LEN</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="nf">sum</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">arr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span><span class="o">*</span> <span class="n">array</span> <span class="o">=</span> <span class="n">sharedArr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="nf">sum</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="n">LEN</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">val</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// sum.c </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define LEN 2 </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">int</span> <span class="n">sharedArr</span><span class="p">[</span><span class="n">LEN</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">sum</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">arr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span> <span class="o">+=</span> <span class="n">arr</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>使用 gcc 将代码分别编译成二进制可执行文件 main 和目标文件 main.o、sum.o</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gcc main.c sum.c -o main <span class="c1"># 生成可执行文件 main;</span> </span></span><span class="line"><span class="cl">gcc -c main.c -o main.o <span class="c1"># 生成目标文件 main.o;</span> </span></span><span class="line"><span class="cl">gcc -c sum.c -o sum.o <span class="c1"># 生成目标文件 sum.o;</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="目标文件">目标文件</h2> <p>目标文件是一种被称为”可重定位文件“的 ELF 文件类型。但它不包含 与动视图相关的的多种 Segment 和 Program 头部, 因此不同类型的 Section 便成了用于描述它所有特征的基本结构。</p> <p><img src="https://ppd0705.github.io/image/learn_c/27_1.webp" alt="object_file"></p> <p>可以使用<code>nm</code> 命令查看目标文件中的符号表,符号表包含了所有全局变量和函数信息 <img src="https://ppd0705.github.io/image/learn_c/27_2.webp" alt="nm_result"></p> <p>nm 会返回三列数据</p> <ul> <li>第一列代表符号在对应 Section 中的偏移量</li> <li>第二列代表符号的类型,D 代表已初始化的全局数据,位于 .data Section; T 代表函数,位于 .text Section; U 代表符号未定义</li> <li>第三列代表名称</li> </ul> <h2 id="符号解析">符号解析</h2> <p>编译器编译源码时,会为无法在当前编译单元内找到定义的符号生成一个特定符号表条目。 链接器在随后进行符号解析时会在全局符号表中进行搜索。</p> <p>如果链接器找到了多个同名定义, 会按照一定规则进行解析。符号有强弱之分,通常函数和已初始化的全局变量为强符号,而未初始化的全局变量为弱符号。</p> <ul> <li>如果有一个强符号和多个弱符号,选强符号</li> <li>如果有多个弱符号,任意选一个(通常选择占用空间最大的)</li> <li>如果有多个强符号,则报错</li> </ul> <p>通过为函数显示添加 <strong>attribute((weak))</strong> 标记可以设置其为弱符号。</p> <h2 id="重定位">重定位</h2> <p>符号解析之后,链接器开始将多个目标文件相同类型的Section 进行合并,同时为这些 Section 及 符号指定运行时 VAS 地址。</p> <p>链接器通过重定位步骤来修改 .data 和 .text 两个 Section 中对每个符号的引用信息,使得它们可以指向正确的运行时地址。</p> <p>重定位依赖特殊的 Section: .rela.data 和 rela.text, 通常这两个 Section 也称之为重定位表。</p> <p><img src="https://ppd0705.github.io/image/learn_c/27_3.webp" alt="readelf"></p> <p>上图中每列数据含义:</p> <ul> <li>Offest: 指目标在 Section 中的偏移</li> <li>Info:</li> <li>Type: 指重定位类型</li> <li>Sym. Value: 当前值</li> <li>Sym. Name + Addend: 符号名称和修正量</li> </ul> <p><img src="https://ppd0705.github.io/image/learn_c/27_4.webp" alt="relocation_type"></p> <p>重定位类型如上图,计算方式中 S 表示实际地址,A 表示 Addend,P 表示被修改的具体位置,L 表示在 PLT 中该符号的入口地址。</p> <p>通过 objdump 可以找到重定向目标的位置</p> <p>以 R_x86_64_pc32 类型 arrary 举例计算, 验证 “S+A-P” 的计算方式</p> <p>S 实际地址 可以通过<code>nm</code> 查看, 结果为 0x601020</p> <p><img src="https://ppd0705.github.io/image/learn_c/27_5.webp" alt="nm"></p> <p>A 修正量为 -4</p> <p>P 修改位置可以通过<code>objdump -M inter -d main</code>查看,40053e未改行机器指令起始位置, 在此基础上加 3个字节(操作指令+寄存器占用?)即可得到重定向的修改位置: 4000541</p> <p><img src="https://ppd0705.github.io/image/learn_c/27_6.webp" alt="objdump"></p> <p><code>0x601020 - 0x4 - 0x400541</code> 结果为 0x200adb,对应大端地址即为 <code>db 01 20 00</code></p> <h1 id="第二十八讲-动态链接">第二十八讲 动态链接</h1> <h2 id="共享库">共享库</h2> <p>能够被动态链接加载的库称之为共享库 Shared Library,在 Linux 中这类库文件以 ”.so“ 后缀结尾</p> <h2 id="使用示例">使用示例</h2> <p>已上一讲中 sum.c 和 main.c 两个文件为例,将 sum.c 编译成 动态库,并让 main.c 对应的应用程序使用。</p> <ol> <li>使用命令<code>gcc sum.c -shared -fPIC -o libsum.o</code> 编译动态库文件</li> <li>使用命令<code>gcc -o main main.c -lsum -L.</code>编译应用程序</li> <li>使用命令<code>export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH</code> 来设置动态链接器查找动态库文件的目录</li> <li>执行<code>./main</code></li> <li></li> </ol> <h2 id="位置无关代码">位置无关代码</h2> <p>位置无关代码(Position Independent Code, PIC)是一类特殊的机器代码,这些代码在使用时,可以被放置在每个进程 VAS 中任意位置,而无需链接器对它内部引用的地址进行重定位。</p> <p>通常可以将模块之间的数据引用分为四种方式:</p> <ul> <li>模块内部函数调用</li> <li>模块内部数据访问</li> <li>模块之间函数调用</li> <li>模块之间数据访问</li> </ul> <p>模块内部函数调用一般以 PC 寄存器寻址方式进行,不依赖 VAS 内的绝对地址。</p> <p>模块内部数据访问时由于.data 和 .text 是相对固定的,数据访问也可以使用相对稳定的地址进行。</p> <h2 id="全局偏移表">全局偏移表</h2> <p>全局偏移表(Global Offset Table, GOT)是位于每个模块 Data Segment起始位置处的一个特殊结构,其内部的每个表项都存放一个地址信息,这些地址对应这被当前 模块引用的外部函数或者变量在 VAS 中的实际地址。</p> <p>当程序被加载进内存时,动态连接器根据实际情况,通过修正 GOT 中的值,来修正代码对应符号的实际引用地址</p> <p><img src="https://ppd0705.github.io/image/learn_c/28_1.webp" alt="GOT"></p> <h2 id="过程链接表">过程链接表</h2> <p>过程链接表 (Procedure Linkage Table, PLT) 是位于 Text Segment 中的一个表结构。其中 第一表项 PT[0] 内部存放的代码专门用于调用动态连链接器,而其他表项则依次存放着用于完成用户函数调用过程相关的代码,这些表项的地址将被 call 指令直接使用。</p> <h2 id="加载时链接">加载时链接</h2> <p>动态连接器进行的符号重定位发生在程序代码被真正执行之前</p> <h2 id="运行时链接">运行时链接</h2> <p>程序可以自动选择想要加载的共享库模块,并在不使用时卸载。动态链接主要使用 dlopen、dlsym、、dlerror、dlclose 四个接口实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;dlfcn.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="k">typedef</span> <span class="nf">double</span> <span class="p">(</span><span class="o">*</span><span class="kt">cos_t</span><span class="p">)(</span><span class="kt">double</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">cos_t</span> <span class="n">cosine</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="o">*</span><span class="n">error</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span><span class="o">*</span> <span class="n">handle</span> <span class="o">=</span> <span class="nf">dlopen</span><span class="p">(</span><span class="s">&#34;libm.so.6&#34;</span><span class="p">,</span> <span class="n">RTLD_LAZY</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">handle</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="nf">dlerror</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="nf">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">dlerror</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">cosine</span> <span class="o">=</span> <span class="p">(</span><span class="kt">cos_t</span><span class="p">)</span> <span class="nf">dlsym</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="s">&#34;cos&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">error</span> <span class="o">=</span> <span class="nf">dlerror</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">error</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%f</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">(</span><span class="o">*</span><span class="n">cosine</span><span class="p">)(</span><span class="mf">2.0</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="nf">dlclose</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><h1 id="第二十九讲-c-程序入口">第二十九讲 C 程序入口</h1> <h2 id="真正的入口函数">真正的入口函数</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// main.c </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p><img src="https://ppd0705.github.io/image/learn_c/29_1.webp" alt="objdump"></p> <p>可以从图中看到,程序首先从 _start 开始执行</p> <h3 id="_start">_start</h3> <p>_start 是链接器在生成目标可执行文件时,默认使用的一个符号名称。链接器在链接时会在全局符号表中找到该符号,并将虚拟地址拷贝到 ELF 头的 e_entry 字段中。</p> <p>可以通过命令<code>ld --verbose</code>查看</p> <p><img src="https://ppd0705.github.io/image/learn_c/29_2.webp" alt="ld_verbose"></p> <p>_start 符号具体定义来自 crt1.o 文件</p> <p>编译时带”-v“ 参数可是看到相关信息</p> <p><img src="https://ppd0705.github.io/image/learn_c/29_3.webp" alt="gcc_verbose"></p> <h3 id="_start-作用">_start 作用</h3> <p>crt1.o 是 C 运行时库 (C Runtime Library CRT) 提供的一个用于辅助应用程序正常运行的特殊文件,汇编代码见<a href="https://github.com/bminor/glibc/blob/b92a49359f33a461db080a33940d73f47c756126/sysdeps/x86_64/start.S">文件</a></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="c1">#include &lt;sysdep.h&gt; </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"><span class="nf">ENTRY</span> <span class="p">(</span><span class="no">_start</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">cfi_undefined</span> <span class="p">(</span><span class="no">rip</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">xorl</span> <span class="nv">%ebp</span><span class="p">,</span> <span class="nv">%ebp</span> <span class="cm">/* 复位 ebp */</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="nv">%RDX_LP</span><span class="p">,</span> <span class="nv">%R9_LP</span> <span class="cm">/* 保存 FINI 函数的地址到 r9 */</span> </span></span><span class="line"><span class="cl"><span class="c1">#ifdef __ILP32__ </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="cm">/* 模拟 ILP32 模型下的栈操作,将位于栈顶的 argc 放入 rsi */</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="p">(</span><span class="nv">%rsp</span><span class="p">),</span> <span class="nv">%esi</span> </span></span><span class="line"><span class="cl"> <span class="no">add</span> <span class="no">$4</span><span class="p">,</span> <span class="nv">%esp</span> <span class="cm">/* 同时让栈顶向高地址移动 4 字节 */</span> </span></span><span class="line"><span class="cl"><span class="c1">#else </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">popq</span> <span class="nv">%rsi</span> <span class="cm">/* 将位于栈顶的 argc 放入 rsi */</span> </span></span><span class="line"><span class="cl"><span class="c1">#endif </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">mov</span> <span class="nv">%RSP_LP</span><span class="p">,</span> <span class="nv">%RDX_LP</span> <span class="cm">/* 将 argv 放入 rdx */</span> </span></span><span class="line"><span class="cl"> <span class="nf">and</span> <span class="no">$</span><span class="err">~</span><span class="mi">15</span><span class="p">,</span> <span class="nv">%RSP_LP</span> <span class="cm">/* 对齐栈到 16 字节 */</span> </span></span><span class="line"><span class="cl"> <span class="nf">pushq</span> <span class="nv">%rax</span> <span class="cm">/* 将 rax 的值存入栈中,以用于在函数调用前保持对齐状态 */</span> </span></span><span class="line"><span class="cl"> <span class="nf">pushq</span> <span class="nv">%rsp</span> <span class="cm">/* 将当前栈顶地址存入栈中 */</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nf">xorl</span> <span class="nv">%r8d</span><span class="p">,</span> <span class="nv">%r8d</span> <span class="cm">/* 复位 r8 */</span> </span></span><span class="line"><span class="cl"> <span class="nf">xorl</span> <span class="nv">%ecx</span><span class="p">,</span> <span class="nv">%ecx</span> <span class="cm">/* 复位 ecx */</span> </span></span><span class="line"><span class="cl"><span class="c1">#ifdef PIC </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="cm">/* 将 GOT 表项中的 main 函数地址存放到 rdi */</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">main@GOTPCREL</span><span class="p">(</span><span class="nv">%rip</span><span class="p">),</span> <span class="nv">%RDI_LP</span> </span></span><span class="line"><span class="cl"><span class="c1">#else </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">mov</span> <span class="no">$main</span><span class="p">,</span> <span class="nv">%RDI_LP</span> <span class="cm">/* 将 main 函数的绝对地址存放到 rdi */</span> </span></span><span class="line"><span class="cl"><span class="c1">#endif </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="cm">/* 调用 __libc_start_main 函数 */</span> </span></span><span class="line"><span class="cl"> <span class="nf">call</span> <span class="p">*</span><span class="no">__libc_start_main@GOTPCREL</span><span class="p">(</span><span class="nv">%rip</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">hlt</span> </span></span><span class="line"><span class="cl"><span class="no">END</span> <span class="p">(</span><span class="no">_start</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="na">.data</span> </span></span><span class="line"><span class="cl"> <span class="na">.globl</span> <span class="no">__data_start</span> </span></span><span class="line"><span class="cl"><span class="nl">__data_start:</span> </span></span><span class="line"><span class="cl"> <span class="na">.long</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="na">.weak</span> <span class="no">data_start</span> </span></span><span class="line"><span class="cl"> <span class="nf">data_start</span> <span class="err">=</span> <span class="no">__data_start</span> </span></span></code></pre></td></tr></table> </div> </div><p>_libc_start_main 函数原型</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">__libc_start_main</span><span class="p">(</span><span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">main</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">init</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fini</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">rtld_fini</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="o">*</span><span class="n">stack_end</span><span class="p">);</span> </span></span></code></pre></td></tr></table> </div> </div><p>_libc_start_main 会为用户代码的执行做一些前期准备工作</p> <ul> <li>执行对用户ID的必要安全性检查</li> <li>初始化线程子系统</li> <li>注册 rtld_fini 函数,以便在动态共享对象退出是释放资源</li> <li>注册 fini 处理程序,以便在程序退出时执行</li> <li>调用初始化函数 init</li> <li>调用 main 函数</li> <li>用 main 的返回值调用 exit 函数</li> </ul> <h2 id="crt">CRT</h2> <p>CRT(C Runtime Library CRT) 为应用程序提供了对启动与退出、C 标准库函数、IO、堆、C 语言特殊实现、调试等多方面功能的实现和支持。</p> <ul> <li>crt1.o 提供了 _start 符号的具体实现,仅参与可执行文件的编译过程</li> <li>crti.o 和 crtn.o 两者通过协作,为共享对象提供了可以使用”构造函数“和”析构函数“的能力</li> <li>crtbegin.o 和 crtend.o 分别提供了 ”构造函数“和”析构函数“的实现</li> </ul> <h1 id="第三十讲-abi-和-api">第三十讲 ABI 和 API</h1> <h2 id="api">API</h2> <p>API(Application Programming Interface)侧重点在于编程,通过遵循 API 规范,可以在相应的编程语言代码中使用这些接口。 对于 C 语言来说,标准库头文件中的函数原型便是一种 API 的具体表现, glibc 和 musl 是其的具体实现。</p> <p>API 的重要特征是提供相应功能的同时隐藏实现细节,让使用者可以按照较为统一和稳定的方式来使用系统的能力。</p> <h2 id="abi">ABI</h2> <p>ABI(Application Bianry Interface)侧重点在于机器指令层面的具体格式, ABI 将程序、操作系统、硬件平台之间协作需要遵守的特定规则暴露出来。这些规则指定这个体系运行的二进制应用程序应该如何在机器代码层面进行数据访问或函数调用。</p> <p>API 规范通常涵盖如下内容:</p> <ul> <li>函数调用规范</li> <li>处理器可以访问的数据类型的大小和对齐方式</li> <li>进程初始化细节(如栈和寄存器的状态变化)</li> <li>对象文件(如 .o)的基本结构</li> <li>程序载入和动态链接的细节</li> </ul> <h1 id="第三十一讲-程序和操作系统交互">第三十一讲 程序和操作系统交互</h1> <h2 id="什么是系统调用">什么是系统调用</h2> <p>系统调用是由操作系统内核封装过的一些可供上层应用程序使用的接口</p> <p>以 getpid 函数具体</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;syscall.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="kt">pid_t</span> <span class="nf">getpid</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">__syscall</span><span class="p">(</span><span class="n">SYS_getpid</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>__syscall 对应的汇编实现</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="na">.global</span> <span class="no">__syscall</span> </span></span><span class="line"><span class="cl"><span class="na">.type</span> <span class="no">__syscall</span><span class="p">,</span><span class="na">@function</span> </span></span><span class="line"><span class="cl"><span class="nl">__syscall:</span> </span></span><span class="line"><span class="cl"> <span class="nf">movq</span> <span class="nv">%rdi</span><span class="p">,</span> <span class="nv">%rax</span> <span class="c1">// 将函数对应的ID 存放到 rax </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">movq</span> <span class="nv">%rsi</span><span class="p">,</span> <span class="nv">%rdi</span> <span class="c1">// 将函数参数存放对寄存器 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">movq</span> <span class="nv">%rdx</span><span class="p">,</span> <span class="nv">%rsi</span> </span></span><span class="line"><span class="cl"> <span class="nf">movq</span> <span class="nv">%rcx</span><span class="p">,</span> <span class="nv">%rdx</span> </span></span><span class="line"><span class="cl"> <span class="nf">movq</span> <span class="nv">%r8</span><span class="p">,</span> <span class="nv">%r10</span> </span></span><span class="line"><span class="cl"> <span class="nf">movq</span> <span class="nv">%r9</span><span class="p">,</span> <span class="nv">%r8</span> </span></span><span class="line"><span class="cl"> <span class="nf">movq</span> <span class="mi">8</span><span class="p">(</span><span class="nv">%rsp</span><span class="p">),</span> <span class="nv">%r9</span> </span></span><span class="line"><span class="cl"> <span class="nf">syscall</span> <span class="c1">// 执行指令 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">ret</span> </span></span></code></pre></td></tr></table> </div> </div><h2 id="系统调用和用户函数">系统调用和用户函数</h2> <p>两者最大区在在于 系统调用执行的代码位于操作系统底层的内核环境中,而用户代码则位于内核之上的应用环境中。</p> <p>在 x86 架构中, 特权级别分为四个层次</p> <p><img src="https://ppd0705.github.io/image/learn_c/31_1.webp" alt="protection_rings"></p> <p>不同特权级别,CPU 能被允许执行的机器指令和使用的起存器不同</p> <h2 id="系统调用实现">系统调用实现</h2> <p>可以使用 int 实现系统调用</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="nf">extern</span> <span class="no">sub</span> </span></span><span class="line"><span class="cl"><span class="nf">global</span> <span class="no">_start</span> </span></span><span class="line"><span class="cl"><span class="nf">section</span> <span class="no">.text</span> </span></span><span class="line"><span class="cl"><span class="nl">_start:</span> </span></span><span class="line"><span class="cl"> <span class="nf">and</span> <span class="no">rsp</span><span class="p">,</span><span class="mi">0xfffffffffffffff0</span> <span class="c1">// 16位对齐 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">mov</span> <span class="no">esi</span><span class="p">,</span> <span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="nf">mov</span> <span class="no">edi</span><span class="p">,</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="nf">call</span> <span class="no">sub</span> </span></span><span class="line"><span class="cl"> <span class="c1"># use &#34;int&#34; to invoke a system call. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">mov</span> <span class="no">ebx</span><span class="p">,</span> <span class="no">eax</span> </span></span><span class="line"><span class="cl"> <span class="no">mov</span> <span class="no">eax</span><span class="p">,</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="nf">int</span> <span class="mi">0x80</span> </span></span></code></pre></td></tr></table> </div> </div><p>上述代码执行过程如图</p> <p><img src="https://ppd0705.github.io/image/learn_c/31_2.webp" alt="syscall"></p>