如何给 DTK 添加文档

背景 经过几年的摸爬滚打,DTK 作为构建 deepin 全家桶的基石,一直被说的主要有两个毛病: 内部人员觉得它不够稳; 外部人员觉得它无从下手; 第一个问题主要原因是 DTK 在最开始的时候缺失的东西太多,各种剧烈的变化同时进行,但是又没有比较好的版本控制和接口管理,所以经常是这个应用要用新版本,新版本删除了一个废弃的接口,用了旧接口的应用就崩了。由于最近 DTK 的变化日趋平稳,以及有了基本的版本控制,第一个问题渐渐淡出了人们的视野。 第二个问题外部人员无从下手的原因是 DTK 在开发过程中并没有留下接口文档,所以目前想使用 DTK,内部靠得是口口相传,外部靠的是阅读源码,完全的石器时代……甚至连社区的小伙伴都看不下去了,自己做了一个《Deepin开发指南》 。 是时候给DTK添加一下文档了! 工具选择 其实, DTK 之前是有写文档的,但是写得并不多,主要问题就是英文文档不好写。我也尝试写过,每个类、每个函数都只能憋出一句英文,这种文档跟没有是差不多的。所以,这里需要做一个艰难的决定:文档用中文来写,英文靠爱好者来翻译。 用什么文档工具呢? DTK 是基于 Qt 之上的开发库,最自然的想法是使用 QDoc,但是据使用 QDoc 搭建了 dde-file-manager 的 Github Pages 的 @BLumia 同学说 QDoc 对国际化的支持不是特别好,我们按道理是要支持中英双语文档的,遂放弃。除了 QDoc 以外,文档生成工具就是 Doxygen、还有一个KDE 项目自己的文档工具 KApiDox ,它们三者之间是什么关系呢?Doxygen 从 QDoc 上 fork 出来的,KApiDox 是基于 Doxygen 的封装……我没有怎么看 KApiDox ,所以就选择了 Doxygen 。 如何写 下面就是规则部分了。 1. 文档注释位置 为了保持头文件的清爽和干净,所有文档注释都要写在源文件中。 2. 多语言 按照 Doxygen 多语言处理的方式,需要在中文文档前添加 \~chinese 命令,在英文文档前添加 \~english 命令,这样设置 Doxyfile 中 OUTPUT_LANGUAGE=Chinese之后,就只会包含中文文档,而不会夹杂英文了,如: Read On →

关于 deepin-code-release 的一点新的思考

为什么要做 deepin-code-release 呢?其实我们在 deepin-code-release 的 README.md 中写得挺详细了: 从deepin项目启动开始,伴随着越来越多的deepin子项目产生,不同开源/商业定制版本的分割加剧,项目间的版本依赖问题、上游项目的patch丢失问题和系统环境无法恢复问题越来越严重,我们迫切需要一个统一的系统来帮助我们记录不同版本的ISO/软件仓库的状态,做各个子项目版本的版本控制和patch的版本控制,以达到快速恢复开发/系统环境,进而使针对不同系统版本的开发更加高效。 deepin release系统就是这样一个辅助系统,它的目标在于将它的每一个状态都对应到我们所有发布的ISO/软件仓库状态、每一个针对ISO/软件仓库的改动都能反映到这个仓库的改动上。 简而言之,deepin-code-release 是版本的版本控制管理工具,用来管理 deepin 众多“轮子”项目的版本号的,每次系统发布都需要进行一次记录,方便以后开发修问题的时候能找到当时的代码进行bug修复。 为了确保记录的准确,它的想法是这样的,每次(系统)发布前,深度的程序员各自检查一下自己负责的项目,如果需要跟随系统进行发布,就提交 CL 到 deepin-code-release 项目,这个 CL 会自动触发项目在对应仓库的打包,并且将打包结果反馈到 CL 的 Code Review 评分上,+1 分的就进行合并,反之闭门思过解决问题后再次提交,直到成功合并。 问题 以上需求还是听简单明确的,但是在目前的实现上却有一个问题:使用了 git-submodule 进行子项目的管理,再加上其他几个没有考虑的情况,这导致了好多小问题,以至于推行到现在还是阻碍重重: 1. 使用起来比较麻烦 deepin 的项目都是放在 gerrit 上的,它并没有一个统一的 git 用户(类似git@github.com中的git),所以同一个项目在不同开发人员的机器上的项目地址是不一样的,当成 submodule 放到 deepin-code-release 这个项目中以后,必须驱动项目地址中的用户名部分,再在 ~/.config/ssh 中添加一些额外的配置(参见 README.md )。 2. submodule 用起来比较别扭 最直接、也是抱怨最多的一点,是大家平常使用 git submodule 系列的命令并不多,所以对操作 git submodule 有不太习惯,而且就算是我比较熟练了,也总觉得 git submodule update 这个有点反直觉,感觉上应该是类似 git pull --recurse-submodules 的功能,但是实际上并不是这样。以至于很多人一不小心,把别个辛辛苦苦提交的更新又给退回去了。 3. 多了一个额外的仓库 原本的打算是每个开发都只需要克隆一个 deepin-code-release 项目,然后在其中 init 自己负责的项目并进行提交,需要关注多个项目的就按需 init 多个项目即可,但是实际推行的过程中还是发现大家并不是特别关注自己项目外的项目,deepin-code-release 里面就一个自己的项目初始化了,而且用着还挺别扭,还不如自己单独克隆一份,结果就每个开发上面既有一个自己项目的仓库,还要有一个包含了自己项目仓库的 deepin-code-release 项目,也是抱怨声颇多。 Read On →

15.7 研发心得——最好版本的台前幕后

