本书全名是 《0 bug- C/C++商用工程之道》,这是一本有争议的书,豆瓣链接:
不过本书的标题有点诱人,相信作者在实战中也积累了一些干货,所以准备阅读一下,并且结合自己的经验总结一下里面的部分思想。
嵌入式设备
对于嵌入式设备的特性,应该在运行的时候保护自身,因为自己资源有限,像网络上的路由器这种设备,通常需要处理大量的数据并且做一些转发等操作。在此期间,路由器的内存可能会被占满,当有新数据来的时候,需要将其丢弃,不然的话机器就挂掉了。所以说这一笔业务失败不算bug,应该是系统资源太少导致的。举个例子,家用路由器不能放在核心网上面,不然Throughput会大大下降,因为处理能力有限,为了提高性能应该购买较高端的设备。对于类似的驱动代码,在cortex-m3上和x86平台上相比,肯定是x86平台能够得到的性能比较高,在cortex这种低功耗平台上面能够得到的wifi的Throughput就只能满足基本需求,因为其处理性能有限,内存也有限,没法alloc到一个大的buffer来处理数据,就算buffer很大,由于cpu的处理速率限制,也会达到一个处理的瓶颈。
在TCP的层面上来看,因为lwip在cortex设备上开辟的buffer有限,所以TCP宣告的窗口也受限制。
线程
线程可能在运行的时候申请资源或者在做一些操作(比如在写文件),一般不要去kill线程,对于外部的ctrl+c也要做好信号的捕获。在线程运行的时候一般会设置一个变量表明是否要继续线程的loop。其他线程给这个线程发消息或者将这个变量设置为false来停止线程。
线程在运行的时候需要强制释放一下时间片。对于FreeRTOS来说,一个高优先级的线程(task)如果一直在运行,不释放时间片,其他线程就无法运行。一个典型的例子就是优先级反转问题(低优先级的线程获得了资源,高优先级的线程死循环来等待资源,这时候只有高优先级的任务在运行,这样就造成了死锁)。
线程之间的通信可以使用socket,因为不仅仅使用起来比较方便,而且可以扩展到服务器集群,甚至可以扩展到不同的操作系统。因为机器之间的通讯使用socket也是可以的。但是在使用Qt做开发的时候,我发现使用信号-槽机制也能很好的通信,主要还是根据特殊的环境选择通信机制。
这个是有待探讨的,可能是作者的操作系统环境有这个限制:(不要一下子开大量线程,否则线程没法被schedule)
本人没有做过这样的应用,一般的高并发服务器应用才会有这样的需求,需要真机测试、long run才能得出结论。
对BUG做好防御
在程序里面需要做一些防御性的设计,就算遇到了bug,也能及时暴露出来。这种做法我也是深深受益。举一个linux kernel的例子:
这里当出现问题的时候就会调用panic,如果没有这样的设计的话,也许在系统运行的时候会直接花屏,而无从debug。
单元测试
如果一下子把代码全部都写好了,再使用,那么可能会有很多bug,因为许多函数流程的组合在自己的测试中可能不会被执行到,但是实际运行的时候可能会遇到,所以做好函数的单元测试可以有效地避免bug。
FOR循环
之前在其他书上也看到过,对于for循环,尽量使用这样的策略:
for (i = 0; i < MAX; i++) {
/* do something */
;
}
这里的i的左边界是0,右边界是MAX-1,对于常见的数组来说正好是这样遍历的,
而且数组的大小是MAX,一目了然。
goto 语句
虽然goto语句是洪水猛兽,可以让代码跑飞,但是合理使用它还是有用的。举一个linux kernel的例子(
do_page_fault函数):
这里有很多标签,可以让程序阅读起来比较方便。但是使用goto的时候不能随心所欲地跳,作为
error handle还是有用的。因为程序员的脑力是有限的,如果不用goto,可能函数会比现在复杂地多。
增加函数的可读性可以大大解放程序员的思维。
指针的禁忌
指针的星号最好不要出现两个以上,我实际在阅读代码过程中也没有发现这样的问题,如果要引用两次的话,应该需要再声明一个变量。
指针不要参与运算。两个指针可能属于两个进程空间,所以减出来的数值是没有意义的。
内存问题
系统默认的字符串处理函数可能会产生内存问题,自己可以封装一个safe版本的,或者
采用现成的代码。(比如这个:http://c.biancheng.net/cpp/html/641.html)
调用内存申请和释放可能会有内存泄露等问题,自己封装一个内存统计模块,在你的程序和操作系统API之间,可以有效地了解当前是否有内存泄露。不过这个应该有现成的解决方案,不用自己写代码。
变量名、函数名
因为在开发过程中不见得会写很多注释,对于变量名字和函数名字要尽量写清楚,避免别人在看你的代码的时候非常疑惑。对于缩写,最好给一个注释,不然想破脑袋也猜不出这个变量意味着什么。
举一个简单的例子:
代码风格
代码要是写的简单,别人看起来也会很开心。代码要是一团乱麻,可能会遭人咒骂,而且出了问题很难debug。乱糟糟的代码会让整个团队的效率大大下降,除非是你自己一直维护这一个模块。
尽量避免代码行数过多的函数,用一些小函数代替,这样虽然降低了效率,但是后期维护的方便性超越了这里牺牲的效率,而且可以用inline来建议编译器来提高效率。
每一个变量不要用做其他用途,比如你计算一个sum,sum又用来存储当做平均值和标准差,这样会让人摸不着头脑而且容易出问题。
加锁解锁的代码要让人看得清楚,这两个动作之间的代码要简洁清晰,如果中间的代码太多,可以考虑
封装成一个函数,这样可以减小bug出现的几率。如果有可能,尽量不要用锁,因为锁是没办法才用的,而且会降低运行效率。
写程序要先写出功能,再优化, 避免到之后自己的代码变的很复杂而难以调试。
函数参数
参数过多的时候,尽量使用结构体来实现(当然是对于c语言来说的)
随便在linux kernel里面截取一下,这里不多说了:
语言高级特性
作者在使用C++的时候说明尽量使用聚合,不用重载。实际上作者的意思可能是扬长避短,使用自己熟悉的东西,对于容易用出问题的一些特性避免使用。而且作者提到,一些嵌入式设备的编译器可能无法支持一些C++的高级特性。
在linux kernel的设计中,结构体内将其他的结构体聚合起来也是常见的,随便举个例子:
重复造轮子
在文中作者有不少代码是和现成的技术类似的,比如作者提到的内存池管理技术,我觉得和linux内核的slab分配器的思想比较像,作者实现的读写锁也有现成的API。不过因为作者强调跨平台的应用,我觉得应该是作者对于跨平台的需求比较强烈,所以不使用现成的库,而是用自己测试好稳定的库用在所有平台上面,这样可以达到自己的需求。但是如果常年在一种平台上面开发,我觉得还是使用现成的、不断更新的高性能库比较适合。总的来说还是以需求做导向。
- 大一C语言学习笔记(10)---编程篇--制作简易计算器,支持加,减,乘,除,取余运算,要求 0 bug
博主自开学初就一直在努力为自己的未来寻找学习方向,学习编程嘛,尽量还是要抱大腿的,所以我就加入了我们学校的智能设备研究所,别的不说,那的学长们看起来是真的很靠谱,学长们的学习氛围也超级浓厚,所以我就打 ...
- wpf Popup Win8.0 bug HorizontalOffset 弹出位置偏移
问题描述参考 wpf 客户端[JDAgent桌面助手]开发详解(四) popup控件的win8.0的bug 当开发完程序后,我们在多操作系统测试时候发现:win8.0 系统中 popup 弹出的位置 ...
- Nuget4.0 bug一粒
这个锅到底是nuget的还是msbuild的我也不是很确定 在使用Nuget4.0打包编译项目时 当执行到nuget pack %%~dpna.csproj -build -Prop Configur ...
- 大一C语言学习笔记(11)---编程篇--写一个程序,可以获取从键盘上输入的的三个数,并能够判断是否可以以这三个数字作为边长来构成一个三角形,如果可以的话,输出此三角形的周长及面积,要求 0 bug;
考核内容: 写一个程序,可以获取从键盘上输入的的三个数,并能够判断是否可以以这三个数字作为边长来构成一个三角形,如果可以的话,输出此三角形的周长及面积: 答案: #include<stdio.h ...
- AutoMySQLBackup 3.0 Bug:"du: WARNING: use --si, not -H"
案例环境: 操作系统版本: Red Hat Enterprise Linux Server release 5.7 64bit 数据库版本 : 5.6.19 MySQL Community Serve ...
- oracle 12c 12.1.0.2.0 BUG 22562145
Wed May 23 17:46:14 2018TT01: Standby redo logfile selected for thread 1 sequence 42251 for destinat ...
- MVC4.0 bug 神奇的是事情 bool 值变成了 onclick ,非常奇怪的
foreach (var item in ViewBag.PhotoGroupList) { // 这里很奇怪 item.IS_DISPLAY 是布尔值 如果直接写 @item.IS_DISPLAY ...
- VC++ 6.0 BUG BUG BUG BUG BUG
http://blog.163.com/amao831@126/blog/#m=0 我经常在的VC++6.0中 定义某个类的对象时 再用.访问或者->访问时不自动弹出他的成员函数或者成员变量 最 ...
- oslo.messaging 1.8.0 bug fix and blueprint
1366597 由于amqp_auto_delete可配置,但是NotifierPublisher使用的是没有在配置中获取而使用的默认的False,即非auo_delete,因而在用户配置了amqp_ ...
随机推荐
- 敏捷开发--scrum
1. 请简述一下什么是敏捷开发(Agile Development),以及什么是持续集成. 敏捷开发是一种以人为核心.迭代.循序渐进的开发方法.在敏捷开发中,软件项目的构建被切分成多个子项目,各个 ...
- Ubuntu/Windows双系统修复引导
Ubuntu/Windows双系统修复引导 首先说明:在Windows存在的前提下安装Ubuntu(或者Ubuntu系列)是不需要修复引导的.因为grub会自动搜索存在硬盘中的系统. 而在Ub ...
- spring filter过滤器
1.简介 Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 ...
- Oracle 列转行函数 Listagg()
这是最基础的用法: LISTAGG(XXX,XXX) WITHIN GROUP( ORDER BY XXX) 例: select listagg(oeid,',') within GROUP (ord ...
- Webview加载本地js、图片的方法
在项目开发中经常会将比较大的js.图片.css等放到app中,而html放服务器,这样在使用时流量较少,加载也比都放服务器上快,其实方法也比较多,网上搜了很久都没结果. 一种是获取服务器返回的html ...
- Linux 多线程编程
概念 原来指向main()的线程叫做主线程(main thread) 使用pthread_create()创建出来的线程,叫做子线程(child thread) 主/子线程只有在创建时才有区别, 创建 ...
- 删除myeclipse下svn用户名和密码
在不同的操作系统下,操作基本类似. 以win7为例 1.进入c:/Users/[你的用户名]/AppData/Roaming/Subversion/auth目录,删除该目录下的所有文件: 2.重启ec ...
- linux centos使用xrdp远程界面登陆
redhat6 安装xrdp 直接使用windows远程桌面连接登陆 下面介绍实现方法: 第一步:下载源码包,并安装一些依赖的软件下载xrdp源码包 wget http://downloads.so ...
- Log4Net根据不同的Logger名称,生成日志文件到不同的地方。
1.定义日志记录类 1: public class Log4NetLogger : ISystemLogger 2: { 3: static log4net.ILog securityLogger = ...
- 常用的JavaScript模式
模式是解决或者避免一些问题的方案. 在JavaScript中,会用到一些常用的编码模式.下面就列出了一些常用的JavaScript编码模式,有的模式是为了解决特定的问题,有的则是帮助我们避免一些Jav ...