条件去重现问题。如果你确信可以使用记录的那些条件去重现问题,那么我们就可以着手去隔离问题。怎么隔离呢?我们可以用#ifdef把一些可能和问题无关的代码关闭,把系统最小化到仍能够重现问题的地步。如果还是无法定位问题所在,那么有必要打开“工具箱”了。可以试着用ICE或数据监视器去查看某个可疑变量的变化;可以使用跟踪工具获得函数调用的情况包括参数的传递;检查内存是否崩溃以及堆栈溢出的问题。
6.以退为进
猎人为了不使自己在森林里迷路,他常常会在树木上流下一些标记,以备自己将来有一天迷路时可以根据这些标记找到出路。对过去代码的修改进行跟踪记录对将来出现问题之后的调试很有帮助。假如有一天,你最近一次修改的程序跑了很久之后忽然死掉了,那么你这时的第一反映就是我到底改动了些什么呢,因为上次修改之前是好的。那么如何检测这次相对于上次的修改呢?没错,代码控制系统SCS或称版本控制系统VCS(Concurrent Version Control,CVS是VCS的演化版本)。将上个版本check in下来后和当前测试版本比较。比较的工具可以是SCS/VCS/CVS自带的diff工具或其它功能更强的比较工具,比如BeyondCompare和ExamDiff。通过比较,记录所有改动的代码,分析所有可能导致问题的可疑代码。
7.确定测试的完整性
你怎么知道你的测试有多全面呢?覆盖测试(coverage testing)可以回答这个问题。覆盖测试工具可以告诉你CPU到底执行了那些代码。好的覆盖工具通常可以告诉你大概20%到40%代码没有问题,而其余的可能存在bug。覆盖工具有不同的测试级别,用户可以根据自己的需要选择某个级别。即使你很确信你的单元测试已经很全面并且没有dead code,覆盖工具还是可以为你指出一些潜在的问题,看下面的代码: if (i >= 0 && (almostAlwaysZero == 0 || (last = i)))
如果almostAlwaysZero为非0,那么last=i赋值语句就被跳过,这可能不是你所期望的。这种问题通过覆盖工具的条件测试功能可以轻松的被发现。
总之,覆盖测试对于提高代码质量很有帮助。
8.提高代码质量意味着节省时间
有研究表明软件开发的时间超过80%被用在下面几个方面: .调试自己的代码(单元测试)
.调试自己和其他相关的代码(模块间测试) .调试整个系统(系统测试)
更糟糕的是你可能需要花费10-200倍的时间来找一个bug,而这个bug在开始的时候可能很容易就能找到。一个小bug可能让你付出巨大的代价,即使这个bug对整个系统的性能没有太大的影响,但很可能会影响让那些你可以看得到的部分。所以我们必须要养成良好的编码和测试手段以求更高的代码质量,以便缩短调试的代码。
9.发现它,分析它,解决它
这世界没有万能的膏药。profile再强大也有力不从心的时候;内存监视器再好,也有无法发现的时候;覆盖工具再好用,也有不能覆盖的地方。一些隐藏很深的问题即使用尽所有工具也有可能无法查到其根源,这时我们能做的就是通过这些问题所表现出来的外在现象或一些数据输出来发现其中的规律或异常。一旦发现任何异常,一定要深入地理解并回溯其根源,直到解决为止。
10.利用初学者的思维 有人这样说过:“有些事情在初学者的脑子里可能有各种各样的情况,可在专家的头脑里可能就很单
一”。有时候,有些简单的问题会被想的很复杂,有些简单的系统别设计的很复杂,就是由于你的“专家思维”。当你被问题难住时,关掉电脑,出去走走,把你的问题和你的朋友甚至你的小狗说说,或许他们可以给你意想不到的启发。
总结:嵌入式调试也是一门艺术。就想其它的艺术一样,如果你想取得成功,你必须具备智慧、经验并懂得使用工具。只要我们能够很好地领悟Oracle这十条秘诀,我相信我们在嵌入式测试方面就能够取得成功。
武功秘籍排行榜:
1. The C programming language《C程序设计语言》 2. Pointers on C《C和指针》
3. C traps and pitfalls《C陷阱与缺陷》 4. Expert C Lanuage《专家C编程》 5. Writing Clean Code
-----Microsoft Techiniques for Developing Bug-free C Programs 《编程精粹--Microsoft 编写优质无错C程序秘诀》
6. Programming Embedded Systems in C and C++《嵌入式系统编程》 7.《C语言嵌入式系统编程修炼》 8.《高质量C++/C编程指南》林锐 9 《C语言解惑》
二 嵌入式linux的NFS开发环境的建立
在两台linux pc之间共享资源通常我们采用NFS技术;而在linux和windows之间共享资源,通常我们采用samba技术[]。NFS可以让你的PC通过网络将远端的NFS 服务器共享出来的文件mount到自己的系统中,在客户端看来使用NFS的远端文件就象是在使用本地文件一样。 使用NFS可以使应用程序的开发变得十分方便, 客户端不需要大容量的存储器,更不需要进行映像文件的烧录和下载,只要mount到服务器端的特定目录下,然后运行该目录下的程序即可观察到结果。建立NFS开发环境的工作分为两个方面,配置NFS服务器和配置客户端。
在开始前需要特别说明的一点:这里的描述是针对自己使用情况的简单记录,可能你在使用过程中会存在一些问题,关于更多关于nfs和使用过程中的问题,建议参考nfs.sourceforge.net这个网站上的资料和FAQ。
在应用程序开发环节,NFS方式比ftp方式的执行效率要高,因为它不需要将linux server端的程序下载到嵌入式目标系统就可以调试。下面先将NFS建立的详细过程写一下,然后举一个简单的应用程序开发实例来比较ftp方式和nfs方式的不同。
1 建立NFS开发环境
嵌入式linux的NFS开发环境包含着两个方面:一是linux server端的NFS Server支持;二是target board的NFS Client支持。
1.1 linux server端
1.1.1 以root的身份登录,编译共享目录的配置文件exports,指定共享目录及其权限。 #vi /etc/exports 在该文件中添加:
/home/lqm(共享目录) 192.168.1.*(rw,sync,no_root_squash)
添加的内容表示允许IP范围在192.168.1.*的计算机以读写的权限来访问共享目录/home/lqm。 【注:】
参数说明如下:
rw---读/写权限。如果设定只读权限,则设为ro。但是一般情况下,为了方便交互,要设置为rw。 sync--数据同步写入内存和硬盘。
no_root_squash--此参数用来要求服务器允许远程系统以它自己的root特权存取该目录。就是说,如果用户是root,那么他就对这个共享目录有root的权限。很明显,该参数授予了target board很大的权利。安全性是首先要考虑的,可以采取一定的保护机制,在下面会讲一下保护机制。如果使用默认的root_squash,target board自己的根文件系统可能有很多无法写入,所以运行会受到极大的限制。在安全性有所保障的前提下,推荐使用no_root_squash参数。
ro: 只读的权限,系统默认选项;如果不明确指定rw, 系统默然采用这种方式;
root_squash: 在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个身份;如果不明确指定no_root_squas, 系统默然采用这种方式;
all_squash: 不论登入 NFS 的使用者身份为何,他的身份都会被压缩成为匿名使用者,通常也就是 nobody 啦!
anonuid: 前面关于 *_squash 提到的匿名使用者的 UID 设定值,通常为 nobody,但是你可以自行设定这个 UID 的值!当然,这个 UID 必需要存在于你的 /etc/passwd 当中!
anongid: 同 anonuid ,但是变成 group ID 就是了; async: 资料会先暂存于内存当中,而非直接写入硬盘;
1.1.2 起用保护机制
可以通过设定/etc/hosts.deny和/etc/hosts.allow文件来限制网络服务的存取权限。 ***/etc/hosts.deny*** portmap:ALL lockd:ALL mountd:ALL rquotad:ALL statd:ALL
***/etc/hosts.allow*** portmap:192.168.1.100 lockd:192.168.1.100 mountd:192.168.1.100 rquotad:192.168.1.100 statd:192.168.1.100
同时使用这两个文件就会使得只有ip为192.168.1.100的机器使用NFS服务。你的target board的ip地址设定为192.168.1.100,这样就可以了。
1.1.3 启动
首先确认在/etc/rc.d/init.d 目录中是否有 portmap, nfs这两个可执行文件,如果没有你需要安装这两个程序,具体方法参见其它资料,不在这里描绘。
首先要启动portmapper(端口映射)服务,这是NFS本身需要的。 #/etc/init.d/portmap start
然后启动NFS Server。此时NFS会激活守护进程,然后开始监听客户端的请求。 #/etc/init.d/nfs start
NFS Server启动后,还要检查一下linux server的iptables等,确定没有屏蔽NFS使用的端口和允许通信的主机。
可以首先在linux server上面进行NFS的回环测设。修改/etc/hosts.allow,把ip改为linux server的ip地址,然后在linux server上执行命令:
#mount -t nfs
如果NFS Server正常工作,应该在/mnt下面看到共享目录/home/lqm的内容。 注:
在启动了NFS之后又修改了/etc/exports,是不是还要重新启动nfs呢?这个时候我们就可以用exportfs命令来使改动立刻生效,该命令格式如下: exportfs [-aruv]
-a :全部mount或者unmount /etc/exports中的内容 -r :重新mount /etc/exports中分享出来的目录 -u :umount 目录
-v :在 export 的时候,将详细的信息输出到屏幕上。
具体例子: exportfs –rv这个命令行要求全部重新输出一次。 在每次修改了/etc/exports文件后都要运行一次该命令。
找不到这个命令,通常在/usr/sbin/ 目录中,如果没有在,利用locate或者其他搜索命令找找看(具体可以参见《鸟哥的linux私房菜 – 基础篇》, 为了以后使用方便还是将
export PATH=/usr/sbin${PATH} 这一行加入到自己目录的.bashrc文件中,然后重新“su 用户名”登陆一次 )。
1.2 target board端的client
1.2.1 嵌入式linux内核应该支持NFS客户端。 配置内核
进入File Systems --->选项选中:
NFS file system support
Provide NFSv3 client support
配置用户选项(这个按照罗威的说法应该是在编译busybox时设定,具体选项可能和实现有关) 进入Network Applications ---> 选项选中: portmap进入BusyBox --->选项选中: mount
mount:support NFS mounts完成以上配置后,即可编译产生映像文件。
1.2.2 在target board的linux shell下,执行下列命令来进行NFS共享目录的挂载。 #mkdir /mnt/nfs
#mount -o nolock -t nfs
由于很多嵌入式设备的根文件系统中不带portmap,所以一般都使用-o nolock参数,即不使用NFS文件锁,这样就可以避免使用portmap。如果顺利,在/mnt/nfs下,就可以看到linux server的共享文件夹下的内容了,而且两个文件夹内的修改是同步的。
这里需要特别说明:我在RT-VS4104D上实际上是没有调通NFS,我估计是目标板操作系统本身的问题,具体原因不明,因为对操作系统制作不是很清楚,操作系统人员又忙,所以不得已放弃。但是我在自己电脑山NFS输出的目录,通过上面的操作步骤在172.16.51.130和172.16.51.188上都很容易mount成功,所以应该不是服务器端设置的问题。大家有机会还是最好把这个东西给走通,因为在调试阶段,采用这种方式的确非常方便。
1.3 其他说明 1.3.1设置的实例:
a). /tmp *(rw,no_root_squash) //*号表示所有的IP都可以访问 b). /tmp *(rw)
/home/public 192.168.0.*(rw) *(ro) //下面两行作用一样 /home/public 192.168.0.0/24(rw) *(ro)
c). /home/test 192.168.0.100(rw) //只对某部机器设置权限
d). /home/linux *.linux.org(rw,all_squash,anonuid=40,anongid=40) //当*.linux.org登陆此NFS主机,并且在/home/linux下面写入档案时,该档案的所有人与所有组,就会变成/etc/passwd里面对应的UID为40的那个身份的使用者了.
1.3.2.权限问题
假设/etc/exports里面的内容为 #vi /etc/exports
/tmp *(rw,no_root_squash)
/home/public 192.168.0.*(rw) *(ro) /home/test 192.168.0.100(rw)
/home/linux *.linux.org(rw,all_squash,anonuid=40,anongid=40)
假设我们在192.168.0.100这个client端登陆此NFS主机(192.168.0.2),那么
情况一:在192.168.0.100的帐号为test这个身份,同时,NFS主机上也有test这个帐号
a).由于NFS主机的/tmp权限为-rwxrwxrwt,所以我(test在192.168.0.100上)在/tmp下面具有存取的权限,并且写入档案的所有人为test.
b).在/home/public中,由于我有读写的权限,如果NFS主机在/home/public这个目录的权限对于test开放写入的话,那么就可以读写,并且写入档案的所有人是test。如果NFS主机的/home/public对于test这个使用者并没有开放写入权限时,那就无法写入,虽然/etc/exports里面是rw,也不起作用.