15.7 终于如期地发布了……好吧,也就晚了2个工作日……毕竟这么好的版本,多等两天也可以的,对吧。需要多解释一句的是,我们之所以也没有按照往常的套路——拖到周五发,是因为周五刚好是情人节,毕竟程序员找个女朋友不容易,所以就拖了两天……这应该是个好借口吧 :P 发完系统第一件事情当然就是写《研发心得》咯,不知道从什么时候开始,《研发心得》成了发布标配,上午发系统,晚上就要《研发心得》,我只能微笑着说……嗯,挺好的!不过虽说是研发心得,但是别人看了我的心得以后都说像软文。我的想法是这样的:像软文就像软文嘛,让各位支持deepin的朋友看得舒舒服服的同时还能了解新功能,多好! 不过这次我准备换个风格,重点是真实。所以,这次的心得我准备说说 15.7 这个版本的台前幕后。 台前&幕后 “这是我用过的最好的deepin版本”。 我预测应该有很多用户会跟我有同样的想法,毕竟,对于发了这么多版本的我来说,15.7 是第一个版本让我有重新完整安装一次这样冲动的版本。三年多以来,每逢系统发布前,我都会挑一个良辰吉日加上一个好时间,这个时间其实主要取决于版本发布的延期情况而定,隆重地安装一下即将发布的新版本的ISO,说是为了测试一下其实是骗人的,毕竟我也测试不出什么bug来……主要是装一下心里面爽啊,你们要明天才能下载,我今天就用上了,呵呵呵…… 虽说每次的ISO我都安装过,但是我从来没有舍得把我现在在用的系统格式化掉重新安装,因为这个老家伙从deepin切换仓库到Debian我重新安装了一次系统开始就一直陪伴着我,没有必要的话,我是想一直让它继续往上升级的。直到这次15.7发布,我感觉它又是一次值得纪念的发布才想着来一次完整重装,不过冲动归冲动,我还是按捺住了心中那团火——还是没有重新安装,因为思来想去,升级上来的也是一样的嘛 :) 这次升级可以分为两大部分,一部分是DDE的优化,另一部分是仓库同步Debian上游。从一个用户的角度出发,我觉得吸引我的地方,或者说值得用户期待的地方主要有这么几个: 更快的启动速度 这个主要得益于“林姐”亲自操刀的文件预热技术warm-sched,它主要的原理是这样的,大家知道操作系统在启动的过程中,会进行大量的IO操作,这个在CPU性能不是问题的平台上就是启动慢的罪魁祸首了,大家还知道Linux内核其实本身是有文件缓存机制的,被缓存的文件访问会非常非常快,所以如果我们在系统加载的适当时候能进行一定的预热,那么系统加载的时间当然就会大大缩短了。 不过这个会导致的一个问题就是,用户登录的速度变得飞快(从原来的10s+变成3s),但是系统好像在deepin水波纹的时间似乎更长了,其实不用”似乎“,真的会这样,文件预热技术不是神话,它没办法吃掉那部分加载文件的时间,它能做到的只是在合适的时间点预热下一个阶段需要的文件。 ”这不是拆东墙补西墙么?“你可能会这么问,但是我要告诉你还真不是,如果没有预热,系统加载文件的过程是”乱序“的,这里的乱序是指文件存放不一定是顺序存放在磁道上的,乱序的文件访问会导致磁盘访问的过程中转来转去的,效率低下;相反,warm-sched不仅只是对文件预热,加载的过程可以保证磁盘的顺序访问,文件多了的话,这部分优化还是比较可观的…… 我记得以前有位同事总是喜欢开玩笑说”苍蝇也是肉啊“,当时在饭桌上没少让人倒胃口,但是这句话用在性能优化、以及上面这个例子上算是再合适不过了。 更少的资源占用 首先是电量,如果你是笔记本的话,应该能感受到笔记本续航的提升,deepin测试人员实测的数据是以前3小时续航能力的笔记本,待机会多出30分钟+的时间,也就是提升差不多有17%,跟使用powerstat这个工具测试的效果基本相差不大。那是不是原来待机4个小时的笔记本,也只会多出30分钟的续航时间呢?并不是的,因为这是一个功耗比例上的变化,这次优化以后理论上可以增加的待机时间就是 原来的待机时间x17% 了,如果你的电池健康程度够的话,应该能有更大的优化效果。 不过,这些都是在使用电池的情况下的数据,因为默认情况下deepin系统只有在拔掉电源的情况下才会自动进入节能模式,如果你想在连接电源的情况下也开启节能模式,我是说假如你真这么环保的话,你可以在控制中心的电源模块手动打开节能模式;相反,如果你像我一样不在乎耗电,只追求卓越的性能,你也可以随时把节能模式关掉,大丈夫就是这么帅,不怕编译的时候机器烫手;总之,由着你的性子来! 另外一个能明显感受到的变化是开机内存的减少,这里所说的开机内存是指在没有任何开机启动项的情况下,DDE达到稳定状态的一个内存值,由原来的 1.1G 占用降低到 830M 左右,在使用独显的情况下,内存占用会更低,简直令人发指!好处是什么呢?更低的内存占用意味着DDE变得更加轻量,应用程序可以占用更多的系统资源,例如你是chrome这个内存占用大户的用户,15.7 可以让你在系统不变卡的情况下多开十来个标签页呢,是不是很开心? 不过做这些优化的过程中最让我感到意外的是DDE对电量消耗的“助攻”竟然没有大家预想的那么多,进程抽风性地CPU占用高、间歇性地进程状态切换等居然连硬件功耗的零头都占不到,不过该改的地方一个都不少,所以,DDE 的CPU占用高的问题在15.7中也可以不用再见了。 更新的软件和驱动 这个就不用多说了,搭载了Debian上游最新的一波升级,你想要的“更新的软件”、“更新的驱动”统统都给你,就是这么大方!而且,这次你收到的不仅仅是一次更新带来的快感,更是deepin更新会变得更加快、准、狠的承诺。在15.7的需求中我们争论了好多次,一方说已经有太多用户吐槽我们软件不够新、基础库不够新,用户需要新的软件;另一方说更新太快容易导致用户环境不稳定,我们毕竟是一个面向普通用户的发行版;争论不休,都可以开一个深度辩论赛了……最后终于达成一致,我们希望能更快速的将软件更新推送给用户,不再一次性积累大批量的更新,同时保证一定的节奏,确保快速更新的同时不会挂挂挂。 另外,值得一提的是论坛用户和老板不停的要求、催促和威逼利诱下,我们终于——终于把PRIME方案引入到了深度显卡驱动管理工具中,现在如果你是双显卡(N+i)用户,你只要小手一点,就可以方便地在几个预设方案中轻松切换了……嗯,不辛苦,用户和老板高兴就好 :) 一点感悟 上面说了这么一大堆,其实当时为了准备这些优化的内容的时候,心里面还是比较慌的,因为系统优化这事儿做起来远没有听起来那么爽,尤其是在没有既定方法的情况下要定一个优化目标出来,还是相当无助的,而且一旦你陷入 盲目—烦躁—焦虑 的怪圈中,就很难再痛快地出来,这就是这次15.7所面临的第一个挣扎。 我跟几个程序员都交流过,程序员大多都有比较强的焦虑感,再加上程序员多有洁癖,动不动这不清真那不科学,更容易掉进上面这个怪圈,在这个怪圈里面,你要么有一天实在承受不了了,咔嚓——破罐子破摔,从此成为浩瀚宇宙中的一粒尘埃;要么有一天你想通了:“还想个毛,就是干”,然后挑一件事后跟别人扯淡都羞于提起的小事,慢悠悠地开始做,做完了你会发现你的心理负担变轻了不少,然后再挑一件小事,接着干……越干目标越近,焦虑感也越来越少……最终,完成发版大业。 所以,当时我们不知道到底优化目标定成什么样子,那就捡最笨的方法,跟其他几个操作系统:Ubuntu、Win7、Win10做一个横向对比,看看其他几家的情况,至少做到在单一侧面都不是最差的吧?(做到任一侧面都是最好的,就留作下次优化的目标吧,毕竟大家都等着咱们发版呢。)定完了就朝着目标开始研究怎么使用优化的工具:perf、valgrind、heaptrack、google-perftools等,大家都不怎么有经验?那就学一个培训一个,各自有任务去练习……等优化的patch都集成完了,对比下优化目标,不够或者还不满意就再来……所有15.7可见的优化都是这么一点点的“苍蝇肉”拼出来的。 刚好最近在Twitter上看到的一段话,可以把我上面想说的非常明白的表达了出来,把这段“心得”分享给大家: 真的,诸位,有什么难事千万别耗着,别等着,那只会让人在无尽的焦虑中煎熬,你就先大吼一句:“去你妈的。”然后两眼血丝地去推进,去做事,做着做着就有出路了。

