|
|
10月23日
引用
使用J-Link GDB Server + Eclipse CDT进行ARM程序调试
之前我预告过这篇文章,这段时间以来,我工作一直比较忙,而且在装修我的小破窝,生活的压力让我把业余的时间都贡献给奥山战场了,我的小牧师,杀人很慢,被杀倒是挺速度的,呵呵;想当年上学的时候,我也是副本RL,逃课带MC;现在工作了就只能沦为战场混荣誉的了……平时没有太多时间,职业不开UT跟混收麦子;周末开UT打各种麦子和各种国家队。国际惯例,二区风行者暗夜暗牧,多多和雯雯(Sigh,这个名字我GF取的,她叫雯雯和多多)。欢迎风行者的周末一同UT,我排队相当狠的,嘿嘿。
在开始前另外提一件事情,我之前说想找个便宜的PowerPC调试解决方案,现在其实我也算找到了——还是Macraigor的OCDRemote,用起来和OpenOCD差不多,具体用法和支持的器件请看Macraigor的官网 http://www.macraigor.com/。不过,“0f 84 f0 fc ff ff”->“0f 85 f0 fc ff ff”;我所提供的信息来自互联网,仅可以用于个人学习和研究,对于产生的后果,我不负任何责任;如果你喜欢这个软件,请联系Macraigor的销售,购买他们的调试硬件。但OCDRemote + Wiggler目前并不支持e300、e500内核,我要用的MPC8313还是调试不了……
首先明确一下,为什么我要抛开IAR、ADS、Keil这样的IDE选择GNU阵营的东西——这并不是因为GNU是自由软件,而是因为在一个CPU刚上手的时候,往往还是需要别人的代码来参考的,u-boot里面的样例很多,但是移植到IDE环境下开发却很困难。
ZylinCDT用起来很方便,只需要在Debug Configuration里面配置Commands选项卡,填入Initiate Commands和Run Commands;其他和Eclipse自己的Debug Configuration差不多。事实上,ZylinCDT就是用这些填入的内容取代了正常的GDB初始化命令,给出我的Initiate Commands给大家参考(Run Commands为空):
target remote 192.168.5.133:2331 # Disable watchdog monitor memU32 0xfffffd44 = 0x00008000
# PD6 - EBI0_NCS4, PD11 - EBI0_NCS2, PD15 - EBIO_CS3/NANDCS, PD16~PD31 - EBI0_D16~EBI0_D31 monitor memU32 0xfffff844 = 0xffff8840 monitor memU32 0xfffff864 = 0xffff8840 monitor memU32 0xfffff870 = 0xffff8840 monitor memU32 0xfffff804 = 0xffff8840
# Configure the EBI0 Slave Slot Cycle to 64 monitor memU32 0xffffec50 = 0x00000040
# Initialize the matrix, VDDIOMSEL = 1 -> Memories are 3.3V powered monitor memU32 0xffffed20 = 0x0001000a
# Configure SDRAM Configuration Register monitor memU32 0xffffe208 = 0x85227259 monitor sleep 10 # Perform Command - Precharge All monitor memU32 0xffffe200 = 0x00000002 monitor memU32 0x20000000 = 0x00000000 monitor sleep 20 # Perform Command - Refresh for 8 times monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x20000004 = 0x00000001 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x20000008 = 0x00000002 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x2000000c = 0x00000003 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x20000010 = 0x00000004 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x20000014 = 0x00000005 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x20000018 = 0x00000006 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x2000001c = 0x00000007 monitor memU32 0xffffe200 = 0x00000004 monitor memU32 0x20000020 = 0x00000008 # Perform Command - Load Mode Register monitor memU32 0xffffe200 = 0x00000003 monitor memU32 0x20000024 = 0xcafedede # Set Refresh Timer monitor memU32 0xffffe204 = 0x000002bc # Perform Command - Set Normal mode monitor memU32 0xffffe200 = 0x00000000 monitor memU32 0x20000000 = 0x00000000 file /home/lxz/share/u-boot-1.3.4/u-boot load
我调试的目标CPU是AT91SAM9263,通过以上的初始化命令,可以实现PLL、SDRAM的初始化,并将u-boot带码加载到内存中。J-Link的monitor命令可以看J-Link安装之后的文档J-Link GDB server user guide的3.4节Supported remote commands。
今天说的这些事情没有什么难度,用Eclipse + J-Link调试的关键就是要用ZylinCDT这样的插件,关于ZylinCDT的信息可以看它的官网 http://www.zylin.com/,解释的比我说的详细多了。Eclipse的教学文档网络上也多得很,Eclipse本身界面就很友好,不用什么教学,自己摸索一会儿也就会了。我就总结下:Eclipse可以自动创建Makefile,虽然使用的是GCC工具链,却有像ADS、Codewarrior这样IDE环境的便利;对于U-boot、Linux内核这样的有现成Makefile的程序,Eclipse能将它们导入,一样能调试。但是,和Source Insight强大的自动完成功能相比,Eclipse逊色太多了。我的开发解决方案是:Linux虚拟机下的SMB共享代码 + Windows下的Source Insight编辑 + Linux虚拟机下的arm-linux-gcc编译 + Windows下的J-Link GDB Server + Linux虚拟机下的Eclipse CDT & ZylinCDT;呵呵,好绕啊;但如果代码不复杂,就直接用Eclipse编辑也挺好。 http://ircspace.bokee.com/2673731.html
http://libai.math.ncu.edu.tw/bcc16/7/latex/
宏和内联模板函数的本质区别在于? 楼主ocean69()2003-05-25 16:16:32 在 C/C++ / 工具平台和程序库 提问
我想内联模板函数和宏的概念上有本质的不同。 从我自己的经验上说,我觉得宏和内联函数的最大不同是,宏把她所定义的右方的码原原本本的搬到程序中去了,比如我定义: #define MYFONT(h, w, pDC) CFont MYFONT; \ MYFONT.CreateFont(h,w,0,0,400,FALSE,FALSE,0,ANSI_CHARSET, \ OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY, \ DEFAULT_PITCH|FF_SWISS,"Arial"); \ CFont* pOldFont = (CFont*)pDC->SelectObject(&MYFONT) #define DEL_MYFONT(pDC) pDC->SelectObject(pOldFont); \ MYFONT.DeleteObject() 之后,在程序中用到她们的时候可以这样: MYFONT(h, w, pDC); ... CString ss; ss.Format("write the string"); pDC->TextOut(x, y, ss); ... DEL_MYFONT(pDC); 等于把这段字符串原原本本的写在用到的位置,而内联模板函数是地地道道的函数,没有这种功能,所以从这点上说宏无法贝内联函数所代替。 不知道诸位师兄的看法如何,请布里赐教。 问题点数:20、回复次数:7Top 1 楼lifanxi(Byron)回复于 2003-05-25 16:25:33 得分 4
它俩有一个最大的不同是处理的时机不同,宏是预编译指令,是在编译前的预编译期进行的替换,而内联函数或const常量是在编译期进行的。 内联函数在真正被内联时,表现出来的特性与宏是一致的。 在C++中,建议使用内联函数和const常量来替换一些可能的宏定义,因为inline函数和const常量是类型安全的,用起来也更方便的。但对于一些不可能用inline和const的地方,宏还是具有其实用性的。Top 2 楼jxtian1975(田)回复于 2003-05-25 16:30:20 得分 3
它俩有一个最大的不同是处理的时机不同,宏是预编译指令,是在编译前的预编译期进行的替换,而内联函数或const常量是在编译期进行的。 另外宏不进行安全检查,存在不安全因素Top 3 楼stukov2002(卡拉是头猪)回复于 2003-05-25 17:02:55 得分 2
宏不会进行参数的类型检查,用起来不安全.Top 4 楼ocean69()回复于 2003-05-25 22:21:33 得分 0
如果宏的特性和内联函数是一样的话象我上面列子中给出来的 #define MYFONT(h, w, pDC) CFont MYFONT; \ MYFONT.CreateFont(h,w,0,0,400,FALSE,FALSE,0,ANSI_CHARSET, \ OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY, \ DEFAULT_PITCH|FF_SWISS,"Arial"); \ CFont* pOldFont = (CFont*)pDC->SelectObject(&MYFONT) #define DEL_MYFONT(pDC) pDC->SelectObject(pOldFont); \ MYFONT.DeleteObject() 用内联函数应该如何表示?Top 5 楼lifanxi(Byron)回复于 2003-05-26 19:14:24 得分 3
您的例子确实不便用内联函数来替代,如果硬要做会比较别扭。 我们通常所说的是指在内联函数被直正内联的时候,表现出来的特性是与宏相同的,并不表示完全可以用内联函数来替代宏。他们连被处理的时机都不同,是完全不同的两个东西,又怎么可能完全互相替代呢? 我们通常只是建议在C++中尽可能的用内联函数来替代宏,原因在于内联函数是类型安全的,而且不容易出现类似于宏所出现的副作用。Top 6 楼plainsong(短歌)()回复于 2003-05-26 19:45:10 得分 5
这个例子确实很难用内联模板函数完全相同地替代,因为很难用模板函数的方式导致这样一个 编译时错误: MYFONT(10, 5, pDC); DEL_MYFONT(pDC); MYFONT(20,20, pDC);//pOldFont重复定义! DEL_MYFONT(pDC); 在编写可重用代码的时候,永远不要选择宏。事实上,同一个问题的解决可以有不同的方法,你很难找到一个问题其解决必须使用宏来解决的,只可能有某一种解决方法是必须使用宏的,并且你使用的方法恰恰是一种很不合理的方法(隐藏了一个变量定义,并且使用了一个与CDC无关的变量保存与CDC有关的数据(以前的字体))。 我从不说“函数可以代替宏”之类的说法,并不是因为我认为宏在作为“可重用代码”的表现形式上有什么地位,而是我认为它们是用于不同目的的。真正不应该用(内联)模板函数代替的宏是类似于assert这样的宏,它也体现了宏最大的用处——条件预编译。
10月22日 C语言头文件的使用
——by janders http://blog.csdn.net/janders/archive/2006/02/27/611081.aspx
C语言中的.h文
件和我认识由来已久,其使用方法虽不十分复杂,但我却是经过了几个月的“不懂”时期,几年的“一知半解”时期才逐渐认识清楚他的本来面目。揪其原因,我的
驽钝和好学而不求甚解固然是原因之一,但另外还有其他原因。原因一:对于较小的项目,其作用不易被充分开发,换句话说就是即使不知道他的详细使用方法,项
目照样进行,程序在计算机上照样跑。 原因二:现在的各种C语言书籍都是只对C语言的语法进行详细的不能再详细的说明,但对于整个程序的文件组织构架却只字不提,找了好几本比较著名的C语言著作,却没有一个把.h文件的用法写的比较透彻的。下面我就斗胆提笔,来按照我对.h的认识思路,向大家介绍一下。
让我们的思绪乘着时间机器回到大学一年级。C原来老师正在讲台上讲着我们的第一个C语言程序: Hello world!
文件名 First.c
main()
{
printf(“Hello world!”);
}
例程-1
看看上面的程序,没有.h文件。是的,就是没有,世界上的万物都是经历从没有到有的过程的,我们对.h的认识,我想也需要从这个步骤开始。这时确实不需要.h文件,因为这个程序太简单了,根本就不需要。那么如何才能需要呢?让我们把这个程序变得稍微复杂些,请看下面这个,
文件名 First.c
printStr()
{
printf(“Hello world!”);
}
main()
{
printStr()
}
例程-2
还是没有, 那就让我们把这个程序再稍微改动一下.
文件名 First.c
main()
{
printStr()
}
printStr()
{
printf(“Hello world!”);
}
例程-3
等等,不就是改变了个顺序嘛, 但结果确是十分不同的. 让我们编译一下例程-2
和例程-3,你会发现例程-3是编译不过的.这时需要我们来认识一下另一个C语言中的概念:作用域.
我们在这里只讲述与.h文件相关的顶层作用域, 顶层作用域就是从声明点延伸到源程序文本结束, 就printStr()这个函数来说,他没有单独的声明,只有定义,那么就从他定义的行开始,到first.c文件结束, 也就是说,在在例程-2的main()函数的引用点上,已经是他的作用域. 例程-3的main()函数的引用点上,还不是他的作用域,所以会编译出错. 这种情况怎么办呢? 有两种方法 ,一个就是让我们回到例程-2, 顺序对我们来说没什么, 谁先谁后不一样呢,只要能编译通过,程序能运行, 就让main()文件总是放到最后吧. 那就让我们来看另一个例程,让我们看看这个方法是不是在任何时候都会起作用.
文件名 First.c
play2()
{
……………….
play1()
………………..
}
play1()
{
……………………..
play2()
……………………
);
}
main()
{
play1()
}
例程-4
也许大部分都会看出来了,这就是经常用到的一种算法, 函数嵌套, 那么让我们看看, play1和play2这两个函数哪个放到前面呢?
这时就需要我们来使用第二种方法,使用声明.
文件名 First.c
play1();
play2();
play2()
{
……………….
play1()
………………..
}
play1()
{
……………………..
play2()
……………………
);
}
main()
{
play1()
}
例程-4
经历了我的半天的唠叨, 加上四个例程的说明,我们终于开始了用量变引起的质变, 这篇文章的主题.h文件快要出现了。
一个大型的软件项目,可能有几千个,上万个play, 而不只是play1,play2这么简单, 这样就可能有N个类似 play1(); play2(); 这样的声明, 这个时候就需要我们想办法把这样的play1(); play2(); 也另行管理, 而不是把他放在.c文件中, 于是.h文件出现了.
文件名 First.h
play1();
play2();
文件名 First.C
#include “first.h”
play2()
{
……………….
play1()
………………..
}
play1()
{
……………………..
play2()
……………………
);
}
main()
{
play1()
}
例程-4
各位有可能会说,这位janders大虾也太罗嗦了,上面这些我也知道, 你还讲了这么半天, 请原谅, 如果说上面的内容80%的人都知道的话,那么我保证,下面的内容,80%的人都不完全知道. 而且这也是我讲述一件事的一贯作风,我总是想把一个东西说明白,让那些刚刚接触C的人也一样明白.
上面是.h文件的最基本的功能, 那么.h文件还有什么别的功能呢? 让我来描述一下我手头的一个项目吧.
这个项目已经做了有10年以上了,具体多少年我们部门的人谁都说不太准确,况且时间并不是最主要的,不再详查了。 是一个通讯设备的前台软件, 源文件大小共 51.6M, 大小共1601个文件, 编译后大约10M, 其庞大可想而知, 在这里充斥着错综复杂的调用关系,如在second.c中还有一个函数需要调用first.c文件中的play1函数, 如何实现呢?
Sencond.h 文件
play1();
sencond.c文件
***()
{
…………….
Play();
……………….
}
例程-5
在sencond.h文件内声明play1函数,怎么能调用到first.c文件中的哪个play1函数中呢? 是不是搞错了,没有搞错, 这里涉及到c语言的另一个特性:存储类说明符.
C语言的存储类说明符有以下几个, 我来列表说明一下
说明符 用法
Auto 只在块内变量声明中被允许, 表示变量具有本地生存期.
Extern 出现在顶层或块的外部变量函数与变量声明中,表示声明的对象
具有静态生存期, 连接程序知道其名字.
Static 可以放在函数与变量声明中. 在函数定义时, 其只用于指定函数
名,而不将函数导出到连接程序. 在函数声明中,表示其后面会有
定义声明的函数, 存储类为static. 在数据声明中, 总是表示定义
的声明不导出到连接程序.
无疑, 在例程-5中的second.h和first.h中,需要我们用extern标志符来修饰play1函数的声明,这样,play1()函数就可以被导出到连接程序, 也就是实现了无论在first.c文件中调用,还是在second.c文件中调用,连接程序都会很聪明的按照我们的意愿,把他连接到first.c文件中的play1函数的定义上去, 而不必我们在second.c文件中也要再写一个一样的play1函数.
但随之有一个小问题, 在例程-5中,我们并没有用extern标志符来修饰play1啊, 这里涉及到另一个问题, C语言中有默认的存储类标志符. C99中规定, 所有顶层的默认存储类标志符都是extern . 原来如此啊, 哈哈. 回想一下例程-4, 也是好险, 我们在无知的情况下, 竟然也误打误撞,用到了extern修饰符, 否则在first.h中声明的play1函数如果不被连接程序导出,那么我们在在play2()中调用他时, 是找不到其实际定义位置的 .
那么我们如何来区分哪个头文件中的声明在其对应的.c文件中有定义,而哪个又没有呢? 这也许不是必须的,因为无论在哪个文件中定义,聪明的连接程序都会义无返顾的帮我们找到,并导出到连接程序, 但我觉得他确实必要的. 因为我们需要知道这个函数的具体内容是什么,有什么功能, 有了新需求后我也许要修改他, 我需要在短时间内能找到这个函数的定义, 那么我来介绍一下在C语言中一个人为的规范:
在.h文件中声明的函数,如果在其对应的.c文件中有定义,那么我们在声明这个函数时,不使用extern修饰符, 如果反之,则必须显示使用extern修饰符.
这样,在C语言的.h文件中,我们会看到两种类型的函数声明. 带extern的,还不带extern的, 简单明了,一个是引用外部函数,一个是自己生命并定义的函数.
最终如下:
Sencond.h 文件
Extern play1();
上面洋洋洒洒写了那么多都是针对函数的,而实际上.h文件却不是为函数所御用的. 打开我们项目的一个.h文件我们发现除了函数外,还有其他的东西, 那就是全局变量.
在大型项目中,对全局变量的使用不可避免, 比如,在first.c中需要使用一个全局变量G_test, 那么我们可以在first.h中,定义 TPYE G_test. 与对函数的使用类似, 在second.c中我们的开发人员发现他也需要使用这个全局变量, 而且要与first.c中一样的那个, 如何处理? 对,我们可以仿照函数中的处理方法, 在second.h中再次声明TPYE G_test, 根据extern的用法,以及c语言中默认的存储类型, 在两个头文件中声明的TPYE G_test,其实其存储类型都是extern, 也就是说不必我们操心, 连接程序会帮助我们处理一切. 但我们又如何区分全局变量哪个是定义声明,哪个是引用声明呢?这个比函数要复杂一些, 一般在C语言中有如下几种模型来区分:
1、 初始化语句模型
顶层声明中,存在初始化语句是,表示这个声明是定义声明,其他声明是引用声明。C语言的所有文件之中,只能有一个定义声明。
按照这个模型,我们可以在first.h中定义如下TPYE G_test=1;那么就确定在first中的是定义声明,在其他的所有声明都是引用声明。
2、 省略存储类型说明
在这个模型中,所有引用声明要显示的包括存储类extern, 而每个外部变量的唯一定义声明中省略存储类说明符。
这个与我们对函数的处理方法类似,不再举例说明。
这里还有一个需要说明,本来与本文并不十分相关,但前一段有个朋友遇到此问题,相信很多人都会遇到, 那就是数组全局变量。
他遇到的问题如下:
在声明定义时,定义数组如下:
int G_glob[100];
在另一个文件中引用声明如下:
int * G_glob;
在vc中,是可以编译通过的, 这种情况大家都比较模糊并且需要注意,数组与指针类似,但并不等于说对数组的声明起变量就是指针。 上面所说的的程序在运行时发现了问题,在引用声明的那个文件中,使用这个指针时总是提示内存访问错误,原来我们的连接程序并不把指针与数组等同,连接时,也不把他们当做同一个定义,而是认为是不相关的两个定义,当然会出现错误。正确的使用方法是在引用声明中声明如下:
int G_glob[10];
并且最好再加上一个extern,更加明了。
extern int G_glob[10];
另外需要说明的是,在引用声明中由于不需要涉及到内存分配,可以简化如下,这样在需要对全局变量的长度进行修改时,不用把所有的引用声明也全部修改了。
extern int G_glob[];
C语言是现今为止在底层核心编程中,使用最广泛的语言,以前是,以后也不会有太大改变,虽然现在java,.net等语言和工具对c有了一定冲击,但我们看到在计算机最为核心的地方,其他语言是无论如何也代替不了的,而这个领域也正是我们对计算机痴迷的程序员所向往的。 10月19日 http://www.gzsums.edu.cn/webclass/html/html_design.html
10月17日 如果你真是初学的话,这里有些不需要现在就弄明白的。
1)
#if defined XXX_XXX
#endif
是条件编译,是根据你是否定义了XXX_XXX这个宏,而使用不同的代码。
一般.h文件里最外层的
#if !defined XXX_XXX
#define XXX_XXX
#endif
是为了防止这个.h头文件被重复include。
2)
#error XXXX
是用来产生编译时错误信息XXXX的,一般用在预处理过程中;
例子:
#if !defined(__cplusplus)
#error C++ compiler required.
#endif
3)
#define new
还真不太清楚
4)
__cdecl和__stdcall是两种C++函数调用规则的系统约定。
__cdecl的:
Argument-passing order
Right to left
Stack-maintenance responsibility
Calling function pops the arguments from the stack
Name-decoration convention
Underscore character (_) is prefixed to names, except when exporting __cdecl functions that use C linkage.
Case-translation convention
No case translation
__stdcall的:
Argument-passing order
Right to left.
Argument-passing convention
By value, unless a pointer or reference type is passed.
Stack-maintenance responsibility
Called function pops its own arguments from the stack.
Name-decoration convention
An underscore (_) is prefixed to the name. The name is followed by
the at sign (@) followed by the number of bytes (in decimal) in the
argument list. Therefore, the function declared as int func( int a,
double b ) is decorated as follows: _func@12
Case-translation convention
None
5)
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )
这个是用来指定类、结构的内存对齐的;
这个说起来有点多,你上网查内存对齐能查到;
6)
__declspec( dllimport ) declarator
__declspec( dllexport ) declarator
这两个是与Dll有关的,声明可以从dll文件中输出和输入的函数、类或数据;
7)
#pragma once
是规定当编译时这个文件只能被include一次;
8)
disable message
才疏学浅,没见过。。。。
9)
__int64
是64位的整数类型,是为了防止32位的int不够用时用的;
10)
#pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
是用来定义函数被放置在obj文件的哪个段里。
---------------------------------------------------
#if defined MACRO_NAME
与
#ifdef MACRO_NAME 还是稍微有点区别的。
譬如我们公司就鼓励写第一种,因为它可以这么写:
#if defined(MACRO_NAME) && defined(MACRO_NAME)
这个#error是可以让用户在编译时手动产生一个编译错误,更准确的说是在预处理的时候。
#if !defined(__cplusplus)
#error C++ compiler required.
#endif
这个就是说如果你的程序不是C++的,譬如C的,就会报错。里面的错误信息是用户自己决定的。
#pragma once基本和前面那个选择编译一样的。 参考资料:MSDN 10月16日 谭浩俊:焉能不亏的中投公司 http://www.sina.com.cn 2008年10月15日 15:32 新华网
焉能不亏的中投公司
谭浩俊
继在摩根士丹利和黑石集团的投资上出现大幅亏损后,中国主权财富基金——中国投资有限责任公司(下称“中投公司”)又在美国货币基金投资上遭受损失。根据最新公布的美国监管机构申报档案文件,受金融危机影响,中投公司冻结于美国货币市场的资产已达到54亿美元。(10月14日新快报)
说来真够令人心寒的,在全国人民的热切期待中,高调成立的中投公司从成立到现在,没有听到令人振奋、让人高兴的消息,却传出一个又一个投资失利、投资受损的信息。投资摩根士丹利大亏,投资黑石集团大亏,投资美国货币市场基金Reserve Primary Fund又是一次错误的决策。中投公司俨然成为一个为亏损而生、为美国公司埋单的“替死鬼”。
中投公司的高管们也许可以找出无数理由,来为自己的投资失利辩解,譬如投资本来就有风险、有的损失只是暂时的、主要是世界金融危机的影响、从长远看不会损失等等。但是,我们要问的是,一个新成立的公司,屡屡出现投资失利,损失惨重呢?公众何时才能听到中投公司投资盈利的消息呢?
有网友感叹,中投公司反正是国家投资成立的,赚了钱,公司的高管们得政绩、拿高薪,甚至可以获得政治上的嘉奖;亏了本,公司的高管们照样可以拿到让普通百姓望尘莫及的“保底”薪水。
事实也是如此,这些具有令人羡慕的政治履历、行政背景、职业经历和学历的高管们,虽然一个个以财政专家、金融专家、投资行家、理财行家的面貌出现,但这些长期在政府机关、金融管理机关工作的高管,已经习惯于机关化的运作模式和操作方式,突然进入到完全需要依靠战略眼光、依靠经验、依靠分析和预测进行判断与决策的市场中,能不感到眼花缭乱、不知所措吗?又如何去从容应对呢?
从中投公司成立后把投资重点完全放在美国也不难看出,中投公司在投资决策上的行政思维是很浓的。对中投公司的高管们来说,如果中投公司成立以后,没有一些实质性的动作,没有一些具体的投资项目,显然难以对上交待,因此迫切需要找一些项目进行投资。于是,按照一贯的行政思维,把投资美国和美国公司作为了第一选择。毕竟,美国是世界第一经济强国,投资美国的公司和项目,应该是不会亏本的,即使亏本,也好找理由。
于是,中投公司对摩根士丹利投资了50亿美元,对黑石集团投资了30亿美元,可是,偏偏这两家公司不争气,市值跳水双双超过70%。
更让人难以接受的是,次贷危机爆发以后,在投资者纷纷逃离市场导致美国信贷紧缩,整个货币市场基金业都面临危局的情况下,中投公司竟然对货币市场基金出手,大量投资美国货币市场基金,导致高达54亿美元的投资遭到冻结。
虽然一位了解中投公司运作情况的人士表示,这只是被冻结,价格上没有下跌太多,所以实际冲击并非那么严重。但是,金融危机还在延续,美国金融市场的风险并没有消除,美国货币市场基金随时有“破碎”的可能,谁又能保证这些被“冻结”的投资不是一块不良资产呢?
俗话说,不见棺材不掉泪。见了棺材还不掉泪,还在为自己的投资失误寻找借口,这样的投资公司,投资能够不失误吗?实事求是地讲,中投公司如果继续维持目前的管理方式和人员结构,那么到底还要交多少学费、付多少代价,恐怕谁也说不清。要改变这种局面,就必须在全球招聘管理人才,引进最先进的管理方式和管理手段,增强市场意识,淡化“行政”意味,让中投公司成为一个真正符合市场经济要求的国有投资公司。
更多精彩评论,更多传媒视点,更多传媒人风采,尽在新浪财经新评谈栏目,欢迎访问新浪财经新评谈栏目。
10月14日 http://bigwhite.blogbus.com/ 许多关于C语言的文章不错 http://blog.chinaunix.net/u1/38994/ 朋友的师傅 http://blog.readnovel.com/user/445476.html 许多关于C语言的文章不错 http://www.cublog.cn/u/13991/index.html 许多关于GNU工具的深入探讨 http://blog.csdn.net/yayong solaris,asm http://blog.solrex.cn/
10月12日 C语言宏定义技巧(常用宏定义) 写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等。下面列举一些成熟软件中常用得宏定义。。。。。。 1,防止一个头文件被重复包含 #ifndef COMDEF_H #define COMDEF_H //头文件内容 #endif 2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。 typedef unsigned char boolean; /* Boolean value type. */ typedef unsigned long int uint32; /* Unsigned 32 bit value */ typedef unsigned short uint16; /* Unsigned 16 bit value */ typedef unsigned char uint8; /* Unsigned 8 bit value */ typedef signed long int int32; /* Signed 32 bit value */ typedef signed short int16; /* Signed 16 bit value */ typedef signed char int8; /* Signed 8 bit value */ //下面的不建议使用 typedef unsigned char byte; /* Unsigned 8 bit value type. */ typedef unsigned short word; /* Unsinged 16 bit value type. */ typedef unsigned long dword; /* Unsigned 32 bit value type. */ typedef unsigned char uint1; /* Unsigned 8 bit value type. */ typedef unsigned short uint2; /* Unsigned 16 bit value type. */ typedef unsigned long uint4; /* Unsigned 32 bit value type. */ typedef signed char int1; /* Signed 8 bit value type. */ typedef signed short int2; /* Signed 16 bit value type. */ typedef long int int4; /* Signed 32 bit value type. */ typedef signed long sint31; /* Signed 32 bit value */ typedef signed short sint15; /* Signed 16 bit value */ typedef signed char sint7; /* Signed 8 bit value */ 3,得到指定地址上的一个字节或字 #define MEM_B( x ) ( *( (byte *) (x) ) ) #define MEM_W( x ) ( *( (word *) (x) ) ) 4,求最大值和最小值 #define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) ) #define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) ) 5,得到一个field在结构体(struct)中的偏移量 #define FPOS( type, field ) \ /*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */ 6,得到一个结构体中field所占用的字节数 #define FSIZ( type, field ) sizeof( ((type *) 0)->field ) 7,按照LSB格式把两个字节转化为一个Word #define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] ) 8,按照LSB格式把一个Word转化为两个字节 #define FLOPW( ray, val ) \ (ray)[0] = ((val) / 256); \ (ray)[1] = ((val) & 0xFF) 9,得到一个变量的地址(word宽度) #define B_PTR( var ) ( (byte *) (void *) &(var) ) #define W_PTR( var ) ( (word *) (void *) &(var) ) 10,得到一个字的高位和低位字节 #define WORD_LO(xxx) ((byte) ((word)(xxx) & 255)) #define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8)) 11,返回一个比X大的最接近的8的倍数 #define RND8( x ) ((((x) + 7) / 8 ) * 8 ) 12,将一个字母转换为大写 #define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) ) 13,判断字符是不是10进值的数字 #define DECCHK( c ) ((c) >= '0' && (c) <= '9') 14,判断字符是不是16进值的数字 #define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\ ((c) >= 'A' && (c) <= 'F') ||\ ((c) >= 'a' && (c) <= 'f') ) 15,防止溢出的一个方法 #define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val)) 16,返回数组元素的个数 #define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) ) 17,返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n) #define MOD_BY_POWER_OF_TWO( val, mod_by ) \ ( (dword)(val) & (dword)((mod_by)-1) ) 18,对于IO空间映射在存储空间的结构,输入输出处理 #define inp(port) (*((volatile byte *) (port))) #define inpw(port) (*((volatile word *) (port))) #define inpdw(port) (*((volatile dword *)(port))) #define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val))) #define outpw(port, val) (*((volatile word *) (port)) = ((word) (val))) #define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val))) [2005-9-9添加] 19,使用一些宏跟踪调试 A N S I标准说明了五个预定义的宏名。它们是: _ L I N E _ _ F I L E _ _ D A T E _ _ T I M E _ _ S T D C _ 如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序 也许还提供其它预定义的宏名。 _ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。 _ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。 源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。 如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是 非标准的。 可以定义宏,例如: 当定义了_DEBUG,输出数据信息和所在文件所在行 #ifdef _DEBUG #define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_) #else #define DEBUGMSG(msg,date) #endif 20,宏定义防止使用是错误 用小括号包含。 例如:#define ADD(a,b) (a+b) 用do{}while(0)语句包含多语句防止错误 例如:#difne DO(a,b) a+b;\ a++; 应用时:if(….) DO(a,b); //产生错误 else 解决方法: #difne DO(a,b) do{a+b;\ a++;}while(0) 宏中"#"和"##"的用法 一、一般用法 我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. 用法: #i nclude<cstdio> #i nclude<climits> using namespace std; #define STR(s) #s #define CONS(a,b) int(a##e##b) int main() { printf(STR(vck)); // 输出字符串"vck" printf("%d\n", CONS(2,3)); // 2e3 输出:2000 return 0; } 二、当宏参数是另一个宏的时候 需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开. 1, 非'#'和'##'的情况 #define TOW (2) #define MUL(a,b) (a*b) printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW)); 这行的宏会被展开为: printf("%d*%d=%d\n", (2), (2), ((2)*(2))); MUL里的参数TOW会被展开为(2). 2, 当有'#'或'##'的时候 #define A (2) #define STR(s) #s #define CONS(a,b) int(a##e##b) printf("int max: %s\n", STR(INT_MAX)); // INT_MAX #i nclude<climits> 这行会被展开为: printf("int max: %s\n", "INT_MAX"); printf("%s\n", CONS(A, A)); // compile error 这一行则是: printf("%s\n", int(AeA)); INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数. #define A (2) #define _STR(s) #s #define STR(s) _STR(s) // 转换宏 #define _CONS(a,b) int(a##e##b) #define CONS(a,b) _CONS(a,b) // 转换宏 printf("int max: %s\n", STR(INT_MAX)); // INT_MAX,int型的最大值,为一个变量 #i nclude<climits> 输出为: int max: 0x7fffffff STR(INT_MAX) --> _STR(0x7fffffff) 然后再转换成字符串; printf("%d\n", CONS(A, A)); 输出为:200 CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2)) 三、'#'和'##'的一些应用特例 1、合并匿名变量名 #define ___ANONYMOUS1(type, var, line) type var##line #define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line) #define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__) 例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号; 第一层:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__); 第二层: --> ___ANONYMOUS1(static int, _anonymous, 70); 第三层: --> static int _anonymous70; 即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开; 2、填充结构 #define FILL(a) {a, #a} enum IDD{OPEN, CLOSE}; typedef struct MSG{ IDD id; const char * msg; }MSG; MSG _msg[] = {FILL(OPEN), FILL(CLOSE)}; 相当于: MSG _msg[] = {{OPEN, "OPEN"}, {CLOSE, "CLOSE"}}; 3、记录文件名 #define _GET_FILE_NAME(f) #f #define GET_FILE_NAME(f) _GET_FILE_NAME(f) static char FILE_NAME[] = GET_FILE_NAME(__FILE__); 4、得到一个数值类型所对应的字符串缓冲大小 #define _TYPE_BUF_SIZE(type) sizeof #type #define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type) char buf[TYPE_BUF_SIZE(INT_MAX)]; --> char buf[_TYPE_BUF_SIZE(0x7fffffff)]; --> char buf[sizeof "0x7fffffff"]; 这里相当于: char buf[11]; 10月11日 VSFTP文件与目录 /usr/sbin/vsftp vsftp的主程序 /etc/rc.d/init.d/vsftp vsftp的启动脚本 /etc/vsftpd/vsftpd.conf vsftp的配置文件 /etc/pamd/vsftpd PAM认证文件 /etc/vsftpd/vsftpd.ftpuser 禁止使用FTP的用户 /etc/vsftpd/vsftpd.user_list 禁止或允许使用ftp的用户列表 /var/ftp ftp匿名主目录 /varftp/pub ftp匿名上传主目录 VSFTP启动 Standalone方式 用于ftp访问频繁的环境 VSFTP进程始终运行监听端口 Service vsftp start|stop|restart|status /etc/rc.d/init.d/vsftpd start|stop|restart 或者在/etc/vsftpd/vsftpd.conf中 加入listen=yes 表示以standalone运行 在inet.d守护进程中运行 用于ftp访问量很小的情况 vsftp在inet.d守护进程中运行 运行/etc/inet.d/vsftp中的脚本 VSFTP的配置文件 /etc/vsftpd/vsftpd.conf 主动模式设置 Port_enable=YES 开启主动模式 Connect_from_port_20=YES 当主动模式开启的时候 是否启用默认的20端口监听 Ftp_date_port=%portnumber% 上一选项使用NO参数是 指定数据传输端口 被动模式 PASV_enable=YES 开启被动模式 PASV_min_port=%number% 被动模式最低端口 PASV_max_port=%number% 被动模式最高端口 匿名上传设置 anonymous_enable=YES 启用匿名帐户 anon_world_readable_only=NO 关闭匿名全局浏览 anon_upload_enable=YES 匿名上传开启 anon_mkdir_write_enable=YES 允许匿名用户创建目录 write_enable=YES 全局写入权限开启 限制本地用户访问文件系统 chroot_local_user=YES 将本地用户浏览限制在其FTP根目录下 限制部分用户访问文件系统 chroot_list_enable=YES 启用列表(不可以与上条命令同时开启) chroot_list_file=%file path% 限制用户的列表文件 连接限制 Max_client=%number% 最大连接数 max_per_ip=%number% 每ip最大连接数 anon_max_rate=%number% 匿名用户最大速率 单位kbps local_max_rate=%number% 本地用户最大速率 单位kbps user_config_dir=%file path%/%username% 针对不同用户的连接速率设置 %username%文件的内容为 local_max_rate=%number% 用户主目录设置 本地用户的主目录定义在/etc/passwd文件中 其中FTP user:________为定义行 全局重定向localuser的ftp主目录 local_root=%path% 安全设置 hide_ids=YES 隐藏用户的UID和GID 改变原有banner ftpd_banner=%message% 或者 banner_file=%file path% 虚拟FTP站点设置 首先创建2套conf文件 #cp /etc/vsftpd/vsftpd.conf /etc/vsftp/vsftp2.conf 创建ftp2的主目录 #mkdir /var/ftp2 #useradd -d /var/ftp2 -M FTP2 #service vsftpd restart 单独启动某一站点 /user/sbin/vsftpd /etc/vsftpd/vsftp2.conf& 来自:Linux文档 现载:Www.8s8s.coM 地址:无名 NFS server可以看作是一个FILE SERVER,它可以让你的PC通过网络将远端得NFS SERVER共享出来的档案MOUNT到自己的系统中,在CLIENT看来使用NFS的远端文件就象是在使用本地文件一样。 NFS协议从诞生到现在为止,已经有多个版本,如NFS V2(rfc1094),NFS V3(rfc1813)(最新的版本是V4(rfc3010)。 二、各NFS协议版本的主要区别 V3相对V2的主要区别: 1、文件尺寸 V2最大只支持32BIT的文件大小(4G),而NFS V3新增加了支持64BIT文件大小的技术。 2、文件传输尺寸 V3没有限定传输尺寸,V2最多只能设定为8k,可以使用-rsize and -wsize 来进行设定。 3、完整的信息返回 V3增加和完善了许多错误和成功信息的返回,对于服务器的设置和管理能带来很大好处。 4、增加了对TCP传输协议的支持 V2只提供了对UDP协议的支持,在一些高要求的网络环境中有很大限制,V3增加了对TCP协议的支持 *5、异步写入特性 6、改进了SERVER的mount性能 7、有更好的I/O WRITES 性能。 9、更强网络运行效能,使得网络运作更为有效。 10、更强的灾难恢复功能。 异步写入特性(v3新增加)介绍: NFS V3 能否使用异步写入,这是可选择的一种特性。NFS V3客户端发发送一个异步写入请求到服务器,在给客户端答复之前服务器并不是必须要将数据写入到存储器中(稳定的)。服务器能确定何时去写入数据或者将多个写入请求聚合到一起并加以处理,然后写入。客户端能保持一个数据的copy以防万一服务器不能完整的将数据写入。当客户端希望释放这个copy的时候,它会向服务器通过这个操作过程,以确保每个操作步骤的完整。异步写入能够使服务器去确定最好的同步数据的策略。使数据能尽可能的同步的提交何到达。与V2 比较来看,这样的机制能更好的实现数据缓冲和更多的平行(平衡)。而NFS V2的SERVER在将数据写入存储器之前不能再相应任何的写入请求。 V4相对V3的改进: 1:改进了INTERNET上的存取和执行效能 2:在协议中增强了安全方面的特性 3:增强的跨平台特性 三、CLIENT和SERVER的具体操作和设置 在讲NFS SERVER的运作之前先来看一些与NFS SERVER有关的东西: RPC(Remote Procedure Call) NFS 本身是没有提供信息传输的协议和功能的,但NFS却能让我们通过网络进行资料的分享,这是因为NFS使用了一些其它的传输协议。而这些传输协议勇士用到这个RPC功能的。可以说NFS本身就是使用RPC的一个程序。或者说NFS也是一个RPC SERVER.所以只要用到NFS的地方都要启动RPC服务,不论是NFS SERVER或者NFS CLIENT。这样SERVER和CLIENT才能通过RPC来实现PROGRAM PORT的对应。可以这么理解RPC和NFS的关系:NFS是一个文件系统,而RPC是负责负责信息的传输。 NFS需要启动的DAEMONS pc.nfsd:主要复杂登陆权限检测等。 rpc.mountd:负责NFS的档案系统,当CLIENT端通过rpc.nfsd登陆SERVER后,对clinet存取server的文件进行一系列的管理 NFS SERVER在REDHAT LINUX平台下一共需要两个套件:nfs-utils和PORTMAP nfs-utils:提供rpc.nfsd 及 rpc.mountd这两个NFS DAEMONS的套件 portmap: NFS其实可以被看作是一个RPC SERVER PROGRAM,而要启动一个RPC SERVER PROGRAM,都要做好PORT的对应工作,而且这样的任务就是由PORTMAP来完成的。通俗的说PortMap就是用来做PORT的mapping 的。 一:服务器端的设定(以LINUX为例) 服务器端的设定都是在/etc/exports这个文件中进行设定的,设定格式如下: 欲分享出去的目录 主机名称1或者IP1(参数1,参数2) 主机名称2或者IP2(参数3,参数4) 上面这个格式表示,同一个目录分享给两个不同的主机,但提供给这两台主机的权限和参数是不同的,所以分别设定两个主机得到的权限。 可以设定的参数主要有以下这些: rw:可读写的权限; ro:只读的权限; no_root_squash:登入到NFS主机的用户如果是ROOT用户,他就拥有ROOT的权限,此参数很不安全,建议不要使用。 root_squash:在登入 NFS 主?C使用分享之目?的使用者如果是 root ?r,那????使用者的?嘞?⒈?嚎s成?槟涿褂谜撸ǔK?UID ? GID 都??成 nobody 那??身份; all_squash:不管登陆NFS主机的用户是什么都会被重新设定为nobody。 anonuid:将登入NFS主机的用户都设定成指定的user id,此ID必须存在于/etc/passwd中。 anongid:同 anonuid ,但是?成 group ID 就是了! sync:资料同步写入存储器中。 async:资料会先暂时存放在内存中,不会直接写入硬盘。 insecure 允许从这台机器过来的非授权访问。 例如可以编辑/etc/exports为: /tmp *(rw,no_root_squash) /home/public 192.168.0.*(rw) *(ro) /home/test 192.168.0.100(rw) /home/linux *.the9.com(rw,all_squash,anonuid=40,anongid=40) 设定好后可以使用以下命令启动NFS: /etc/rc.d/init.d/portmap start (在REDHAT中PORTMAP是默认启动的) /etc/rc.d/init.d/nfs start exportfs命令: 如果我们在启动了NFS之后又修改了/etc/exports,是不是还要重新启动nfs呢?这个时候我们就可以用exportfs命令来使改动立刻生效,该命令格式如下: exportfs [-aruv] -a :全部mount或者unmount /etc/exports中的内容 -r :重新mount /etc/exports中分享出来的目录 -u :umount 目录 -v :在 export 的?r候,将详细的信息输出到屏幕上。 具体例子: [root @test root]# exportfs -rv <==全部重新 export 一次! exporting 192.168.0.100:/home/test exporting 192.168.0.*:/home/public exporting *.the9.com:/home/linux exporting *:/home/public exporting *:/tmp reexporting 192.168.0.100:/home/test to kernel exportfs -au <==全部都卸载了。 客户段的操作: 1、showmout命令对于NFS的操作和查错有很大的帮助,所以我们先来看一下showmount的用法 showmout -a :这个参数是一般在NFS SERVER上使用,是用来显示已经mount上本机nfs目录的cline机器。 -e :显示指定的NFS SERVER上export出来的目录。 例如: showmount -e 192.168.0.30 Export list for localhost: /tmp * /home/linux *.linux.org /home/public (everyone) /home/test 192.168.0.100 2、mount nfs目录的方法: mount -t nfs hostname(orIP):/directory /mount/point 具体例子: Linux: mount -t nfs 192.168.0.1:/tmp /mnt/nfs Solaris:mount -F nfs 192.168.0.1:/tmp /mnt/nfs BSD: mount 192.168.0.1:/tmp /mnt/nfs 3、mount nfs的其它可选参数: HARD mount和SOFT MOUNT: HARD: NFS CLIENT会不断的尝试与SERVER的连接(在后台,不会给出任何提示信息,在LINUX下有的版本仍然会给出一些提示),直到MOUNT上。 SOFT:会在前台尝试与SERVER的连接,是默认的连接方式。当收到错误信息后终止mount尝试,并给出相关信息。 例如:mount -F nfs -o hard 192.168.0.10:/nfs /nfs 对于到底是使用hard还是soft的问题,这主要取决于你访问什么信息有关。例如你是想通过NFS来运行X PROGRAM的话,你绝对不会希望由于一些意外的情况(如网络速度一下子变的很慢,插拔了一下网卡插头等)而使系统输出大量的错误信息,如果此时你用的是HARD方式的话,系统就会等待,直到能够重新与NFS SERVER建立连接传输信息。另外如果是非关键数据的话也可以使用SOFT方式,如FTP数据等,这样在远程机器暂时连接不上或关闭时就不会挂起你的会话过程。 rsize和wsize: 文件传输尺寸设定:V3没有限定传输尺寸,V2最多只能设定为8k,可以使用-rsize and -wsize 来进行设定。这两个参数的设定对于NFS的执行效能有较大的影响 bg:在执行mount时如果无法顺利mount上时,系统会将mount的操作转移到后台并继续尝试mount,直到mount成功为止。(通常在设定/etc/fstab文件时都应该使用bg,以避免可能的mount不上而影响启动速度) fg:和bg正好相反,是默认的参数 nfsvers=n:设定要使用的NFS版本,默认是使用2,这个选项的设定还要取决于server端是否支持NFS VER 3 mountport:设定mount的端口 port:根据server端export出的端口设定,例如如果server使用5555端口输出NFS,那客户端就需要使用这个参数进行同样的设定 timeo =n:设置超时时间,当数据传输遇到问题时,会根据这个参数尝试进行重新传输。默认值是7/10妙(0.7秒)。如果网络连接不是很稳定的话就要加大这个数值,并且推荐使用HARD MOUNT方式,同时最好也加上INTR参数,这样你就可以终止任何挂起的文件访问。 intr 允许通知中断一个NFS调用。当服务器没有应答需要放弃的时候有用处。 udp:使用udp作为nfs的传输协议(NFS V2只支持UDP) tcp:使用tcp作为nfs的传输协议 namlen=n:设定远程服务器所允许的最长文件名。这个值的默认是255 acregmin=n:设定最小的在文件更新之前cache时间,默认是3 acregmax=n:设定最大的在文件更新之前cache时间,默认是60 acdirmin=n:设定最小的在目录更新之前cache时间,默认是30 acdirmax=n:设定最大的在目录更新之前cache时间,默认是60 actimeo=n:将acregmin、acregmax、acdirmin、acdirmax设定为同一个数值,默认是没有启用。 retry=n:设定当网络传输出现故障的时候,尝试重新连接多少时间后不再尝试。默认的数值是10000 minutes noac:关闭cache机制。 同时使用多个参数的方法:mount -t nfs -o timeo=3,udp,hard 192.168.0.30:/tmp /nfs 请注意,NFS客户机和服务器的选项并不一定完全相同,而且有的时候会有冲突。比如说服务器以只读的方式导出,客户端却以可写的方式mount,虽然可以成功mount上,但尝试写入的时候就会发生错误。一般服务器和客户端配置冲突的时候,会以服务器的配置为准。 4、/etc/fstab的设定方法 /etc/fstab的格式如下: fs_spec fs_file fs_type fs_options fs_dump fs_pass fs_spec:该字段定义希望加载的文件系统所在的设备或远程文件系统,对于nfs这个参数一般设置为这样:192.168.0.1:/NFS fs_file:本地的挂载点 fs_type:对于NFS来说这个字段只要设置成nfs就可以了 fs_options:挂载的参数,可以使用的参数可以参考上面的mount参数。 fs_dump - 该选项被"dump"命令使用来检查一个文件系统应该以多快频率进行转储,若不需要转储就设置该字段为0 fs_pass - 该字段被fsck命令用来决定在启动时需要被扫描的文件系统的顺序,根文件系统"/"对应该字段的值应该为1,其他文件系统应该为2。若该文件系统无需在启动时扫描则设置该字段为0 。 5、与NFS有关的一些命令介绍 nfsstat: 查看NFS的运行状态,对于调整NFS的运行有很大帮助 rpcinfo: 查看rpc执行信息,可以用于检测rpc运行情况的工具。 四、NFS调优 调优的步骤: 1、测量当前网络、服务器和每个客户端的执行效率。 2、分析收集来的数据并画出图表。查找出特殊情况,例如很高的磁盘和CPU占用、已经高的磁盘使用时间 3、调整服务器 4、重复第一到第三步直到达到你渴望的性能 与NFS性能有关的问题有很多,通常可以要考虑的有以下这些选择: WSIZE,RSIZE参数来优化NFS的执行效能 WSIZE、RSIZE对于NFS的效能有很大的影响。 wsize和rsize设定了SERVER和CLIENT之间往来数据块的大小,这两个参数的合理设定与很多方面有关,不仅是软件方面也有硬件方面的因素会影响这两个参数的设定(例如LINUX KERNEL、网卡,交换机等等)。 下面这个命令可以测试NFS的执行效能,读和写的效能可以分别测试,分别找到合适的参数。对于要测试分散的大量的数据的读写可以通过编写脚本来进行测试。在每次测试的时候最好能重复的执行一次MOUNT和unmount。 time dd if=/dev/zero of=/mnt/home/testfile bs=16k count=16384 用于测试的WSIZE,RSIZE最好是1024的倍数,对于NFS V2来说8192是RSIZE和WSIZE的最大数值,如果使用的是NFS V3则可以尝试的最大数值是32768。 如果设置的值比较大的时候,应该最好在CLIENT上进入mount上的目录中,进行一些常规操作(LS,VI等等),看看有没有错误信息出现。有可能出现的典型问题有LS的时候文件不能完整的列出或者是出现错误信息,不同的操作系统有不同的最佳数值,所以对于不同的操作系统都要进行测试。 设定最佳的NFSD的COPY数目。 linux中的NFSD的COPY数目是在/etc/rc.d/init.d/nfs这个启动文件中设置的,默认是8个NFSD,对于这个参数的设置一般是要根据可能的CLIENT数目来进行设定的,和WSIZE、RSIZE一样也是要通过测试来找到最近的数值。 UDP and TCP 可以手动进行设置,也可以自动进行选择。 mount -t nfs -o sync,tcp,noatime,rsize=1024,wsize=1024 EXPORT_MACHINE:/EXPORTED_DIR /DIR UDP 有着传输速度快,非连接传输的便捷特性,但是UDP在传输上没有TCP来的稳定,当网络不稳定或者黑客入侵的时候很容易使NFS的 Performance 大幅降低甚至使网络瘫痪。所以对于不同情况的网络要有针对的选择传输协议。nfs over tcp比较稳定, nfs over udp速度较快。在机器较少网络状况较好的情况下使用UDP协议能带来较好的性能,当机器较多,网络情况复杂时推荐使用TCP协议(V2只支持UDP协议)。在局域网中使用UDP协议较好,因为局域网有比较稳定的网络保证,使用UDP可以带来更好的性能,在广域网中推荐使用TCP协议,TCP协议能让 NFS在复杂的网络环境中保持最好的传输稳定性。可以参考这篇文章:http: //www.hp.com.tw/ssn/unix/0212/unix021204.asp 版本的选择 V3作为默认的选择(RED HAT 8默认使用V2,SOLARIS 8以上默认使用V3),可以通过vers= mount option来进行选择。 LINUX通过mount option的nfsvers=n进行选择。 五、NFS故障解决 1、NFSD没有启动起来 首先要确认 NFS 输出列表存在,否则 nfsd 不会启动。可用 exportfs 命令来检查,如果 exportfs 命令没有结果返回或返回不正确,则需要检查 /etc/exports 文件。 2、mountd 进程没有启动 mountd 进程是一个远程过程调用 (RPC) ,其作用是对客户端要求安装(mount)文件系统的申请作出响应。mountd进程通过查找 /etc/xtab 文件来获知哪些文件系统可以被远程客户端使用。另外,通过mountd进程,用户可以知道目前有哪些文件系统已被远程文件系统装配,并得知远程客户端的列表。查看mountd是否正常启动起来可以使用命令rpcinfo进行查看,在正常情况下在输出的列表中应该象这样的行: 100005 1 udp 1039 mountd 100005 1 tcp 1113 mountd 100005 2 udp 1039 mountd 100005 2 tcp 1113 mountd 100005 3 udp 1039 mountd 100005 3 tcp 1113 mountd 如果没有起来的话可以检查是否安装了PORTMAP组件。 rpm -qa|grep portmap 3、fs type nfs no supported by kernel kernel不支持nfs文件系统,重新编译一下KERNEL就可以解决。 4、can't contact portmapper: RPC: Remote system error - Connection refused 出现这个错误信息是由于SEVER端的PORTMAP没有启动。 5、mount clntudp_create: RPC: Program not registered NFS没有启动起来,可以用showmout -e host命令来检查NFS SERVER是否正常启动起来。 6、mount: localhost:/home/test failed, reason given by server: Permission denied 这个提示是当client要mount nfs server时可能出现的提示,意思是说本机没有权限去mount nfs server上的目录。解决方法当然是去修改NFS SERVER咯。 7、被防火墙阻挡 这个原因很多人都忽视了,在有严格要求的网络环境中,我们一般会关闭linux上的所有端口,当需要使用哪个端口的时候才会去打开。而NFS默认是使用111端口,所以我们先要检测是否打开了这个端口,另外也要检查TCP_Wrappers的设定。 六、NFS安全 NFS的不安全性主要体现于以下4个方面: 1、新手对NFS的访问控制机制难于做到得心应手,控制目标的精确性难以实现 2、NFS没有真正的用户验证机制,而只有对RPC/Mount请求的过程验证机制 3、较早的NFS可以使未授权用户获得有效的文件句柄 4、在RPC远程调用中,一个SUID的程序就具有超级用户权限. 加强NFS安全的方法: 1、合理的设定/etc/exports中共享出去的目录,最好能使用anonuid,anongid以使MOUNT到NFS SERVER的CLIENT仅仅有最小的权限,最好不要使用root_squash。 2、使用IPTABLE防火墙限制能够连接到NFS SERVER的机器范围 iptables -A INPUT -i eth0 -p TCP -s 192.168.0.0/24 --dport 111 -j ACCEPT iptables -A INPUT -i eth0 -p UDP -s 192.168.0.0/24 --dport 111 -j ACCEPT iptables -A INPUT -i eth0 -p TCP -s 140.0.0.0/8 --dport 111 -j ACCEPT iptables -A INPUT -i eth0 -p UDP -s 140.0.0.0/8 --dport 111 -j ACCEPT 3、为了防止可能的Dos攻击,需要合理设定NFSD 的COPY数目。 4、修改/etc/hosts.allow和/etc/hosts.deny达到限制CLIENT的目的 /etc/hosts.allow portmap: 192.168.0.0/255.255.255.0 : allow portmap: 140.116.44.125 : allow /etc/hosts.deny portmap: ALL : deny 5、改变默认的NFS 端口 NFS默认使用的是111端口,但同时你也可以使用port参数来改变这个端口,这样就可以在一定程度上增强安全性。 6、使用Kerberos V5作为登陆验证系统 NFS(网络文件系统)的建立与配置方法 网络文件系统(NFS,Network File System)是一种将远程主机上的分区(目录)经网络挂载到本地系统的一种机制,通过对网络文件系统的支持,用户可以在本地系统上像操作本地分区一样来对远程主机的共享分区(目录)进行操作。 在嵌入式Linux 的开发过程中,开发者需要在Linux 服务器上进行所有的软件开发,交叉编译后,通用FTP 方式将可执行文件下载到嵌入式系统运行,但这种方式不但效率低下,且无法实现在线的调试。因此,可以通过建立NFS,把Linux 服务器上的特定分区共享到待调试的嵌入式目标系统上,就可以直接在嵌入式目标系统上操作Linux 服务器,同时可以在线对程序进行调试和修改,大大的方便了软件的开发。因此,NFS 的是嵌入式Linux 开发的一个重要的组成部分,本部分内容将详细说明如何配置嵌入式Linux 的NFS 开发环境。 嵌入式Linux 的NFS 开发环境的实现包括两个方面:一是Linux 服务器端的NFS 服务器支持;二是嵌入式目标系统的NFS 客户端的支持。因此,NFS 开发环境的建立需要配置linux 服务器端和嵌入式目标系统端。 一、Linux 服务器端NFS 服务器的配置 以root 身份登陆Linux 服务器,编辑/etc 目录下的共享目录配置文件exports,指定共享目录及权限等。 执行如下命令编辑文件/etc/exports: # vi /etc/exports 在该文件里添加如下内容: /home/work 192.168.0.*(rw,sync,no_root_squash) 然后保存退出。 添加的内容表示:允许ip 地址范围在192.168.0.*的计算机以读写的权限来访问/home/work 目录。 /home/work 也称为服务器输出共享目录。 括号内的参数意义描述如下: rw:读/写权限,只读权限的参数为ro; sync:数据同步写入内存和硬盘,也可以使用async,此时数据会先暂存于内存中,而不立即写入硬盘。 no_root_squash:NFS 服务器共享目录用户的属性,如果用户是 root,那么对于这个共享目录来说就具有 root 的权限。 接着执行如下命令,启动端口映射: # /etc/rc.d/init.d/portmap start 最后执行如下命令启动NFS 服务,此时NFS 会激活守护进程,然后就开始监听 Client 端的请求: # /etc/rc.d/init.d/nfs start 用户也可以重新启动Linux 服务器,自动启动NFS 服务。 在NFS 服务器启动后,还需要检查Linux 服务器的防火墙等设置(一般需要关闭防火墙服务),确保没有屏蔽掉NFS 使用的端口和允许通信的主机,主要是检查Linux 服务器iptables,ipchains 等选项的设置,以及/etc/hosts.deny,/etc/hosts.allow 文件。 我们首先在Linux 服务器上进行NFS 服务器的回环测试,验证共享目录是否能够被访问。在Linux 服务器上运行如下命令: # mount –t nfs 192.168.0.20:/home/work /mnt # ls /mnt 命令将Linux 服务器的NFS 输出共享目录挂载到/mnt 目录下,因此,如果NFS 正常工作,应该能够在/mnt 目录看到/home/work 共享目录中的内容。 二、嵌入式目标系统NFS 客户端的配置 在Linux 服务器设置好后,还需要对客户端进行相关配置。在配置内核时选择Load an Alternate Configuration File输入配置文件的路径和文件名添加内核对NFS的支持: 选中networking options-》IP:kernel level auloconfiguralion项 选中file systems-》network file systems-》下的root file system on nfs 和nfs file system support重新编译内核下载bootloader和kernel到开发板上 在嵌入式目标系统的Linux Shell 下,执行如下命令来进行NFS 共享目录挂载: # mkdir /mnt/nfs //建立Linux 服务器输出共享目录的挂载点; # mount –t nfs 192.168.0.20:/home/work /mnt/nfs –o nolock # cd /mnt/nfs # ls 此时,嵌入式目标系统端所显示的内容即为Linux 服务器的输出目录的内容,即Linux 服务器的输出目。 录/home/work 通过NFS 映射到了嵌入式目标系统的/mnt/nfs 目录。用户可以用增/删/修改文件的方式来验证实际效果。mount 命令中的192.168.0.20 为Linux 服务器的IP 地址,/home/work 为Linux 服务器端所配置的共享输出目录,/mnt/nfs 为嵌入式设备上的本地目录。 在开发过程中,来回输入命令非常烦人,我写了两个简单的脚本来完成nfs的启动,挂载。 host启动nfs: snfs #!/bin/bash ifconfig eth0 192.168.0.20 /etc/rc.d/init.d/portmap start /etc/rc.d/init.d/nfs start 嵌入式目标机挂载nfs: mnfs:
#!/bin/sh mount -t nfs 192.168.0.20:/home/work/nfs /mnt/nfs -o nolock echo “nfs ok!”
====================================================== ======================================================
NFS为network file system 的简称,最早由sun公司开发,一般NFS广泛应用在集群服务器上,他的最大特点是可以通过网络让不同的机器,不同的操作系统可以彼此的共享文件,所以它可以看作一个简单的文件服务器。NFS其实可以被视为一个RPC服务程序,在启动RPC程序前我们先要做好端口的映射工作这就是portmap,portmap的意思是当Client要连接服务器时必须知道服务器的一个空闲端口这时Client会向服务器的portmap请求一个端口然,然后Server告诉Client这端口后才可以建立连接,所以在启动NFS前要先启动portmap
[wds@localhost ~]# rpm –qa |grep nfs && rpm –qa | grep portmap #查找这两个是否安装
[wds@localhost ~]# vi /etc/exports # 这文件是NFS的主要配置文件
[wds@localhost ~]# /usr/sbin/exportfs #这个文件是nfs共享资源命令
[wds@localhost ~]# /usr/sbin/showmount #可以查看远程服务器的共享目录
[wds@localhost ~]# /var/lib/nfs/xtab #nfs 的日志文件
[wds@localhost ~]# vi /etc/exports
[你想要的共享的目录] + ip 地址(参数一,参数二) [主机名二](参数三,参数四)
参数列表
rw: 可以写入权限
ro: 只读权限
no_root_squash: 登陆NFS主机共享目录的如果是root用户那么那的权限也为root但是这样并不安全
root_squash: 登陆的用户如果为root它的权限将变成nobody
all_squash: 不论登陆的用户是什么用户都以匿名用户的权限
sync: 数据同步写入硬盘和内存中
async: 数据先暂时存放在内存中,而不写入硬盘
anounid: 这个可以自己设定uid,但是必须与/etc/passwd目录中用户uid一样
anongid: 同anonuid,但是变的是group id
服务器端配置
[wds@localhost ~]# service portmap start #首先打开portmap
[wds@localhost ~]# service nfs start # 在打开 nfs
[wds@localhost ~]#i iptables –F #清空防火墙命令
[wds@localhost ~]#
比如说我要共享/var/www/html 目录 但是只是让和我一个网段的机器访问192.168.0.0/24这个网段读或写,其他的就只能读,然后在发布一个私人目录/home/wds/只开放给192.168.0.8
这个IP
[wds@localhost ~]# vi /etc/exports
/var/www/html 192.168.0.0/24 (rw) *(ro)
/home/wds 192.168.0.8(rw)
现在想要*.chinaunix.com网段的机器登陆我的NFS,并且访问我的/home/wds/ 但是它们存储时我希望它们的uid和gid都变成40这个用户身份
[wds@localhost ~]# vi /etc/exports
/var/www/html 192.168.0.0/24 (rw) *(ro)
/home/wds 192.168.0.8(rw)
/home/wds *.chinaunix.com(rw,all)squash,anounid=40,anongid=40)
如果我们修改/etc/exports这个文件后,是否要从新启动nfs呢?答案是不不需要,只要使用exportfs来从新扫描一次/etc/exports文件,并且从新设置文件加载即可
语法为:
[wds@localhost ~]# exportfs [-aruv]
参数说明:
-a: 全部挂载(或者卸载)/etc/exports 文件的设置
-r: 从新挂载/etc/exports 里设置,此外,同步更新/etc/exports 及/var/lib/nfs/xtab的内容
-u: 卸载某一目录
-v: 在导出时,将共享目录显示在屏幕上
例如:
[wds@localhost ~]# exportfs –rv 全部从新导出一次
[wds@localhost ~]# exportfs –au 全部卸载掉
Showmount 的是显示是否有挂载
语法为:
[wds@localhost ~]# showmount [-ae] hostname
参数说明:
-a: 在屏幕上显示与当前的client连接后使用目录的状态
-e: 显示Hostname这台机器的/etc/exports中的共享信息
[wds@localhost log]# showmount -e localhost
Export list for localhost:
/var/www/html (everyone)
Rpcinfo [-p]hostname[or ip]
-p 显示端口与程序的信息
[wds@localhost log]# rpcinfo -p localhost
program vers proto port
100000 2 tcp 111 portmapper
100000 2 udp 111 portmapper
100024 1 udp 1024 status
100024 1 tcp 1024 status
100011 1 udp 837 rquotad
100011 2 udp 837 rquotad
100011 1 tcp 840 rquotad
100011 2 tcp 840 rquotad
100003 2 udp 2049 nfs
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
100003 2 tcp 2049 nfs
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
100021 1 udp 1026 nlockmgr
100021 3 udp 1026 nlockmgr
100021 4 udp 1026 nlockmgr
100021 1 tcp 1026 nlockmgr
100021 3 tcp 1026 nlockmgr
100021 4 tcp 1026 nlockmgr
100005 1 udp 858 mountd
100005 1 tcp 861 mountd
100005 2 udp 858 mountd
100005 2 tcp 861 mountd
100005 3 udp 858 mountd
100005 3 tcp 861 mountd
Client端的设置
Server端设置完毕,接下来就是让client端连接上server!连接server步骤如下:
1. 扫描可以使用的server目录:
2. 在client端建立装载点
3. 使用mount 命令远程挂载远程共享目录
4. 解决可能发生的问题(被防火墙过滤掉了)
Showmount是显示远程主机共享资源
[wds@localhost ~]# showmount -e 192.168.0.8
Export list for 192.168.0.8:
/var/www/html (everyone)
/home/wds *.chinaunix.com,192.168.0.6
[wds@localhost ~]# mount -t nfs 192.168.0.8:/var/www/html /mnt 把远程的/var/www/html 挂载到本地
[wds@localhost ~]# umount /mnt 卸载远程目录
如果你想要开机启动时自动加载NFS服务器导出目录,我们在NFS端/etc/fstab文件中加入以下一行
192.168.0.8:/var/www/html /mnt nfs rsize=8192,wsize=8192,timeo=14,intr ====================================================== ======================================================
【IT168 服务器学院】在192.168.16.125上修改 /etc/exports # / 192.168.16.107(rw,no_root_squash) 注释: /: 接入点 192.168.16.107: 允许的客户端 rw: 允许读写 no_root_squash: root不映射到匿名账户 启动 /etc/init.d/portmap start /etc/init.d/nfs start 在192.168.16.107上执行: mount -o rw -t nfs 192.168.16.125:/ /mnt/nfs OK 测试中忘记重新启动portmap 导致修改 exports加上读写选项部工作。 环境: 192.168.16.125 Red Hat Linux release 9 (Shrike) 192.168.16.107 Red Hat Enterprise Linux AS release 3 (Taroon Update 3) 10月9日 1 基本解释 extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。 另外,extern也可用来进行链接指定。 2 问题:extern 变量 在一个源文件里定义了一个数组: char a[6]; 在另外一个文件里用下列语句进行了声明: extern char *a; 请问,这样可以吗? 答案与分析: 1)、不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的指针并不等价于类型T的数组。extern char *a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern char a[ ]。 2)、例子分析如下,如果a[] = "abcd",则外部变量a=0x61626364 (abcd的ASCII码值),*a显然没有意义,如下图:
显然a指向的空间(0x61626364)没有意义,易出现非法内存访问。 3)、这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。 4)、extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明。 3 问题:extern 函数1 常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用? 答案与分析: 如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别: extern int f(); 和int f(); 当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。 4 问题:extern 函数2 当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会照成系统错误,这种情况应该如何解决? 答案与分析: 目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。以避免这种错误。 宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。 5 问题:extern “C” 在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢? 答案与分析: C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。 下面是一个标准的写法: //在.h文件的头上 #ifdef __cplusplus #if __cplusplus extern "C"{ #endif #endif /* __cplusplus */ … … //.h文件结束的地方 #ifdef __cplusplus #if __cplusplus } #endif #endif /* __cplusplus */ 查看本文来源 10月8日 摘要:c 运算符优先表 运算符 结合性 ------------------------------------------------------------------------- () [] -> . 左->右 ! ~ ++ -- - (type) * & sizeof 右->左 * / % 左->右 + - 左->右 << >> 左->右 < <= > >= 左->右 == != 左->右 & 左->右 ^ 左->右 | 左->右 && 左->右 || 左->右 ?: 右->左 assignments 右->左 , 左->右 ---------------------------------------------------------------------------- 1. 最高的其实并不是真正意义上的运算符,包括:数组下标、函数调用操作符,各结构成员选择操作符。 从左->右结合意味着 a.b.c 含义是 (a.b).c 而不是 a.(b.c) 2. 单目运算 比1优先级低,所以 *p() ,会被解释为 *(p()), 如果 p 是函数指针,必须这样写 (*p)() 从右->左结合 意味着 *p++被解释为*(p++) 3. 双目运算 算术运算>移位运算>关系运算>逻辑运算>赋值运算>3目运算 * 任何一个逻辑运算符的优先级低于任何一个关系运算符 * 移位运算符的优先级比算术运算符要低 a = 1>>2+1 被解释为 a=1>>(2+1) 而不是 a=(1>>2)+1 1/2*a means (1/2)*a a<b == c<d 10月6日 看一下下面的代码(当然包括错误,以检验splint的功能): #include
int main(int argc,char* argv[]){
int a=100; /*没有使用的变量*/
int b[8];
printf("Hello c\n");
b[9]=100; /*明显数组越界 */
/* 用到了两个为声明的变量c和d/
c=100;
d=10;
return 0;
}
现在可以用splint来检查一下,为了检验是否可以检测到数组越界,使用 bounds选项。
splint hi.c bounds
输出结果: hi.c: (in function main)
hi.c:9:2: Unrecognized identifier: c
Identifier used in code has not been declared. (Use -unrecog to inhibit
warning)
hi.c:10:2: Unrecognized identifier: d
hi.c:4:6: Variable a declared but not used
A variable is declared but never used. Use /*@unused@*/ in front of
declaration to suppress message. (Use -varuse to inhibit warning)
hi.c:7:2: Likely out-of-bounds store:
b[9]
Unable to resolve constraint:
requires 7 >= 9
needed to satisfy precondition:
requires maxSet(b @ hi.c:7:2) >= 9
A memory write may write to an address beyond the allocated buffer. (Use
-likely-boundswrite to inhibit warning)
hi.c:3:14: Parameter argc not used
A function parameter is not used in the body of the function. If the argument
is needed for type compatibility or future plans, use /*@unused@*/ in the
argument declaration. (Use -paramuse to inhibit warning)
hi.c:3:25: Parameter argv not used
Finished checking --- 6 code warnings
现在具体看一下结果:
检查结果1: hi.c:9:2: Unrecognized identifier: c
Identifier used in code has not been declared. (Use -unrecog to inhibit
warning)
hi.c:10:2: Unrecognized identifier: d
hi.c:4:6: Variable a declared but not used
A variable is declared but never used. Use /*@unused@*/ in front of
declaration to suppress message. (Use -varuse to inhibit warning)
这些应该是splint检测到变量c和d没有声明。
检查结果2: hi.c:7:2: Likely out-of-bounds store:
b[9]
Unable to resolve constraint:
requires 7 >= 9
needed to satisfy precondition:
requires maxSet(b @ hi.c:7:2) >= 9
A memory write may write to an address beyond the allocated buffer. (Use
-likely-boundswrite to inhibit warning)
这些是检查存在数组越界,因为吧b[8]的最大数组序号应该是7,而不是9,所以出现requires 7 >= 9;
检查结果3: hi.c:3:14: Parameter argc not used
A function parameter is not used in the body of the function. If the argument
is needed for type compatibility or future plans, use /*@unused@*/ in the
argument declaration. (Use -paramuse to inhibit warning)
hi.c:3:25: Parameter argv not used
这些表明argc和argv变量声明了,但是没有使用。这个不是什么问题。
假如小心使用splint,应该对于c语言的程序编写有非常大的辅助作用! 10月2日 [转] #ifndef#define#endif的用法(整理) 原作者:icwk 文件中的#ifndef 头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。 还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的: #ifndef <标识> #define <标识> ...... ...... #endif <标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h #ifndef _STDIO_H_ #define _STDIO_H_ ...... #endif 2.在#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。 #ifndef AAA #define AAA ... int i; ... #endif 里面有一个变量定义 在vc中链接时就出现了i重复定义的错误,而在c中成功编译。 结论: (1).当你第一个使用这个头的.cpp文件生成.obj的时候,int i 在里面定义了当另外一个使用这个的.cpp再次[单独]生成.obj的时候,int i 又被定义然后两个obj被另外一个.cpp也include 这个头的,连接在一起,就会出现重复定义. (2).把源程序文件扩展名改成.c后,VC按照C语言的语法对源程序进行编译,而不是C++。在C语言中,若是遇到多个int i,则自动认为其中一个是定义,其他的是声明。 (3).C语言和C++语言连接结果不同,可能(猜测)时在进行编译的时候,C++语言将全局 变量默认为强符号,所以连接出错。C语言则依照是否初始化进行强弱的判断的。(参考) 解决方法: (1).把源程序文件扩展名改成.c。 (2).推荐解决方案: .h中只声明 extern int i;在.cpp中定义 <x.h> #ifndef __X_H__ #define __X_H__ extern int i; #endif //__X_H__ <x.c> int i; 注意问题: (1).变量一般不要定义在.h文件中。 ------------------------------------------------------------------------------------------------------------------------------------------- 一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。 条件编译命令最常见的形式为: #ifdef 标识符 程序段1 #else 程序段2 #endif 它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。 其中#else部分也可以没有,即: #ifdef 程序段1 #denif 这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可以提高C源程序的通用性。如果一个C源程序在不同计算机系统上系统上运行,而不同的计算机又有一定的差异。例如,我们有一个数据类型,在Windows平台中,应该使用long类型表示,而在其他平台应该使用float表示,这样往往需要对源程序作必要的修改,这就降低了程序的通用性。可以用以下的条件编译: #ifdef WINDOWS #define MYTYPE long #else #define MYTYPE float #endif 如果在Windows上编译程序,则可以在程序的开始加上 #define WINDOWS 这样则编译下面的命令行: #define MYTYPE long 如果在这组条件编译命令之前曾出现以下命令行: #define WINDOWS 0 则预编译后程序中的MYTYPE都用float代替。这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。当然以上介绍的只是一种简单的情况,可以根据此思路设计出其它的条件编译。 例如,在调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段: #ifdef DEBUG print ("device_open(%p) ", file); #endif 如果在它的前面有以下命令行: #define DEBUG 则在程序运行时输出file指针的值,以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能觉得不用条件编译也可达此目的,即在调试时加一批printf语句,调试后一一将printf语句删除去。的确,这是可以的。但是,当调试时加的printf语句比较多时,修改的工作量是很大的。用条件编译,则不必一一删改printf语句,只需删除前面的一条“#define DEBUG”命令即可,这时所有的用DEBUG作标识符的条件编译段都使其中的printf语句不起作用,即起统一控制的作用,如同一个“开关”一样。 有时也采用下面的形式: #ifndef 标识符 程序段1 #else 程序段2 #endif 只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,否则编译程序段2。这种形式与第一种形式的作用相反。 以上两种形式用法差不多,根据需要任选一种,视方便而定。 还有一种形式,就是#if后面的是一个表达式,而不是一个简单的标识符: #if 表达式 程序段1 #else 程序段2 #endif 它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。 --------------------------------------------------------------------------------------------------------------------------------------- 作用范围就是当前文件啊。因为编译是以cpp或c文件位单位的嘛。还以这个为例: //正常代码 #ifdef _DEBUG TRACE("Some infomation"); #else //Now is release version,so do nothing #endif //正常代码 编译时是先把所有的预编译处理展开(比如宏)再编译,所以Debug模式下,编译时的代码是: //正常代码 TRACE("Some infomation"); //正常代码 Release模式下的代码是: //正常代码 //正常代码
|