diff && path[ZT]
diff
diff是生成源码补丁的必备工具。其命令格式为:
diff [命令行选项] 原始文件 新文件
常用命令行选项如下:
-r 递归处理目录 -u 输出统一格式(unified format)
-N patch里包含新文件 -a patch里可以包含二进制文件
它的输出在stdout上,所以你可能需要把它重定向到一个文件。diff的输出有“传统格式”和“统一格式”之分,现在大都使用统一格式:
传统格式示例:
[hahalee@builder]$ diff a.txt b.txt
1a2
> here we insert a new line
3d3
< why not this third line?
统一格式示例:
[hahalee@builder]$ diff -u a.txt b.txt
— a.txt Thu Apr 6 15:58:34 2000
+++ b.txt Thu Apr 6 15:57:53 2000
@@ -1,3 +1,3 @@
This is line one
+here we insert a new line
and this is line two
-why not this third line?
通过比较可以看出,传统格式的patch文件比较小,除了要删除/插入的行外没有冗余信息。统一格式则保存了上下文(缺省是上下各三行,最少需要两行),这样,patch的时候可以允许行号不精确匹配的情况出现。另外,在patch文件的开头明确地用—和+++标示出原始文件和当前文件,也方便阅读。要选用统一格式,用 u 开关。
通常,我们需要对整个软件包做修改,并生成一个patch文件,下面是典型的操作过程。这里就要用到前面介绍的几个命令行开关了:
tar xzvf software.tar.gz # 展开原始软件包,其目录为software
cp _a software software-orig # 做个修改前的备份
cd software
[修改,测试……]
cd ..
diff _ruNa software-orig software > software-my.patch
现在我们就可以保存software-my.patch做为这次修改的结果,至于原始软件包,可以不必保存。等到下次需要再修改的时候,可以用patch命令把这个补丁打进原始包,再继续工作。比如是在linux kernel 上做的工作,就不必每次保存几十兆修改后的源码了。这是好处之一,好处之二是维护方便,由于unified patch格式有一定的模糊匹配能力,能减少原软件包升级带来的维护工作量(见后)
patch
patch命令跟diff配合使用,把生成的补丁应用到现有代码上。常用命令行选项:
patch [命令行选项] [待patch的文件[patch]]
-pn patch level(n是数字) -b[后缀] 生成备份,缺省是.orig
为了说明什么是patch level,这里看一个patch文件的头标记。
diff -ruNa xc.orig/config/cf/Imake.cf xc.bsd/config/cf/Imake.cf
— xc.orig/config/cf/Imake.cf Fri Jul 30 12:45:47 1999
+++ xc.new/config/cf/Imake.cf Fri Jan 21 13:48:44 2000
这个patch如果直接应用,它会去找xc.orig/config/cf目录下的Imake.cf文件,假如你的源码树的根目录是缺省的xc而不是xc.orig,除了mv xc xc.orig之外,有无简单的方法应用此patch呢?patch level就是为此而设:patch会把目标路径名砍去开头patch level个节(由/分开的部分)。在本例中,可以用下述命令:cd xc; patch _p1 < /pathname/xxx.patch 完成操作。注意,由于没有指定patch文件,patch程序默认从stdin读入,所以用了输入重定向。
如果patch成功,缺省是不建备份文件的(注:FreeBSD下的patch工具缺省是保存备份),如果你需要,可以加上 b 开关。这样把修改前的文件以“原文件名.orig”的名字做备份。如果你喜欢其它后缀名,也可以用“b 后缀”来指定。
如果patch失败,patch会把成功的patch行给patch上,同时(无条件)生成备份文件和一个.rej文件。.rej文件里是没有成功提交的patch行,需要手工patch上去。这种情况在原码升级的时候有可能会发生。
关于二进制文件的说明:binary文件可以原始方式存入patch文件。diff可以生成(加-a选项),patch也可以识别。如果觉得这样的patch文件太难看,解决方法之一是用uuencode处理该binary文件。
rcs
单个文件的版本控制/管理,适合对少量文件进行版本控制,不适合小组进行项目协作开发。优点:使用简便;缺点:功能有限。RCS常用命令有ci/co/rcsdiff。
rcs用一个后缀为“,v”的文件保存一文件的内容和所有修改的历史信息,你可以随时取出任意一个版本,用rcs保存程序就不必为不同版本分别备份。
ci _ check in,保存新版本
co _ check out,取出当前(或任意)版本
cvs是多平台的,开发可以在多种平台比如,可以把linux上的CVS Repository通过samba export出来在Windows平台上做开发。现在很多软件包里包含有*NIX/Windows/MacOS等多平台支持代码,cvs的跨平台特性可提供最好的多平台开发支持。
不过,cvs的操作是直接基于文件系统的,在需要大量远程协作的场合问题很多,远程的NFS mount效率太差,也会有安全问题。新版本的cvs自身内建了Client/Server支持,也可以利用Unix上传统的远程交互手段来通讯。
1,通过rsh(也可用ssh替换)
2,使用cvs自带的C/S用户认证:pserver(缺省端口2401)
3,使用kerberos的gserver、kserver
# 可以把库文件拷贝到 /etc/ld.so.conf 中列举出的任何目录中,并以
root 身份运行 ldconfig;或者
# 运行 export LD_LIBRARY_PATH=’pwd’,它把当前路径加到库搜索路径中去。
1> ldd 工具
ldd 用来显示执行文件需要哪些共享库, 共享库装载管理器在哪里找到了需要的共享库.
2> soname
共享库的一个非常重要的,也是非常难的概念是 soname–简写共享目标名(short for shared object name)。这是一个为共享库(.so)文件而内嵌在控制数据中的名字。如前面提到的,每一个程序都有一个需要使用的库的清单。这个清单的内容是一系列库的 soname,如同 ldd 显示的那样,共享库装载器必须找到这个清单。
soname 的关键功能是它提供了兼容性的标准。当要升级系统中的一个库时,并且新库的 soname 和老的库的 soname 一样,用旧库连接生成的程序,使用新的库依然能正常运行。这个特性使得在 Linux 下,升级使用共享库的程序和定位错误变得十分容易。
在 Linux 中,应用程序通过使用 soname,来指定所希望库的版本。库作者也可以通过保留或者改变 soname 来声明,哪些版本是相互兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。
查看/usr/local/lib 目录,分析 MiniGUI 的共享库文件之间的关系
3> 共享库装载器
当程序被调用的时候,Linux 共享库装载器(也被称为动态连接器)也自动被调用。它的作用是保证程序所需要的所有适当版本的库都被调入内存。共享库装载器名字是 ld.so 或者是 ld-linux.so,这取决于 Linux libc 的版本,它必须使用一点外部交互,才能完成自己的工作。然而它接受在环境变量和配置文件中的配置信息。
文件 /etc/ld.so.conf 定义了标准系统库的路径。共享库装载器把它作为搜索路径。为了改变这个设置,必须以 root 身份运行 ldconfig 工具。这将更新 /etc/ls.so.cache 文件,这个文件其实是装载器内部使用的文件之一。
对软件的评价:代码的稳定性、友好性、代码的易读性、统一的风格、技巧。
1。尽量少的使用全局变量
2。局部变量一定要初始化,特别是指针变量
3。成员函数功能单一,不要过分追求技巧,函数体不要过长。
4。最好有头文件
5。关于变量名的长短问题
6。设计函数时考虑到通用性
7。申请内存时,一定先要释放。注意 if 问题。
8。对浮点数比较大小时注意不要使用 ==
9。最好不要用 goto 语句
10。所有成员函数要单出口单入口
11。函数中,要先检验参数的合法性
12。最好所有的函数都有返回值,表明错误的原因。
13。注释问题
14。类型转化一律用显示转换。
15。定义宏说,参数使用括号,结果也应该括起来
#define SUB(a,b) ((a)-(b))
3*SUB(3,4-5);
16。变量长度一定要用 sizeof 来求
17。malloc 后千万别忘 free 及使指针等于 NULL。
18。字符串拷贝时尽量少使用 sprintf,而使用 memcpy,最后别忘加上’