致王勇

以下内容是我在知乎对“如何看待Deepin操作系统创始人王勇离职”的回答。 这个月,从王勇改微博后缀,到删除deepin字样相关的代码,再到一则朋友圈,最后论坛的一篇《感谢亲爱的你们,大家继续加油》,有好几个朋友过来问、或者代朋友问王勇的情况,我虽然已经知道他的打算,但是不管朋友远近我都是说他在家休息,因为我知道他离开的消息有多大的影响。现在大家都知道了,也都震惊了,有人觉得伤感、失落,包括我自己都一样,这没什么,但是很多无良的媒体大肆渲染,最后以讹传讹,我甚至感觉我明天就不用去上班了…… 压抑了好久,有必要在这里说说。 王勇离开深度,我觉得对我最大的影响是 少了一个导师,没有了精神寄托。 跟很多deepin的粉丝一样,我还在上大学的时候就知道了deepin,随之就对deepin和ManateeLazycat(王勇的网名)产生了巨大的喜爱,成了王勇的脑残粉;幸运的是我居然凭着自学(当然也得感谢当时猴哥和xiangzhai在网上对我的指导),拿到了去深度工作的机会。而我刚进深度的第一个目标就是“王勇,我要在技术上超越你”,被王勇知道后以至于到现在还被他拿出来刺激我 :O 深度当时的技术氛围非常浓厚,因为一个屋子,清一色的程序猿。所以在深度的第一年里,是我技术成长接触面最广的一年,学了很多语言,接触了很多技术,对我影响最大的可能就是跟着王勇和猴哥学了Emacs,另外也自学了Elisp和Common Lisp,虽然当时心高气傲搞了一个hualet-emacs,实际上用王勇的话说“就是对着deepin-emacs抄嘛 :P”,事实上也确实如此,那时候也是第一次看到王勇在UI细节打磨上执着,他把emacs的每个插件都折腾成自己的风格,所有插件放在一起你都不觉得他们是分开的,自成一套体系,对我影响很大。 后来的一年多时间,是我在深度的沉寂期。默默无闻,但是我看到了王勇做的一些事情,其中就包括跟其他社区的人吵架,或者叫“互喷”吧。当时有点不能理解,社区都好好相处,不是挺好么,还容易积聚力量,后来慢慢懂了。前段时间看《走向共和》和《曾国藩的正面与侧面》,我觉得王勇很像晚清那些想叫醒中国人的斗士、英雄,看到开源社区很多浮在空中的“所谓的大牛”,说的话都对,却很少或者说从来不做实事,而愤怒地发出怒吼。 “有了求实一念,人才会从道德制高点上下来,脚踏实地,不激不随。”我觉得王勇能成为我心目中的领袖,是他很早就知道“求实”,而不是站在道德的制高点上喷这个、吹那个。 15年的时候,我迎来了自己在深度的机会:掌管桌面组和系统发布。但实际上当时我申请了这个职位以后没过两周我就后悔了,深度的用户都知道,15版本是近年来最大的一次版本变迁,首先是仓库从ubuntu切换到了debian;另外是桌面环境全换Qt重写。这对我来说不仅是巨大的工作量,关键是我当时还主要是做应用,压根儿不知道这个桌面是怎么一步一步被做出来的,还要面临的一个问题是为了赶商业机会,我们的重写必须在那3-6个月完成,那时候真的是异常痛苦。 也就是这个时候,我和王勇的关系才从以前一种盲目崇拜与被崇拜,变成了学生和导师的关系。记得非常清楚,无数个加班的夜晚,我跟兄弟们加完班以后,还得找王勇“倒垃圾”,这个事情有多难、有多坚持不下去、这方面有问题怎么办、那方面又问题怎么办……有时候王勇也不见得有什么好的办法,但是有一个人一直在支撑你走下去的感觉,会产生奇迹。这种经历一生也不会有太多。 那半年是我蜕变的半年,王勇在其中起了太多的作用。 所以要说王勇身上背负的不仅仅是自己的压力,还要背负兄弟姐妹们身上的压力,这个压力我自忖是完全承受不来的。这也是为什么当时猜出来王勇要离职的时候,我对他说“从公司员工的身份上我对你这个决定是接受不了的,但是从朋友的角度我觉得你做这个决定我很佩服”。他确实承受了太多事情,如果都这么决定了,我们能做的就是尽量不要给他压力。 王勇临离开之前做的最后一些事情,就是研发各个团队的负责人挨个谈了一遍,跟我说的内容很少涉及自己怎么样,更多的是帮我捋清方向。 就这样。 至于说深度会不会因此倒闭了,或者说deepin项目会不会凉了,我觉得至少短时间内不会。 一方面,王勇作为武汉研发总部的负责人,长期以来从来也没有忘记去培养人,培训和言传身教,影响了一部分有想法的同事,虽说我们现在没有能力像他一样作为个人去领导一方,但是目前在做的事情是可以承担起来的;另一方面,就像 张成前辈 的回答里面说的一样,深度前期能在完全没有盈利目标的情况下开始做,到现在依然坚持在为社区做贡献,王勇的热情是一部分、刘总(deepin)对社区的看重是另外一部分。现在王勇出走了,但是我没有看到刘总说社区就此打住、不再做了,实际上公司上下稍微有点Linux背景的人都没有质疑过社区这一条方向。所以deepin项目不会就此凉了,相反我们可能会在做国产化的同时去探索更多的面向社区和普通消费者的方向。 最后,致勇哥一声深深的谢谢!

使用 Perf 优化程序性能

因为原定deepin 15.7会做优化(能耗、资源占用等)方面的工作,所以在此之前,想提升一下团队整体的性能剖析、优化方面的水平,也就有了这次(周五)的培训。本来想用视频记录的,一个是自己回头能看看,反思和改进一下演说能力;另一个是以后有新人入职,我也就不用挨个再讲一遍,天不遂人愿,录制视频的软件半道挂了( 所以只能这里尽量回忆当时想表述的内容,这里文字稿记录了。 下面就是培训内容: 好,人到齐了我们就开始了。今天要培训或者说要分享的内容是程序性能优化方面的内容,其实我们对性能优化不陌生:最开始接触龙芯和申威平台,系统组件间调用不是异步导致系统卡得无法使用;好不容易交了一个版本,控制中心各个模块还是因为切换卡顿、使用体验不好等做改进;前段时间为2G内存机器做优化等等。现在我们因为程序设计问题导致的性能问题比以前少了很多,一方面我们还要对这些部分持续改进,另一方面我们需要掌握一些性能剖析工具的使用,来帮助我们改进一些更细粒度的程序性能问题,今天我们主要集中在程序执行性能、也就是CPU占用这部分的性能优化。 今天培训的内容比较基础,还是老规矩,我做敲门砖,其余的还需要各位发挥。 刚才说了我们以前做了几次优化,我们再来回顾一下为什么要做性能优化。第一点肯定是占用过多的系统资源,不论是CPU、内存还是IO,如果系统组件的资源占用比较高,那系统资源有限、程序间的资源又是会互相竞争的,必定导致用户使用的应用程序的可用资源减少,在一些配置普通的机器上,可能就会导致第二个问题,也就是系统使用不够流畅,当然导致系统使用不流畅也分两部分原因,一个就是我们刚才说的,系统组件占用资源过多,另外一个也是刚才提过的程序架构设计有问题,导致卡界面等等; 第三个是性能问题在低配置的机器上会被无限放大,就像我们在最初的龙芯和申威平台一样,在x86上可能感受不到或者感受不太强烈的问题,在龙芯和申威上就会严重到无法使用(当然,现在的龙芯和申威平台使用已经没有问题了);最后一个也是跟我们15.7想要解决的能耗问题息息相关,就是能耗高、发热大。主要是我们有些系统组件“不老实”,很多情况下随机“抽风”。 这个问题我们论坛用户提到的也不在少数: 当然了,大家可能也知道功耗主要跟系统配置关系很大,例如CPU开不开睿频、有没有设置节能模式,各个网卡、声卡驱动有没有设置节能模式等等,但是我觉得至少我们的程序至少不能成为能耗高的帮凶吧。 现在的情况是我们的程序不仅是帮凶,而且帮得很厉害,一会儿会给大家看一下。 刚才说了我们做了很多次性能优化,可能很多用户期待说我们能一次解决所有性能问题,但是实际上性能优化是持久战,它是一个需要持续做下去的事情。 另外主要还是因为性能优化很难,第一点主要就是性能瓶颈的定位很难,比如最开始我们系统登录的过程非常慢,所有程序都是并行启动,看着所有的系统资源占用都很高,换成串行启动以后,依然如此,这个定位当时就比较麻烦; 第二点是有时候优化的效果并不明显,虽然说我们看到有时候系统的资源占用挺高,但是可能分到每个程序中就不是特别多,做性能优化得慢慢抠,可能从单个程序来看效果并不明显;第三点是有时候做性能优化的技术和经验要求比较高,一方面是性能剖析工具的使用、对程序运行的本质要熟悉,另一方面就是要对被优化程序的代码要非常了解,不然就会导致我们说的第四个问题:优化可能影响正常功能。比如我上次给Dock提交了一个性能优化的提交,虽然有让sbw同学审核代码,但是最后还是导致了一个功能性bug。 虽说性能优化很难,但是……不积跬步无以至千里。所有复杂的系统都不是一天两天做好的,比如我们的桌面环境,到现在经过了四个版本,基本上就是四代人的心血堆积,才做成现在这个样子。 我们每个人都能做好自己那部分的话,众人拾材火焰高,解决系统性能问题我觉得没有那么麻烦,这其实也是为什么我这么迫切要组织这次培训/分享的原因,还是希望我做好敲门砖,像去年做高分屏支持一样,虽然是我开的头,但是其实最后基本上每个都比我做得好,最后我们做高分屏的效果也非常不错。 刚才说了性能优化很难,大家也不要害怕,其实性能优化也没有想象的那么复杂。 第一个是因为现在优秀的工具很多,比如右边这个非常出名的图,来自 Brendan Gregg 大神的博客。把系统性能优化每个部分对应的工具都清楚的标出来了。 第二个说实话是因为deepin的性能问题还比较多,比较容易发现,相应的上层的性能问题也多,所以说性能优化简单也是因为我们这次主要将注意力集中在 Applications & System Libaraies & System Call Interface这三部分即可。这部分的工具可以看到其实就是 perf、ltrace、strace这些,最多加上 bpfcc 、eBPF等。 说deepin性能问题还比较多是大家可以看到,静置状态下,我什么都不操作,几个组件就又可能时不时抽风一下(占用CPU部分)。 看到了有性能问题,但是我们总得有办法把这些性能问题优化掉吧。 性能优化一般分三个部分,第一部分就是查找程序性能热点,实际上一般我需要先定位性能瓶颈到底是在哪部分,是CPU?是内存还是IO等。不过我们这里目标很明确就是优化CPU占用,所以可以直接朝着有性能问题的程序去。 第二步是热点诊断和修复,找到了性能热点,我们肯定需要修复吧,看看导致这个热点的原因,正常进行修复即可。 第三步是回归测试,就像我们平常修bug一样,修了bug要测试,做了性能优化也一样,我们需要做回归测试、对比一下前后的性能数据。 我们先来看第一步,利用perf查找程序性能热点,这也是我们今天要说得重点。 perf这个工具最开始是作为Performance Counter的接口引入内核的,但是慢慢引入了一堆调试接口如 tracepoint、kprobe、uprobe等等,也就慢慢发展成为Linux几乎最好用的性能调试工具了。 我们平常使用的perf命令是用户态的工具,前面说得那些都是内核里面做的事情,内核态工具主要是对搜集到的数据和事件进行处理和统计。因为这个工具跟内核版本关系比较紧密,所以安装perf的时候需要注意跟内核版本对应的问题。当然,调试的时候装dbgsym包是必须的。 使用perf查找程序性能热点,一般主要用到三个子命令,第一个就是 perf top,这个很好理解,top命令大家都用过,那个top主要是针对进程或者线程级别的资源占用进行统计和展示,perf top可以理解为函数级别的top,可以动态展示系统目前占用资源最高的函数分布情况,它后面的 -g 是启用函数栈,-p 后面加上进程 PID就可以针对单个进程进行追踪,这个跟top命令一致,如果不加 -p 就是默认系统级的统计和展示。 第二个命令是 perf record,它跟第三个命令 perf report 是搭配使用的,record 用来记录一段时间内的程序执行情况,然后用 report 来进行展示。 -g 参数的意义跟 perf top一致,启用了函数栈以后,我们可以使用 –call-graph 来制定使用哪种方式来获取函数栈: fp 方式,是使用传统的方式 frame pointer来获取堆栈,这个我在之前的文章中也介绍过; Read On →

自制 Profiler 第二部分——调用栈回溯

书接上篇,我们现在已经能在其他程序中执行我们自己的代码,并且也做到了以固定的频率去执行采样代码(我们的printf),但是如何采样还是一个问题,这篇文章会就这这个问题继续探讨接下去我们面临的挑战——调用栈回溯。 为什么要获取函数调用栈?一方面是因为profiler除了要分析程序存在的性能问题,即函数执行热点以外,还需要帮助我们可怜的程序员找到问题的原因,这时候能提供问题函数的堆栈信息就非常必要了;另一方面,我们上一篇文章其实说了,是为了通过堆栈信息尽量还原程序的执行过程:试想一个程序执行的过程是 main->funca->funcb->funcc,我们第一次采样 main->funca,第二次采样 main->funca->funcb->funcc,假如我们没有堆栈信息,我们只会统计一次 funca 和一次funcc,但是这并不能反应事实,相反,我们有堆栈信息的话,就会把 funca 、funcb 和 funcc 各计数一次,更能反应实际的执行过程。 概念 函数调用栈(Call Stack)和相应的栈帧(Stack Frame)我们其实都不陌生:在使用 gdb 调试程序的时候,bt(backtrace)命令打印出来的就是函数调用栈;而函数调用栈列表中的每一项则代表一个栈帧,我们执行 frame 命令跳转到某一个栈帧,其实就是一次回溯的过程。 想要在内存中解析出我们想要的函数调用栈,首先我们需要知道的就是一个程序的stack 段里面各个栈帧是如何布局的,要搞清楚这个,我们还需要了解一个概念叫:调用约定(Calling Convention),调用约定主要约定了(好绕): 函数的参数是如何传递的,是全都放到寄存器,还是全都放在 stack 段,还是混用两者; 函数的参数是按什么顺序放置到内存中的; 函数中的本地变量是如何分配的; 子函数调用是如何返回的; 子函数的栈帧是如何清理的; 等等 所以,调用约定基本上决定了函数调用中每个栈帧的产生、压栈、出栈对内存布局的影响,而这个约定是因架构和平台而异的。我们这里只关注x86 平台下的 cdecl 约定。 在这个约定下,假如我们有一个函数 DrawSqure 调用了 DrawLine (例子来自Wikipedia),那么程序内存布局中的 stack 段就应该是类似下图所示: 每个函数调用即创建一个栈帧,每个栈帧一次压入 stack 中。 其中,Stack Pointer(esp) 永远指向栈顶, Frame Pointer(ebp) 指向当前栈帧的中一个固定的地方(基地址);函数参数以从右往左的顺序依次压栈,然后是压入Return Address ,它是当前函数(或者栈帧)执行完成后,程序要继续执行的指令地址, 同时压入父函数的栈帧基地址(Saved EBP),它是当前函数执行完成以后,Stack Pointer 和 Frame Pointer 将会指向的地方,基于这个地址,程序指令可以方便地访问函数本地变量(ebp负向偏移)和函数参数(ebp正向偏移)。 结合上面两张图,其实可以看出,每个栈帧其实都保存了上一个栈帧的基地址,因此所有的栈帧最终组成了一个链表,这也就是我们能拿到函数堆栈的理论基础了。 (注:上面只是粗略的讲解,参考链接 [1] 非常详细的描述了函数调用的过程中栈帧、stack 段和esp、ebp寄存器的变化,如果感兴趣,可以详细了解一下。) 参考方案 看完上面一大串概念以后,我们发现如果我们要按照函数约定的方式去获取函数调用堆栈,可以,但是太过蛋疼,而且不跨平台,很难受。 所以秉承不要重复造轮子的优良传统,我们发现有几个方式可以简单地获取到函数调用栈: Read On →

自制 Profiler 第一部分

​ 最近对性能剖析的技术颇感兴趣。好不容易来了三分钟热度,自然不能浪费,因此在余热消失之前研究并实践了其中一部分细节,对于其中一些知识点,个人感觉对于自身编程能力提升还是比较有益的,因此在这里写出来,抛砖引玉。 Linux profiler简介 ​ 性能剖析的工具,其实在Linux平台还是挺多的,比如小巧实用的strace、ltrace、latrace,大名鼎鼎的google-perftools、gprof、valgrind,以及瑞士军刀型的linux-perf等等,它们主要分为三个阵营,一个是针对程序执行的性能进行剖析,对程序执行的热点进行分析,如*trace、gprof这些工具;另一个是针对程序运行过程中内存的使用进行剖析,方便针对性地做内存优化,如valgrind;最后一部分就是“脚踏两条船”的,两个我都做,比如google-perftools、linux-perf这些,我们这个系列主要集中在“针对程序执行的性能进行剖析”方面。 Profiler的本质 ​ 要自制Profiler,首先要知道Profiler的本质是什么。简单来说,Profiler的本质其实就是在程序执行的过程中对程序正在“做什么”进行搜集和统计,如何搜集呢?无非两种: Instrumentaion - 程序主动Profiler告诉它在做什么; Sampling - Profiler自己间断性地去看程序在做什么; 前者的实现主要依靠程序运行时提供的某些钩子机制、代码插桩等方式,例如gprof主要是依靠 gcc 在编译程序的时候”夹带私货“来达到程序运行的时候主动提供给gprof采样样本,来达到事后分析的目的[1]。这种方式相对来说虽然比较可靠、准确,但是对于无法控制编译条件的程序就比较无可奈何了。 后者的实现则主要是定期对程序当前执行的指令和对程序执行的函数堆栈进行回溯(unwind)来尽量还原程序执行的过程,例如google-perftools就是采用这种方式,这种情况下,采样的周期就显得尤为重要。 ​ 对比以上两种方式,我们果断采用第二种。 ​ PS: 这里需要说明一下的是,利用ptrace系统调用完成工作的strace和ltrace,虽然不依赖编译器夹带私货,但是相当于依赖了内核的“钩子”,不属于sampling的范围;同样,latrace则是依赖了ld的LD_AUDIT“钩子”,也不属于sampling的范围。 Profiler启动 ​ 那么问题又来了,ptrace没得用,我们怎么去获取被剖析程序的执行状态呢?总不至于profiler要搞成root权限的吧?答案是:不,内核管天管地,总管不到程序自己偷看自己的数据吧?我们想办法把我们的代码塞到被剖析程序中去就可以了! ​ 刚才提到了ld的LD_AUDIT,这次就轮到它的兄弟——大名鼎鼎的LD_PRELOAD——登场了。想法是这样的,我们的profiler其实只需要在程序开始的时候执行一个定时器,以后每次定时器执行的时候去抓取我们的样本就OK了,所以我们完全可以把自己伪装成一个人畜无害的动态库,等到别个程序有意无意加载到我们,哼哼……事实上,很多profiler都是采取类似的策略,比如google-perftools,再比如heaptrack等等。 编码实战 ​ 废话少说,放码过来! ​ 我们在QtCreator中创建一个动态库项目 simple-profiler,主类 SimpleProfiler。首先,我们需要设置好我们的定时器: void SimpleProfiler::enableProfile() { int ret = setitimer(ITIMER_PROF, &m_tick, nullptr); if ( ret != 0) { fprintf(stderr, "failed to enable profiler: %s", strerror(errno)); } } 根据 setitimer 的man文档,计时器主要有三种类型: ITIMER_REAL 这个计时器是根据墙上时间进行倒计时,最终触发 SIGALRM 信号; ITIMER_VIRTUAL 这个计时器是根据进程花费在用户空间的 CPU 时间进行倒计时的,最终触发 SIGVTALRM 信号; ITIMER_PROF 这个计时器是根据进程话费在用户空间和内核空间的 CPU 时间进行倒计时的,最终触发 SIGPROF 信号; 个人认为,假如都是用在 profiler 上的话,第一种计时器用来查找程序执行慢(某个函数有IO等情况)的瓶颈比较有效果;后两种计时器用来查找程序 CPU 占用瓶颈比较有效果。不过使用第一种计时器的话,对被追踪程序的执行影响相较于后两者就比较大了,所以这里采用第三种计时器。 Read On →

初探Linux内核态——通过proc文件系统作快速问题定位

文章翻译自 Peeking into Linux kernel-land using /proc filesystem for quick’n’dirty troubleshooting 这篇博客的内容完全是关于现代Linux内核的。换句话说,指的是与RHEL6一样使用的2.6.3x系列内核,而不是古老的RHEL5所使用的2.6.18内核(都什么鬼了?!),虽然大部分企业都还在使用RHEL5。另外,这篇文章也不会涉及内核调试器或者SystemTap脚本之类的东西,完全是最最简单地在有用的proc文件系统节点上执行“cat /proc/PID/xyz”这样的命令。 定位一个程序“运行缓慢”的问题 下面要举的这个例子是这样的:一个DBA反映说他们的find命令一直运行缓慢,半天都没有什么输出,他们想知道这是为什么。听到这个问题的时候我就大概有直觉造成这个问题的原因,但是他们还是想知道怎么系统地追踪这类问题,并找到解决方案。刚好出问题的现场还在…… 还好,系统是运行在OEL6上的,内核比较新,确切地说是2.6.39 UEK2。 首先,让我们看看find进程是否还在: [root@oel6 ~]# ps -ef | grep find root 27288 27245 4 11:57 pts/0 00:00:01 find . -type f root 27334 27315 0 11:57 pts/1 00:00:00 grep find 跑的好好的,PID是27288(请记好这个将会伴随整篇博客的数字)。 那么,我们就从最基础的开始分析它的瓶颈:如果它不是被什么操作卡住了(例如从cache中加载它所需要的内容),它应该是100%的CPU占用率;如果它的瓶颈在IO或者资源竞争,那么它应该是很低的CPU占用率,或者是%0。 我们先看下top: [root@oel6 ~]# top -cbp 27288 top - 11:58:15 up 7 days, 3:38, 2 users, load average: 1.21, 0.65, 0.47 Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie Cpu(s): 0. Read On →

15.5研发心得

迟到的经典 2017年11月29日,农历十月十二,经过一番挣扎,deepin 15.5的ISO终于安静地躺在了内网服务器上,静待着属于它的时刻的到来。 是的,deepin在重新定义了十月一日为农历十月初一以后,甚至连“保证月底发布”的底线也没有守住,妥妥地把发布时间拖到了十一月(阳历)中旬。即便如此,我也觉得这次“迟到”是值得的,在“手机不赚钱,交个朋友”的时代,PC操作系统的研发需要特别用心,才能留住那些对我们期待了那么久的用户朋友。我想,在未来可以预见的时间内,所有deepin的用户都会记住这个特殊的版本号:15.5。这个能让我们在视觉体验上有质的飞跃,在应用包格式上走在世界前面,自带修复工具箱——live修复系统,并且在稳定性和易用性上大幅提升的版本。 deepin15.5注定成为经典。 故事 按照传统,一年中各大版本的主要功能都是预先定义好的,搭配一点重要的bug修复和用户体验改进,基本上就妥妥的了。但是15.5是个奇葩。在七八月份定义15.5详细需求的时候,我们梳理github上的issue,发现一条一岁半“高龄”的主题,大意是“跪求高分屏支持”;同样早在16年初,公司成立了一个神秘的组织叫“Alpha Team”,这个小组的第一个任务就是“独立包格式”;在记忆都稍微有点模糊的14年,就有用户想要在deepin下创建热点……这几个严重拖延的任务让人汗颜,所以我们当时就任性地决定15.5主要来解决这些问题。虽说是决定,但是并没有人特别自信,“毕竟是拖了这么长时间都没有解决的问题”,再加上高分屏涉及面太广,深度全家桶都要“挨刀子”;独立包格式本身社区也不统一;WiFi热点挑硬件,Linux下的网卡驱动又不都是那么好……越想越恐怖。 “要不再加点其他任务?”,我提议。 众人惊恐状。 “这样万一前面几个任务完成不了,做点其他任务应付一下老王嘛”。 逆向思维有时候还是挺管用的。 就这样,像是一锅羊肉火锅一样,15.5的配菜变成了主菜,也就是前面提到的三个点;又加入了新的配菜,VPN导入导出、应用代理、触摸板手势、色温调节、bug修复和其他一大波新应用等。 当所有人都以为15.5就这样的时候,一个不识趣的朋友跟老王说我们系统的恢复模式进不了root,所以系统一旦出了问题就只能靠重装大法解决。我闻风一边心想绝对不可能,一边赶紧试了下,结果是果然不好用。 还没等我返回神儿来,老王已经在Tower上建好了“live系统支持”的任务。 高分屏 高分屏支持的话题由来已久,大概是在我刚进入deepin(14年)的时候就有人在“布局”:图片资源一定要用svg、尺寸不能写死等等,然而坚持了一段时间以后,大家发现这种坚持似乎毫无意义,因为没有人用高分屏,自然发现不了高分屏那种细腻到看不到“痘痘”的美,于是乎慢慢地各种png、死尺寸就满天飞了。 俗话说“出来混总要还的”,因为以前的任性,在适配高分屏的过程中,deepin团队可谓吃尽了苦头。不仅几乎所有程序中用到的图片资源都要重新绘制(还好图标主题一直坚持svg),还差点在x2.png的道路上栽了个跟头,还好悬崖勒马,及时止损。 普通应用适配高分屏在如今的环境下,Qt和GTK等界面库的高分屏支持已经比较成熟,还算是比较简单的。但是桌面环境和一些特殊应用,如启动器、截图、录屏等,就需要些功夫了,在适配的时候还要考虑缩放前后的窗口大小、窗口位置等等,恰巧这些地方Qt的处理又比较坑,有时候实在受不了了,只能patch Qt来处理。 看图的适配也比较麻烦,很多地方都用到了缩略图。缩略图也要支持高分屏!听起来有点好笑,但事实是不仅是缩略图,很多情况下一条线、一个圆角的瑕疵,在高分屏下都可能被放大,变得非常恼人。 15.5的细腻就是在这种情况下“磨”出来的。 Flatpak Flatpak其实原名叫“xdg-app”,可能后来觉得不够高端大气,配不上“The future of application distribution”的名头,遂改名Flatpak。 按照官网的说法: Flatpak is the next-generation technology for building and installing desktop applications. It has the power to revolutionize the Linux desktop ecosystem. Flatpak将会改变Linux世界的生态。 不谋而合,同样这样想的还有snap技术。 snap是Ubuntu为了布局服务器和容器市场推出的一套解决方案,与Flatpak类似,它也是利用Linux内核提供的沙盒机制和方案,再加上一套包分发、安装、更新和卸载的辅助系统,来实现应用与系统隔离的想法,以解决目前Linux世界包分发过程中经常让人头疼不已的依赖问题和系统安全问题。 在两者之中,deepin毅然选择了Flatpak,为什么?因为从Mir vs Walyland的例子中,我们发现一个非常真实地存在于开源世界的“潜规则”:“政治不正确”的项目,在开源世界中一般是无法长久的,其中所谓的“政治不正确”其实就是“g家族”已经有同样的项目存在,又有人造了“轮子”。所以,与其寄希望于一个可能不能长久的项目,还是在Flatpak上孤注一掷来得靠谱一点。 想法虽然显得投机了点,但是像往常一样,deepin团队做起实事来一点也不含糊。深度全家桶中的成员,非系统级的应用,全数被打成了Flatpak包。重要的是,这些Flatpak包已经在15.5的ISO中替换了原来的deb包,也就是说deepin 15.5可能是全球第一个预置了Flatpak和自家应用的Flatpak包的发行版了。 “少数人活在未来,人们中的大多数则是静待着未来的降临,这是整个人类文明的演进方式”,最近耳朵里总是充斥着这样的声音。我想deepin团队在推动Flatpak上是走在世界前列的,为身处这样的团队感到骄傲。 live修复系统 对于喜欢折腾系统的用户来说,这次可能最期待的就属live修复系统了,它是个什么东西呢?可以这样简单理解,live修复系统就是一个超级mini版的deepin系统:除了量身裁剪过的DDE外,只预装几款必要的系统工具。最重要的一点是,live修复系统独立于你所安装的deepin系统,完全运行于内存中,这样,你就不会遇到在你修复系统到一半的时候系统弹出警告框,告诉你需要卸载某个磁盘之类的尴尬情况了。 在live修复系统中,预置有深度全家桶的一位新成员“深度备份还原工具”。这个工具具有化腐朽为神奇的超级牛力,它可以助你轻轻松松地把老系统上的数据迁移到你的新硬件上:全新的机器内部,还是熟悉的老伙计——deepin。 live修复系统为了保持简单、简洁,目前除了必要的浏览器、备份还原工具和终端外,并没有预置更多的工具和应用,毕竟要完全跑在内存里嘛。在系统中安装上live修复系统,打开启动器,你甚至感受不到它的存在!因为它的入口确实不在启动器里面,而是在grub菜单。好的功能/用户体验就是这样,需要它的人一定知道它在哪,不需要它的人完全没必要因为每天都能看到它而感到不自然。 在可预计的将来,应该会有更多实用工具登陆live修复系统,让你进入live修复系统,就感觉到自己像是被武装到了牙齿。 其他 每次介绍新功能,都不得不辟一个“其他”的栏目,因为新的变动实在太多,其中有些像网易云音乐新版一样,内部真刀实枪地大改造(用qcef替换原来的webkit),但是用户可能不会有太多直接感知的;还有一些“彩蛋”类的像是本次的“自动色温调节”,有些用户发现这个功能后溢于言表,幸福感可能都超过了使用这个功能本身的;另外的一些工具类,如Wi-Fi热点、VPN导入导出的工具类功能,天生就是理科男的命——绿叶。 在deepin系统的完整度方面,指纹识别算是这次的另外一个亮点了,如果你的电脑配备了指纹识别器,那么升级到15.5,你将从输入密码这项繁琐的工作中解放出来,一切都是那么顺滑。 Read On →

给小崔的基础管理课

最近小崔的日子好像不太好过,负责一个项目的同事发了farewell letter,于是她被莫名其妙地选中来担任整个项目的负责人。 这突如其来的变动,打了小崔一个措手不及:因为给同事分配任务不顺利气哭、因为担心第二天的工作是否能很好完成而失眠……就跟我刚开始担任深度桌面组组长的时候一摸一样。看着实在心疼啊,于是趁着今天天气好出去晃悠的时候顺便跟她传授了一点小小的经验,这里顺便记录一下,希望对有需要的朋友有所帮助。 在说初级管理的经验之前,我想强调一下“自信”,因为这是我觉得一个人能做成一件事情最基础的基础。如果连自信都做不到,那就不要妄谈管理了,你甚至连自己都做不好。小时候我妈一直说谦虚使人进步、骄傲使人落后,让我平时谦虚一点。我当时想不通,现在依然想不通,我甚至觉得自负要比自卑强得多,这是因为自信心实在是太难培养了。比如我虽然一直对自己的学习能力和技术比较(盲目)自信,但是我刚做管理的时候,其实也不自信能把所有人捏到一起、团结起来,但是因为心里面有执念觉得不能把做不到跟自己扯上关系,所以还是努力地去尝试,很清楚地记得有一次我当着所有人的面把自己的完整解决方案说出来那一刻,我的自信心才完全建立起来,之后的管理就相对容易了很多。说到小崔,我大概是不担心的,她只要把自己在镜子前臭美的状态转化一下,自信心应当是爆棚的 😄 自信做好了,用来做什么呢?这就是管理的内容了,管理不再是扎头自己的事情,而是掌握大局,在掌握大局以后在处理非自己处理不可的事情。什么是掌控大局?我觉得分两个部分:承上和启下,承上就是了解清楚上级的需求和工作内容;启下就是把需要做的事情分配给自己的组员,让他们清楚自己工作的内容,这个部分其实是管理的核心,后面会提到我的秘诀!什么又是非自己处理不可的事情?这绝不是自己什么都不做,所有事情一股脑塞给下属同事,而是在了解清楚工作内容以后,把那些能提升团队工作效率的事情或者实在是只有自己才能做的事做了,自身做则才能做到服人。 因为初级管理承上启下的特点,所以这种工作其实有时候特别难做。我从朋友那里学到了十个字,感觉对我的管理工作有着非常关键的指导作用:对上要有胆,对下要有心。仔细揣摩一下,其实不难发现这其实是在约束一个管理者的行为:面对上级领导的时候,不要老感觉自己是个小兵,要大胆跟对方进行沟通,因为你代表的不仅仅是你自己,还有自己的下属同事;面对下属同事的时候,不要感觉自己官大一级压死他们,其实你跟他们是与荣俱荣与损具损的共生体;这对初级管理尤其重要,因为初级管理更像是战场前线的班长,你连面对自己上级的胆量都没有,何谈面对敌人的炮火?你不把自己的班子成员当兄弟,谁会给你当炮灰? 当然,就算你做好这些了,做得非常好了,也不一定能让团队里面所有的人都服你,你一样会遭受挫折、遇到不顺利;这里首先要想到的就是:“如果这件事情简单,哪还需要老子上?” 在这种心态/格局下,再去看待问题可能会更容易接受一些;做管理,容忍度一定是要有的,因为林子大了什么样的鸟都有,在一时半会改变不了别人的情况下,只能让自己学会接受现实,伺机而动。情绪控制也是要有的,下属情商不够,也不能让自己失控,有情绪很正常,不然变成一个冷血的混蛋,没有人会跟你真诚,这样的管理就像是古代的暴政,总会被推翻的。有情绪代表你是一个有血有肉的人,但是也不要让情绪化过多干扰自己的理智,最最基础的也是要做到不要让情绪影响到工作。 这看起来很难,但是只要转换心态就可以让自己好过很多,比如你给对方分配任务,对方无理由拒绝或者态度恶劣,你可以想:“对方拒绝做或者做不好的事公司的事情,又不是自己的事情,犯不着跟对方闹情绪”。对,管理就是要这么灵活,该把公司的事当成自己的事的时候,拼死了干;该把不相干的事情甩出去的事情,就要爽快的甩出去。至少这个时候你无能为力了,这样安慰安慰自己,让自己平复情绪还是很有用的。不要怕沟通,尤其是跟刺头的沟通!那天做到了“今天就算是吵翻了天,明天还是要像一个没事人一样去跟对方相处”,基本上在这方面算是合格了。 还有一部分没有跟小崔聊的,大概涉及到项目管理的事情,还比较重要,所以提一下。分出去的任务,要定期做检查,毕竟公司有管理是为了管人,但是最终的目的是把事情做好。分出去的任务,反馈回来的至少需要有完成的时间和安排,拿到安排后按照自己的经验核算一下是否合理,如果不合理就需要跟对方再进行沟通,合理的话还要提前检查,不检查的时间相当于没有定!其实项目管理的精髓就是这个:计划、检查和沟通,其他的方法论都是技巧性的表皮罢了。 根据我这几年的工作和生活经验,说一下我的“困难观”。面对困难一般有两条路可选,第一是消极面对,相信这个事情总会能熬过去的,等熬过去了,又是春光明媚。这种一般在面对自己完全非自愿但又不得不去抗的事情时用来安慰自己,让自己不至于分裂、崩溃;另一个是积极面对,相信自己的聪明,找到问题的关键,搞定它,解决这个困难,你自己也会有所提升;这种情况我一般都把困难拆分成一个一个的难题(拆解问题的能力也很关键,因为暂时不需要,等以后再聊),像福尔摩斯一样运用自己的智慧去解开每个难题的答案,这样的面对困难重重的生活才不会觉得枯燥无味。 最后,不管使用哪种方式处理现在项目上的问题,我觉得都是对自己的一种磨砺吧,希望小崔可以顺利度过难关 :3