colorful

zc qq:1337220912

 

大端小端(Big- Endian和Little-Endian)

1/ 网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用big-endian排序方式。
2/ 而我们常用的 x86 CPU (intel, AMD) 电脑是 little-endian,也就是整数的低位字节放在内存的低字节处。

举个例子吧。假定你的数据是0x1234,
在网络字节顺序里 这个数据放到内存中就应该显示成
addr addr+1
0x12 0x34
而在x86电脑上,数据0x1234放到内存中实际是:
addr addr+1
0x34 0x12
htons 的用处就是把实际主机内存中的整数存放方式调整成网络字节顺序。


------------------------------------------------------------------------------------------------------------

大端小端(Big- Endian和Little-Endian)

字节序(Endian),大端(Big-Endian),小端(Little-Endian) 图文并茂

http://www.cppblog.com/tx7do/archive/2009/01/06/71276.html

 http://my.oschina.net/alphajay/blog/5478

在 各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机 通信领 域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送。如果不达成一致的规则,通信双方将无法进行正 确的编/译码从而导致通信失败。目前在各种体系的计算机中通常采用的字节存储机制主要有两种:Big-Endian和Little-Endian,下面先 从字节序说起。

 

一、什么是字节序

字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)其实大部分人在实际的开 发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。

在所有的介绍字节序的文章中都会提到字 节序分为两类:Big-Endian和Little-Endian,引用标准的Big-Endian和Little-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

1.1 什么是高/低地址端

首先我们要知道我们C程序映像中内存的空间布局情况:在《C专 家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:
----------------------- 最高内存地址 0xffffffff
栈底

栈顶

-----------------------

NULL (空洞)
-----------------------

-----------------------
未初始 化的数据
----------------------- 统称数据段
初始化的数据
-----------------------
正 文段(代码段)
----------------------- 最低内存地址 0x00000000

以上图为例如果我们在栈 上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢?看下图:
栈底 (高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]

----------
栈顶 (低地址)

1.2 什么是高/低字节

现在我们弄清了高/低地址,接着考虑高/低字节。有些文章中称低位字节为最低有效位,高位字节为最高有效位。如果我们有一个32位无符号整型0x12345678,那么高位是什么,低位又是什么呢? 其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78
高/低地址端和高/低字节都弄清了。我们再来回顾 一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:
以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:

Big-Endian: 低地址存放高位,如下图:
栈底 (高地址)
---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
栈顶 (低地址)

Little-Endian: 低地址存放低位,如下图:
栈底 (高地址)
---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
--------------
栈 顶 (低地址)

 

二、各种Endian

2.1 Big-Endian

计算机体系结构中一种描述多字节存储顺序的术语,在这种机制中最重要字节(MSB)存放在最低端的地址 上。采用这种机制的处理器有IBM3700系列、PDP-10、Mortolora微处理器系列和绝大多数的RISC处理器。
+----------+
| 0x34 |<-- 0x00000021
+----------+
| 0x12 |<-- 0x00000020
+----------+
图 1:双字节数0x1234以Big-Endian的方式存在起始地址0x00000020中

 在Big-Endian中,对于bit序列 中的序号编排方式如下(以双字节数0x8B8A为例):
bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+-----------------------------------------+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+----------------------------------------+
图 2:Big-Endian的bit序列编码方式

2.2 Little-Endian

计算机体系结构中 一种描述多字节存储顺序的术语,在这种机制中最不重要字节(LSB)存放在最低端的地址上。采用这种机制的处理器有PDP-11、VAX、Intel系列 微处理器和一些网络通信设备。该术语除了描述多字节存储顺序外还常常用来描述一个字节中各个比特的排放次序。

+----------+
| 0x12 |<-- 0x00000021
+----------+
| 0x34 |<-- 0x00000020
+----------+

图3:双字节数0x1234以Little-Endian的方式存在起始地址0x00000020中

 在 Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0x8B8A为例):

bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+-----------------------------------------+
val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
+-----------------------------------------+
图 4:Little-Endian的bit序列编码方式

注2:通常我们说的主机序(Host Order)就是遵循Little-Endian规则。所以当两台主机之间要通过TCP/IP协议进行通信的时候就需要调用相应的函数进行主机序 (Little-Endian)和网络序(Big-Endian)的转换

注3:正因为这两种机制对于同一bit序列的序号编排方式恰 恰相反,所以《现代英汉词典》中对MSB的翻译为“最高有效位”欠妥,故本文定义为“最重要的bit/byte”。

2.3 Middle-Endian

除了Big-Endian和Little-Endian之外的多字节存储顺序就是Middle- Endian,比如以4个字节为例:象以3-4-1-2或者2-1-4-3这样的顺序存储的就是Middle-Endian。这种存储顺序偶尔会在一些小 型机体系中的十进制数的压缩格式中出现

嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用 Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。 32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存 地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x78 0x56 0x34 0x12

 

而在Big- endian模式CPU内存中的存放方式则为:

内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x12 0x34 0x56 0x78

 

三、Big-EndianLittle-Endian优缺点

Big-Endian优点:靠首先提取高位字节,你总是可以由看看在偏移位置为0的字节来确定这个数字是 正数还是负数。你不必知道这个数值有多长,或者你也不必过一些字节来看这个数值是否含有符号位这个数值是以它们被打印出来的顺序存放的,所以从二进制到十进制的函数特别有效。因而,对于不同要求的机器,在设计存取方式时就会不同。

Little-Endian优点:提取一个,两个,四个或者更长字节数据的汇编指令以与其他所有格式相同的方式进行:首先在偏移地址为0的地方提取最低位的字节,因为地址偏移和字节数是一对 一的关系,多重精度的数学函数就相对地容易写了

如 果你增加数字的值,你可能在左边增加数字(高位非指数函数需要更多的数字)。 因此, 经常需要增加两位数字并移动存储器里所有Big-endian顺序的数字,把所有数向右移,这会增加计算机的工作量。不过,使用Little- Endian的存储器中不重要的字节可以存在它原来的位置,新的数可以存在它的右边的高位地址里。这就意味着计算机中的某些计算可以变得更加简单和快速。

四、如何检查处理器是Big-Endian还是Little-Endian?

由于联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用Little- endian还是Big-endian模式读写。例如:
int checkCPUendian(){
union {
unsigned int a;
unsigned char b;           
}c;
c.a = 1;
return (c.b == 1);      

}   /*return 1 : little-endian, return 0:big-endian*/

 

五、Big-EndianLittle-Endian转 换

 

现有的平台上Intel的X86采用的是Little-Endian,而像 Sun的SPARC采用的就是Big-Endian。那么在跨平台或网络程序中如何实现字节序的转换呢?这个通过C语言的移位操作很容易实现,例如下面的 宏:

#if defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN)

#define htons(A)   (A)
#define htonl(A)     (A)
#define ntohs(A)   (A)
#define ntohl(A)    (A)

#elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)

#define htons(A)     ((((uint16)(A) & 0xff00) >> 8) | \
(((uint16)(A) & 0x00ff) << 8))
#define htonl(A)     ((((uint32)(A) & 0xff000000) >> 24) | \
(((uint32)(A) & 0x00ff0000) >> 8) | \
(((uint32)(A) & 0x0000ff00) << 8) | \
(((uint32)(A) & 0x000000ff) << 24))
#define ntohs htons
#define ntohl htohl

#else

#error "Either BIG_ENDIAN or LITTLE_ENDIAN must be #defined, but not both."

 

 

网络字节顺序
1、字节内的比特位不受这种顺序的影响
比如一个字节 1000 0000 (或表示为十六进制 80H)不管是什么顺序其内存中的表示法都是这样。

 

2、大于1个字节的数据类型才有字节顺序问题
比如 Byte A,这个变量只有一个字节的长度,所以根据上一条没有字节顺序问题。所以字节顺序是“字节之间的相对顺序”的意思。


3、大于1个字节的数据类型的字节顺序有两种
比如 short B,这是一个两字节的数据类型,这时就有字节之间的相对顺序问题了。
网络字节顺序是“所见即所得”的顺序。而Intel类型的CPU的字节顺序与此相反。
比如上面的 short B=0102H(十六进制,每两位表示一个字节的宽度)。所见到的是“0102”,按一般数学常识,数轴从左到右的方向增加,即内存地址从左到右增加的话,在内存中这个 short B的字节顺序是:
01 02
这就是网络字节顺序。所见到的顺序和在内存中的顺序是一致的!
假设通过抓包得到网络数据的两个字节流为:01 02

而相反的字节顺序就不同了,其在内存中的顺序为:02 01

如果这表示两个 Byte类型的变量,那么自然不需要考虑字节顺序的问题。如果这表示一个 short 变量,那么就需要考虑字节顺序问题。根据网络字节顺序“所见即所得”的规则,这个变量的值就是:0102

假设本地主机是Intel类型的,那么要表示这个变量,有点麻烦:
定义变量 short X,字节流地址为:pt,按顺序读取内存是为x=*((short*)pt);
那么X的内存顺序当然是 01 02按非“所见即所得”的规则,这个内存顺序和看到的一样显然是不对的,所以要把这两个字节的位置调换。调换的方法可以自己定义,但用已经有的API还是更为方便。

网络字节顺序与主机字节顺序
NBO 与HBO 网络字节顺序NBO(Network Byte Order):按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。主机字节顺序(HBO,Host Byte Order):不同的机器HBO不相同,与CPU设计有关计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换。

htonl()
简述:
    将主机的无符号长整形数转换成网络字节顺序。
    #include <winsock.h>
    u_long PASCAL FAR htonl( u_long hostlong);
    hostlong:主机字节顺序表达的32位数。
注释:
    本函数将一个32位数从主机字节顺序转换成网络字节顺序。
返回值:
    htonl()返回一个网络字节顺序的值。

inet_ntoa()
简述:
将网络地址转换成“.”点隔的字符串格式。
#include <winsock.h>
char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
in:一个表示Internet主机地址的结构。
注释:
本 函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设 该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。
返回值:
若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。

网 络中传输的数据有的和本地字节存储顺序一致,而有的则截然不同,为了数据的一致性,就要把本地的数据转换成网络上使用的格式,然后发送出去,接收的时候也 是一样的,经过转换然后才去使用这些数据,基本的库函数中提供了这样的可以进行字节转换的函数,如和htons( ) htonl( ) ntohs( ) ntohl( ),这里n表示network,h表示host,htons( ) htonl( )用于本地字节向网络字节转换的场合,s表示short,即对2字节操作,l表示long即对4字节操作。同样ntohs( )ntohl( )用于网络字节向本地格式转换的场合。

 #endif

posted @ 2012-05-17 10:13 多彩人生 阅读(3130) | 评论 (0)编辑 收藏

关于跨平台数据类型的几篇博文 有些矛盾

c++ 中关于int,unsigned int , short的跨平台移植

int类型比较特殊,具体的字节数同机器字长和编译器有关。如果要保证移植性,尽量用__int16 __int32 __int64吧
__int16、__int32这种数据类型在所有平台下都分配相同的字节。所以在移植上不存在问题。
所谓的不可移植是指:在一个平台上编写的代码无法拿到另一个平台上运行时,不能达到期望的运行结果
例如:在32为平台上(所谓32位平台是指通用寄存器的数据宽度是32)编写代码,int 类型分配4个字节,而在16位平台是则分配2个字节,那么在16位上编译出来的exe,
其中是为int分配2字节,而在32位平台上运行时,会按照4个字节来解析,显然会出错误的!!

而对于非int行,目前为止,所有的类型分配的字节数都是兼容的,即不同平台对于同一个类型分配相同的字节数!!

建议:在代码中尽量避免使用int类型,根据不同的需要可以用short,long,unsigned int 等代替。

下面是各个类型一览表【转】

64位指的是cpu通用寄存器的数据宽度是64位的。

数据类型名称字节数别名取值范围
int*signed,signed int操作系统决定,即与操作系统的"字长"有关
unsigned int*unsigned由操作系统决定,即与操作系统的"字长"有关
__int81char,signed char–128 到 127
__int162short,short int,signed short int–32,768 到 32,767
__int324signed,signed int–2,147,483,648 到 2,147,483,647
__int648–9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
bool1false 或 true
char1signed char–128 到 127
unsigned char10 到 255
short2short int,signed short int–32,768 到 32,767
unsigned short2unsigned short int0 到 65,535
long4long int,signed long int–2,147,483,648 到 2,147,483,647
long long8none (but equivalent to __int64)–9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long4unsigned long int0 到 4,294,967,295
enum*由操作系统决定,即与操作系统的"字长"有关
float43.4E +/- 38 (7 digits)
double81.7E +/- 308 (15 digits)
long double81.7E +/- 308 (15 digits)
wchar_t2__wchar_t0 到 65,535
类型标识符类型说明长度
(字节)
范围备注
char字符型1-128 ~ 127-27 ~ (27 -1)
unsigned char无符字符型10 ~ 2550 ~ (28 -1)
short int短整型2-32768 ~ 327672-15 ~ (215 - 1)
unsigned short int无符短整型20 ~ 655350 ~ (216 - 1)
int整型4-2147483648 ~ 2147483647-231 ~ (231 - 1)
unsigned int无符整型40 ~ 42949672950 ~ (232-1)
float实型(单精度)41.18*10-38 ~ 3.40*10387位有效位
double实型(双精度)82.23*10-308 ~ 1.79*1030815位有效位
long double实型(长双精度)103.37*10-4932 ~ 1.18*10493219位有效位


发表于 2011-04-15 14:02 阿π 阅读(1812) 评论(4)  编辑 收藏 引用 所属分类: 其它
 
评论
# re: c++ 中关于int,unsigned int , short的跨平台移植
C99应该用int**_t
空明流转 评论于 2011-04-15 16:14  回复  更多评论    
# re: c++ 中关于int,unsigned int , short的跨平台移植
我也做过移植.
影响中16位平台,多用C来开来,多是嵌入式开发.
32、64位在PC、服务器级较多,目前16位已很少.
我个人认为int型相于对long类型要安全.
long类型在windows-64下,仍是32字节;
但在LINUX-64下long和指针是相当的,已升级到了64字节,
如果结构体中使用long,结果大小有变,windows64下做的资源在linux下64处理,会有问题,
最常见的资源头大小就一致.
如果是大型项目,还是建立自己的一套低层基本数据类型封装方为上策.
bbxyard 评论于 2011-04-15 23:33  回复  更多评论    
# re: c++ 中关于int,unsigned int , short的跨平台移植[未登录]
lz说的不对,应该用int8_t, int16_t,int32_t, int64_t, uintXX_t 等等。而以两个下划线开头的那种类型是微软自己的东西,是不可以移植的。
Alex 评论于 2011-04-16 23:52  回复  更多评论    
# re: c++ 中关于int,unsigned int , short的跨平台移植
原来这么复杂啊,分析的好详细,顶!
free keylogger 评论于 2011-09-08 22:46  回复  更多评论    
 

============================================================

C/C++ 32位机器和64位机器 差异问题总结 跨平台 移植问题 语言编程需要注意的64位和32机器的区别  

2011-09-28 22:39:45|  分类: C++ |  标签:c++  64位机  数据类型   |字号 订阅

转载自:http://hi.baidu.com/jrckkyy/blog/item/61d7869b264d64aec8eaf411.html

#include <stddef.h>

OS version:Red Hat Enterprise Linux Server release 5.3 (Tikanga) Linux 2.6.18-128.el5 #1 SMP Wed Dec 17 11:41:38 EST 2008 x86_64 x86_64 x86_64 GNU/Linux

size_t本身一个作用就是避免考虑64还是32。64位下Long和指针是64位的

size_tm_unNo;

sprintf(path,"%u",m_unNo); //这句在32位机器上正常 64位机器上会编译警告:“警告:格式 ‘%u’ 需要类型 ‘unsigned int’,但实参 4 的类型为 ‘size_t’”

%u 对应 unsigned int在64位机器上还是32位,而size_t已经变成64位了。

char* 指针在64位下是64位

m_pMem = new char[nSize];
int off = (int)m_pMem%nAlign; // 在 32位编译正常,在64位机器上编译报错:“ 错误:从 ‘char*’ 到 ‘int’ 的转换损失精度”

改为就可以达到兼容效果了int off = (uint64_t)m_pMem%nAlign; // 因为int在64位下仍为32位,char×已经变位64位了。

 

 

 

 

一、数据类型特别是int相关的类型在不同位数机器的平台下长度不同。C99标准并不规定具体数据类型的长度大小,只规定级别。作下比较:

16位平台

char         1个字节8位

short        2个字节16位

int            2个字节16位

long         4个字节32位

指针         2个字节

32位平台

char         1个字节8位

short        2个字节16位

int            4个字节32位

long         4个字节

long long 8个字节

指针         4个字节

64位平台

char         1个字节

short        2个字节

int            4个字节

long         8个字节(区别)

long long 8个字节

指针        8个字节(区别)

二、编程注意事项

为了保证平台的通用性,程序中尽量不要使用long数据库型。可以使用固定大小的数据类型宏定义:

typedef signed char       int8_t

typedef short int             int16_t;

typedef int                      int32_t;

# if __WORDSIZE == 64
typedef long int              int64_t;
# else
__extension__
typedef long long int      int64_t;

#endif

三、使用int时也可以使用intptr_t来保证平台的通用性,它在不同的平台上编译时长度不同,但都是标准的平台长度,比如64位机器它的长度就是8字节,32位机器它的长度是4字节,定义如下:

#if __WORDSIZE == 64
typedef long int                intptr_t;
#else
typedef int                        intptr_t;
#endif
编程中要尽量使用sizeof来计算数据类型的大小

以上类型定义都有相应的无符号类型。

另 外还有ssize_t和size_t分别是unsigned和signed size of computer word size。它们也是表示计算机的字长,在32位机器上是int型,在64位机器上long型,从某种意义上来说它们等同于intptr_t和 uintptr_t。它们在stddef.h里面定义。需要注意的是socket的accept函数在有些操作系统上使用size_t是不正确的,因为 accept接收的int*类型,而size_t可能是long int 类型。后来BSD使用sock_t来替代它。

posted @ 2012-05-15 17:00 多彩人生 阅读(1490) | 评论 (0)编辑 收藏

编写跨平台的软件入门——有关字节对齐

一,             为什么要跨平台?

你想过把你的 Windows 上编写的程序在 Linux 编译运行吗,以及在 Mac 或其他 OS 上运行等等?反过来也一样?这就需要涉及到跨平台编程知识。这里需要注意的是,平时很多在一个平台运行的程序在跨平台的时候变的不再正确。

Java 并非真的是跨平台的开发环境,它是运行在它自己的平台上。这里主要关注 C 和 C++ 的跨平台开发。

下面主要就几个方面来讨论跨平台编程的注意事项:

1.  字节序

2.  字节填充

3.  其他

二,             字节序

大家都知道计算机使用两种字节序,一种是 little-endian ,另一种是 big-endian 。这主要是由于当前流行的 CPU 之间的差异造成的,基本上是 IBM-PowerPC 使用的大序,而其他 CPU 使用的小序。

这里先来介绍一下 little-endian 和 big-endian 之间的具体差异。

X86 指 令集合使用小序( little-endian )字节顺序;这就意味着多个字节值的最重要字节在地址的最低位。小序很早就使用,因为硬件容易实现,但和今天的制造商技术有点不同;但在第一代 IBM PC 机的 Vaxen 和 8086 处理器使用是它如此流行的主要原因。

看一个例子:

short example[2] = {0x0001,0x0302};

 

按照 16 进制的形式来显示上面数据在内存中的存储方式:

01 00 02 03

我们看到对于数组的第一个元素,高 8 位应该是 0 ,而最终存储的时候是在低 8 位的后面。

而 另一方面 PowerPC 和 Sparc 芯片是 big-endian 的,也就是说,最重要的字节存储在较低的地址。对于 CPU 需要额外的电路实现这个功能,但对于今天的处理器技术与缓存控制技术相比较显的微不足道。使用 BIG-ENDIAN 的最大好处是在使用低级调式器时比较容易理解数据的存储,同样对于文件十六进制 DUMP 或网络 Sniffer 显示也是一样的。

对于 BIG-ENDIAN ,上面的例子中内存如下表示:

00 01 03 02

这里需要注意的是:由于 BIG-ENDIAN 格式的 RAW 数据比较容易调式,如果我们有机会设计一个新的文件格式,那么使用 BIG-ENDIAN 格式,而不是根据 CPU 架构来决定。

下面看几个关于字节序的问题:

1.  Long 型指针和 char 指针之间的转换

看下面这段代码

unsigned long value = 0x03020100;

unsigned long *ptr = &value;

unsigned char charVal;

charVal = *(unsigned char *)ptr;

程序的含义比较简单,主要是从一个指向 long 的指针强制转换为一个指向 char 的指针,这里假设指针指向的是最不重要的字节地址。

在一个 little-endian 处理器上, charVal 是 0 ,而在一个 big-endian 处理器上, charVal 的值是 3 。这样的问题是最难以发现的问题之一。

为了避免这个错误,使用一个临时变量可以解决这个问题,如下:

unsigned long temp = *ptr;

charVal = (unsigned char)temp;

上面的第二行代码就保证将在任何架构上都将最不重要的字节传递给 charVal ;编译器处理具体的细节。

2.  读写文件和写网络数据

在从文件读数据或写数据到文件的时候以及网络,对于字节顺序的处理一定要小心;一定记住不能将多个字节的数据写到文件或网络上;例如:

long val = 1;

int result = write(fileDes,&val,sizeof(val));

这段代码在 little-endian 和 big-endian 机器上执行的结果是不一样的,如果读数据的时候使用如下代码:

long val ;

int result = read(fileDes,&val,sizeof(long));

如果这两段代码分别位于 little-endian 和 big-endian 机器上,那么最终得到的 val 不是 1 ,而是 0x01000000 。

解决多字节的读写有很多办法,这里提供两种。

方法 1 :

写的代码

long val = 1;

char buf[4];

buf[0] = 0xff&val;

buf[1] = (0xff00&val)>>8;

buf[2] = (0xff0000&val)>>16;

buf[3] = (0xff000000&val)>>24;

int result = write(fileDes,buf,4);

读的代码

long val;

char buf[4];

int result = read(fileDes,buf,4);

val = buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24);

3.  运行时检查字节顺序

bool gIsBigEndian;

void InitializeEndianFlag()

{

Short one = 1;

Char *cp = (char *)&one;

If(*cp == 0)

    gIsBigEndian = true;

else

    gIsBigEndian = false;

return ;

}

4.  字节交换对性能的影响

由于字节顺序的问题导致在处理的时候需要进行字节交换或类似 2 中方法 1 的处理,这里称为交换。通常情况下,做字节顺序的交换并不影响,因为交换两个字节或四个字节值只需要很少的 CPU 指令,并且完全可以在寄存器中执行。

但如果有很多数据需要交换,例如:一个 1024*768 位图的图像,在这么大的循环中执行是影响性能的。

另外对于 3 的运行时检查字节序的代码要查看具体的位置。如果仅仅调用一次或几次,不会影响性能,如果对于上面的这个循环中调用,对性能的影响是显著的,这个时候可以使用一个预编译宏来分别处理。例如:

#ifdef BIG_ENDIAN//big-endian

#else//little-endian

#endif//BIG_ENDIAN

 

三,             字节填充

另一个写可移植代码的注意点是结构体的字节对齐和填充。通常,在单个平台上,如果需要保存一个结构体到文件,那么是作为一个整体写到文件的,如下:

struct myStruct{

char theChar;

long theLong;

};

struct myStruct foo;

foo.the Char = 1;

foo.theLong = 2;

如果我们已经将数据按照 big-endian 进行了交换,然后直接将整个结构体写到文件中。那么什么样的数据会被写到磁盘上呢?

int result = write(fileDes, &foo, sizeof(foo));

实际上我们不知道具体写了什么数据,因为我们还不知道这个代码在什么平台上运行;实际上上面的 code 中会将垃圾数据写到文件里,垃圾数据多少由 foo 分配到的内存决定的。

一种可能我们认为的情况是:

 01 00 00 00 02

但我们可能得到的这样的数据:

01 f 8 00 00 00 02

甚至是:

01 e6 a7 20 00 00 00 02

这里到底发生了什么? sizeof(foo) 是编译器和处理器依赖的。

有 些处理器不能从某些位置读或写多个字节;几乎所有的都不能从奇数地址来读数据。通常他们只读那些是 sizeof ( value )倍数的地址;对于四个字节只能读地址是 4 个字节的倍数,对于 2 个字节的 short 只能读两个字节倍数的地址。如果不遵从这个字节对齐的规律,处理器会抛出一个异常并且终止程序,有些系统上会锁定机器(如果发生在 kernel 中)。

有 时,读没有对齐的数据需要花费额外的时间。例如: PowerPC 能够读任何偶数地址,但对于那些不能被 4 整除的地址需要耗费额外的总线周期。为了读一个 long 数值( value )在 2 整除而不是 4 整除的地址,它将读四个字节并包括需要读的值的上面两个字节,抛弃 2 个字节,然后读另外四个包含 value 低 2 个字节的字节,同样抛弃另外两个。这与读 4 个字节对齐的地址相比需要多访问一次缓存。

为了达到字节对齐的目的,编译器会插入未命名的填充字节到结构体中。至于插入几个字节是通过编译器和 OS 或库内存分配器一起决定的。

在 Windows VC 编译器中,可以使用 #pragma 来指定字节对齐的方式。

总而言之,在定义结构的时候要按照字节边界对齐来定义,一般按照 4 个字节,如果不够就需要增加填充字段。

另外对于结构体写文件或输出到网络上,最好的办法是按照成员来逐个写入或发送,这可以避免将垃圾数据存放到文件中或传输到网络上。

 

四,             其他

下面是几个笔者在实际编写代码中发生过的错误,这里与大家一道分析一下。

1.         示例 1 :

for(int i = 0;i<1000;i++)

{

   ….

}

...

for(int i = 0;i<1000;i++)

{

...

}

上面这段代码是很普通的 C++ 代码,但这段代码不一定可以在所有的编译器中都能编译通过。主要的原因在于变量 i 的声明。

C++ 标 准说:在 for 循环内部声明的变量在 for 结束的时候无效,因此可以连续使用再次在 for 循环中使用该记数器变量。但很不幸的是很多编译器都提供编译选项来让你觉得变量是否在 for 循环以后仍然有效。 VC 中默认编译选项 /Ze 用来指定 for 循环变量的局部性,但并非所有的编译器都是将这个选项作为默认编译参数;所以为了能让你的代码可以在任意平台编译通过,使用 C 风格的会有保证一点;如下:

int i = 0;

for(i = 0;i<1000;i++)

{

   ….

}

...

for(i = 0;i<1000;i++)

{

...

}

 

2.         示例 2 : int 型变量的使用

Int 型变量是一个奇怪的东西,它在 16 位机器上是 2 个字节,在 32 位机上是 4 个字节;将来可能在 64 位机上是 8 个字节。所以如果你的代码中有对 int 的使用,而你想代码可以在很多平台上运行,那么一定要注意了。看一下下面的情况:

for(int i = 0;i<65540;i++)

{

   ….

}

这个代码可能在不同的平台上得到不同的结果。如果这个代码是在 16 位机器上运行,那么得到的结果与 32 位机器上可能不同。

同样在使用 int 型变量写文件和输出到网络时都要小心这个问题。最好的办法是,在这些情况下不要使用 int 型变量; int 型变量仅仅在程序内部使用。

3.         关于 Bit field 的问题

在 C 语法中有 bit field 的语法,可以根据需要来定义一个符号具体占用的 bit 数,例如:

typedef struct tagTest
{
   char a:4;
   char b:2;
   char c:2;
}TagTest,*PTagTest;

实际上 tagTest 的字节数是 1 个字节,成员 a 占用 4 位, b 和各占用两位。这样的好处是可以针对每个成员赋值而设置指定的位的值,例如:

tagTest myTest;
myTest.a = 10;
myTest.b = 2;
myTest.c = 1;

假如你在 Windows 上是使用 VC 来编译连接上面的程序,不管如何处理,你不会发生任何问题。但现在我们假设将 myTest 放入缓冲区中,然后在 MAC 机器上取出来,那么会发生什么来?看代码:

Windows:

char buf[10];

buf[0] = myTest;

buf[2]=...

int result = send(fd,buf,10,..);

 

MAC:

char buf[10];

int ret = 0;

int result = recv(fd,buf,10,..);

PTagTest pTest = (PTagTest)&buf[0];

 

if(pTest->a == 10)

   ret = 1;

else

    ret = 0;

...

那么 ret 的值是什么呢?我们期望是 1 但,结果不是 1 。如果你通过调试器来观察一下 pTest 各成员的值你发现:

pTest->a = 6; pTest->b =2 ; pTest->c =2;

细 心的读者可能发现这里的问题所在,原因在于不同的编译器对 bit field 进行了不同的处理。在 Windows 平台上, c 被放在字节的最高两位,而 a 被放在字节的最低 4 位,在 MAC 上正好相反。但一定要注意,这是编译器行为,而不是数据在传输过程中发生了字节的位交换。在 Windows 发送到网络的时候, buf[0] 的内容二进制表示为:

01 10 1010

在 MAC 上 recv 之后, buf[0] 的内容仍然与上面的相同。

为了避免这个问题,请不要在写文件或网络输出的时候使用 BIT FILED 语法,如果一定要使用请注意编译器对位处理的区别。

n         五 小结

其 实实际工作中,大家认为自己的代码都不需要在多个平台上运行,而认为跨平台编码与自己无关;其实不然,好的编码习惯是慢慢养成的,如果大家都知道这些跨平 台编码的细节,在开始写代码的时候就开始避免这样的问题,一旦有一天我们的代码需要跨平台运行或一点我们要写跨平台代码时,我们就不会无从下手,而是顺其 自然,因为我们已经具备了这样的习惯。

当然这里的介绍只是一个开始,跨平台编码涉及的问题还很多,由于笔者经验的限制不能一一描述。


本文引用通告地址:http://lionwq.spaces.eepw.com.cn/articles/trackback/item/19180

posted @ 2012-05-15 16:21 多彩人生 阅读(492) | 评论 (0)编辑 收藏

Winscp脚本同步文件

# winscp.exe /console /script=sample.txt
# Automatically answer all prompts negatively not to stall
# the script on errors
# option echo  on|off
option echo off
# option batch on|off|abort|continue
option batch on
# option confirm  on|off
option confirm off
# option transfer  binary|ascii|automatic
# option synchdelete  on|off
# option exclude clear | <mask>[;<mask2>...]
# option include clear | <mask>[;<mask2>...]
# open [ sftp|ftp|scp:// ][ <user> [ :password ] @ ] <host> [ :<port> ]
# open user:password@example.com
# Connect  
open  USER:PASS@IP:PORT
# Change remote directory
# cd /home/user              #远程工作目录
cd REMOTE PATH
# Change local directory
# set to Self's working dir 
lcd LOCAL PATH                   #本地工作目录
# Force binary mode transfer
option transfer binary
# Download file to the local directory d:\
# get examplefile.txt d:\
# option synchdelete  on|off
option synchdelete off       #是否同步删除  
# option include clear | <mask>[;<mask2>...]
# option include /2008-*-*/;/2009-*-*/;/2010-*-*/;/2011-*-*/;/2012-*-*/;/2013-*-*/
# synchronize local|remote|both [ <local directory> [ <remote directory> ] ] 
# 传输方式:Local为远程至本地,remote为本地到远程,both双向传输
synchronize local  
# Disconnect
close
# Exit WinSCP
exit


======================================================
#清除上面的注释和个人理解了一下
option echo off
option batch on
option confirm off
option transfer binary
option synchdelete off
#只同步 *.h *.hpp *.cpp, 清除mask用命令:option include clear  
option include "*.h; *.hpp; *.cpp; */"
open scp://zc:123@192.168.1.128

#请确保/home/zc/project/doloio 已存在
cd /home/zc/project/doloio
lcd E:\zc\test\doloio
synchronize remote

close
exit

posted @ 2012-05-15 10:57 多彩人生 阅读(992) | 评论 (0)编辑 收藏

关于boost::thread 的一件怪事

本想测试printf的线程安全的,结果发现boost::thread的一件有趣的事,见代码
#include <cstdio>
#include 
<boost/thread.hpp>
#include 
<boost/bind.hpp>

int fun1()
{
   
for(int i=0; i<10; i++)
      printf(
"哈哈哈哈哈哈\n");
   
return 0;
}

int fun2(int count)
{
   
for(int i=0; i<count; i++)
      printf(
"呵呵呵呵呵呵\n");
   
return 0;
}

int main()
{
   boost::thread(fun1);     
// 不以打印
   boost::thread(fun2, 10); // 可以打印
   boost::thread t(fun1);   // 可以打印

   getchar();
   
return 0;
}
不知什么原因,知道的可以告诉我吗

posted @ 2012-05-14 21:50 多彩人生 阅读(322) | 评论 (0)编辑 收藏

too many files open

在ubuntu上开了服务端,在window上开了2000个连接去连server. 结果提示 too many files open错误:
解决方法是: ulimit -n 4096
{
  个人体会:注意,一定要在同一个terminal 下运行这个命令
   很容易找到这个解决方法,于是我打开了另一个terminal, 输入了这个命令,哈哈,结果当然还是不行,原来ulimit -n 4096只是改变当前terminal的  limit
   Note this has no much relation to system max files (/proc/sys/fs/file-max).
}

网上相关资料
-----------------------------------------------------------------------------------------------

I had similar problem. Quick solution is :

ulimit -n 4096

explanation is as follows - each server connection is a file descriptor. In CentOS, Redhat and Fedora, probably others, file user limit is 1024 - no idea why. It can be easily seen when you type: ulimit -n

Note this has no much relation to system max files (/proc/sys/fs/file-max).

In my case it was problem with Redis, so I did:

ulimit -n 4096 redis-server -c xxxx

in your case instead of redis, you need to start your server.

----------------------------------------------------------------------------------------------------

linux下Too many open files问题

2011-01-21 14:42:13
执行:ulimit -n
应该会显示默认值1024
执行:ulimit -n 8192
但是ulimit直接修改的数字,在系统重启后会重置,所以还要修改:
1、/etc/security/limits.conf,在里面加上:
* soft nofile 8192
* hard nofile 20480
2、/etc/pam.d/login,里面加上:
session required /lib/security/pam_limits.so

----------------------------------------------------------------------------------------------------
ps:这个有好多知识点可以学
分类: AIX


登陆数据库出现错误:ORA-12537: TNS:connection closed
于是登陆操作系统
[root@CCN-BJ-3-578 ~]# su - oracle
su: /bin/bash: Too many open files in system
[root@CCN-BJ-3-578 ~]#
检查报警日志没有发现任何错误。
[root@CCN-BJ-3-578 .ssh]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
pending signals                 (-i) 1024
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 73728
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@CCN-BJ-3-578 .ssh]#
查看最大可以打开的文件数量
[root@CCN-BJ-3-578 fs]# cat /proc/sys/fs/file-max
65536
查看各个进程打开的文件数据量
[root@CCN-BJ-3-578 home]# lsof -n |awk '{print $2 " " $3}'|sort|uniq -c |sort -nr|more
  64387 18885 oracle
     56 2669 oracle
     53 2667 oracle
     50 2693 oracle
     。。。
18885 明显异常

[root@CCN-BJ-3-578 ~]# ps -ef|grep 18885
oracle   18885     1  0 Jan24 ?        00:00:19 ./linux
root     10241 10049  0 13:45 pts/2    00:00:00 grep 18885
发现oracle用户在执行一个linux文件
查找该文件
[root@CCN-BJ-3-578 home]# find / -name linux
/u01/app/oracle/product/10.2.0/db_1/jdk/include/linux
/u01/app/oracle/product/10.2.0/db_1/oui/lib/linux
/usr/java/jdk1.5.0_11/sample/jnlp/jreinstaller/build/linux
/usr/java/jdk1.5.0_11/include/linux
/usr/java/jdk1.5.0_11/demo/jvmti/hprof/src/linux
/usr/X11R6/lib/Server/include/linux
/usr/X11R6/lib/Server/modules/linux
/usr/X11R6/lib/Server/modules/drivers/linux
/usr/X11R6/lib/Server/modules/input/linux
/usr/X11R6/lib/modules/linux
/usr/X11R6/lib/modules/drivers/linux
/usr/X11R6/lib/modules/input/linux
/usr/share/terminfo/l/linux
/usr/share/doc/ntp-4.2.0.a.20040617/build/hints/linux
/usr/share/systemtap/runtime/relayfs/linux
/usr/include/linux
/usr/src/kernels/2.6.9-22.EL-hugemem-i686/include/linux
/usr/src/kernels/2.6.9-22.EL-hugemem-i686/include/config/logo/linux
/usr/src/kernels/2.6.9-22.EL-i686/include/linux
/usr/src/kernels/2.6.9-22.EL-i686/include/config/logo/linux
/usr/src/kernels/2.6.9-22.EL-smp-i686/include/linux
/usr/src/kernels/2.6.9-22.EL-smp-i686/include/config/logo/linux
/usr/lib/perl5/5.8.5/i386-linux-thread-multi/linux
/usr/lib/bcc/include/linux
/usr/lib/dietlibc/include/linux
/home/oracle/.ssh/ /linux
由于其它都是root用户的,可以确定是/home/oracle/.ssh/ /linux正在执行
[root@CCN-BJ-3-578 ~]# cd /home/oracle/.ssh/
[root@CCN-BJ-3-578 .ssh]# cd " "
[root@CCN-BJ-3-578  ]# pwd
/home/oracle/.ssh/
[root@CCN-BJ-3-578  ]# ll
总用量 1572
-rwxr-xr-x  1 oracle dba    188  1月 24 22:46 2
-rwxr-xr-x  1 oracle dba    188  1月 24 22:46 3
-rwxr-xr-x  1 oracle dba    188  1月 24 22:46 4
-rwxr-xr-x  1 oracle dba   2467 2005-02-21  auto
-rwxr-xr-x  1 oracle dba    182  1月 24 22:46 born___.seen
-rwxr-xr-x  1 oracle dba    182  1月 24 22:46 born__.seen
-rwxr-xr-x  1 oracle dba     58  1月 24 22:46 born_.seen
-rwxr-xr-x  1 oracle dba 463188 2004-10-17  darwin
-rwxr-xr-x  1 oracle dba 582960  1月 24 22:46 freebsd
-rwxr-xr-x  1 oracle dba     34  1月 24 22:46 LinkEvents
-rwxr-xr-x  1 oracle dba 497561 2005-02-21  linux
-rwxr-xr-x  1 oracle dba      0  1月 25 13:00 mech.levels
-rwxr-xr-x  1 oracle dba      6  1月 24 22:46 mech.pid
-rw-r--r--  1 oracle dba      0  1月 25 13:00 mech.session
-rwxr-xr-x  1 oracle dba   3669  1月 19 13:23 mech.set
drwxr-xr-x  2 oracle dba   4096  1月 24 22:44 randfiles
-rwxr-xr-x  1 oracle dba      0  1月 19 13:10 TiGeR02.seen
-rwxr-xr-x  1 oracle dba      0  1月 19 13:10 TiGeR03.seen
[root@CCN-BJ-3-578  ]#

问题原因找到。


posted @ 2012-05-11 14:15 多彩人生 阅读(800) | 评论 (0)编辑 收藏

ctags

安装ctags

sudo apt-get install ctags 

-------------------------------------------------------------------------------

生成某项目的tags文件

cd 项目目录

ctags –R    // "-R"表示递归创建,也就包括源代码根目录下的所有子目录下的源程序。

"tags"文件中包括这些对象的列表:
l 用#define定义的宏
l 枚举型变量的值
l 函数的定义、原型和声明
l 名字空间(namespace)
l 类型定义(typedefs)
l 变量(包括定义和声明)
l 类(class)、结构(struct)、枚举类型(enum)和联合(union)
l 类、结构和联合中成员变量或函数
******注意:运行vim的时候,必须在"tags"文件所在的目录下运行。否则,运行vim的时候还要用":set tags="命令设定tags文件的路径,
这样vim才能找到"tags"文件 :set tags=/home/xxxxx/tags 在vim中输入命令导入tags文件

-------------------------------------------------------------------------------------

使用三种定位方法

1) 用命令行。在运行vim的时候加上"-t"参数,例如: [/home/brimmer/src]$ vim -t foo_bar 这个命令将打开定义"foo_bar"(变量或函数或其它) 的文件,并把光标定位到这一行。

2) 在vim编辑器内用":ta"命令,例如: :ta foo_bar

3) 最方便的方法是把光标移到变量名或函数名上,然后按下"Ctrl-]"。用"Ctrl-t"退回原来的地方

posted @ 2012-05-10 20:38 多彩人生 阅读(569) | 评论 (0)编辑 收藏

16个桌面Linux用户必须要知道的Shell命令

16个桌面Linux用户必须要知道的Shell命令

94人收藏此文章, 我要收藏 发表于昨天(13:19) , 已有3773次阅读 共10个评论
    有些人仍然会有这中愚蠢的想法,他们认为使用Linux就必须使用Linux shell命令。胡说!你可以不懂得任何Linux命令,比如说ps,grep,ls等,但是你仍然可以使用很多现代的Linux桌面发行版。
    Linux的系统管理员与桌面用户不一样,他们不像桌面用户一样使用Cinnamon, GNOME, Unity, 或者 KDE,他们所有的时间都是用Linux命令。

    对于桌面用户来讲,若是了解一部分Linux命令,你可以更好的使用Linux,体验它的魅力,下面列举出了一些:

Shell 基础:

    你可以通过打开Linux的terminal(终端)来执行Shell命令。Shell的种类有很多种,例如CSH,Bourne Shell,Korn Shell。在现在的大多数Linux发行版中,默认的Shell一般都是Bourne again shell(bash).

想看看你的Shell是哪一种,执行下面的命令

1 echo $SHELL
在Linux中,$符号代表一个shell 变量。所有的shell都用这种方式使用变量。有一些shell变量在你的系统启动的时候就有了默认值。例如,$SHELL;$LOGNAME是你的登录名,而$PATH变量指明了你的shell命令的搜索范围。
echo命令的作用就是打印出你的输入。如果你的输入具有shell的特殊意义,例如shell变量,他就输出变量的值。

 

Echo.png

    一个重要的地方是,你要注意文本的大小写。例如,ls,是DOS的dir命令的Linux版本。这个命令列出当前工作目录下的文件列表。如果你输入的是LS,你得到的只能是“找不到命令”的错误信息。
    另外在Linux shell命令中一个重要的地方是,你可以将命令串起来。这是Unix/Linux从第一天开始就有的巧妙的特点。最简单的将命令连起来的办法就是使用“|”,我们称之为“pipe”。第一个命令的输出就是下一个命令的输入。
Linux命令有自己的语法规则:
基本的语法就像这样:
1 command -option file
例如:
这行命令的意义是输出当前目录的所有文件的文件名,l代表“long”,a代表“all”,有了l选项,你会发现,输出的内容比较丰富,不只包括文件名,还有文件的访问权限,所有者,所属组等。你会发现这个命令会在屏幕上输出大量的信息,如果当前目录的文件比较多的话。
现在就是“pipe”出场的时候了。
1 ls -la | more

你会在屏幕上看到如下信息:

ls-la.png

    你也可以在大多数Linux命令中使用通配符。通配符就是可以代表文件名中任何未知的字符或字符串。例如,*就代表任意字符串,?代表单个字符。例如:
1 ls -l a*
这个命令会列出在当前工作目录下所有的以小写a开头的文件,比如说abc.txt,alpha.jpg等等。
1 ls a?cd

    这条命令会列出所有以小写a开头隔一个未知字符以小写cd结尾的所有文件。例如adcd,axcd,但是不会列出adfdcd,也不会列出axcd.txt。

一些常用的命令

man:如果你想了解每一个命令代表什么含义以及他的用法,你就可以使用man(意义为manual)命令,例如:
    man输出的内容是为系统管理员和开发者编写的,如果你想了解更多命令的用法,你可以去找网络上的Linux命令文档,有一个非常方便的就是丹尼尔·巴雷特的Linux的袖珍指南。
在以前如果你真的想学习Linux和Unix你必须阅读man手册。而在2012年的今天,面对如此好用的图形界面,这句话显得不是那么的重要,但是如果你想了解Linux更深,阅读man手册仍然是一个好的开始。
su 和sudo: su命令的作用是切换用户,这也被称为超级用户,因为在有些系统中su命令可以使你以系统的所有权限用户root登录。除非你是系统管理员,否则我绝不推荐你使用su切换到root,因为这可能给你带来很多麻烦。
一个相对安全的多的办法是使用sudo命令,这个命令可以上你以root权限运行一个命令。
这两个命令都需要系统密码。在大多数Linux的桌面发行版中这两个是相同的,就是你系统的第一个用户设置的密码。
grep:grep是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。 例如:
1 grep foo /etc/passwd
返回在password文件中所有的含有foo的行
1 grep -i "foo" /etc/passwd
-i选项是忽略大小写,这就意味着若某一行含有FOO或FOo也会被搜索到。
grep还支持递归搜索:
1 grep -r "foo" /home/sjvn
这条命令将会搜索/home/sjvn目录及其子目录下所有的包含“foo”的行
grep也通常与其他命令串连起来使用,例如:
1 ls -la | grep foo*
列出当前目录下任何以foo开头的文件或目录。例如如下文件将被匹配:foo,foobar,foolish.txt等等。
ps:报告进程的状态。此命令将显示哪些程序正在运行。我使用PS和grep比其他任何命令使用的都要多。 
现在假如你有一个程序运行出了错误,你不能再桌面上把他关掉,(我看准你了,火狐),我运行如下命令就能查看他的进程id:
1 ps -ef | grep firefox
这条命令做了如下事情:
1 kill -9 1234
·第一,他找出所有当前在我的电脑上运行的程序
·然后grep命令找出这些文件中叫“firefox”的,显示到屏幕上。
·每一行都有一个进程id数字。有了,现在我就可以是用...
kill:这条命令就想他的名字一样,他可以干掉指定的进程。干掉运行错误的firefox或者其他我想干掉的进程。
1 kill 1234
这将会干掉所有进程id为1234的程序。为了确保这个程序已经被干掉(强行终止),我使用下面的命令

clear:  如果你觉得你的屏幕上有太多的东西,看起来很讨厌,可以使用clear命令清空。

文件/目录命令

cp:cp代表copy,就是复制,他可以复制一个或多个文件到指定的一个或多个目录下。
一个通常的用法是:
1 cp fred.txt ethel.txt
把fred.txt中的内容复制到当前文件夹中名为ethel.txt中
1 cp fred.txt /home/sjvn/docs/fred.txt
把fred.txt复制到指定文件中。
1 cp *.txt  /home/sjvn/docs/
复制当前目录下所有以.txt结尾的文件到指定的目录下。
1 cp -r /home/sjvn/docs/* /home/sjvn/backup
递归的复制在/home/sjvn/docs/目录下的所有文件到指定的文件夹。
hostname: 显示你当前登录进去的计算机的名称。
mv:mv(意义为move)顾名思义就是移动,可以移动指定文件到指定位置。当然这个命令有的时候还可以用来实现重命名。
例如:
1 mv fred.txt ethel.txt
这条命令的作用你可以简单的认为是重命名fred.txt为ethel.txt。
当然此命令还有其他用法,此处不再一一罗列,用法与cp类似,只是这条命令不再保留原文件。
rm:rm代表remove,是删除的意思,所以我运行:
1 rm fred.txt
我将删除fred.txt。
我强烈推荐你在执行rm命令的时候使用-i选项。这儿选项将在你执行命令的时候询问你是否要真的删除文件。就像这样:
1 rm -i fred.txt

系统信息

uname: uname -a命令用一行简短的给你总结的电脑的基本信息。这通常包括你的电脑名称,Linux内核版本,你的发行版名称等。

uname-a.png

对于更详细的信息,你可以使用cat命令,他可以显示你的Linux电脑中的文本信息。
1 cat /proc/cpuinfo
 展示你的CPU的重要的统计。
1 cat /proc/version
展示你的当前运行的Linux发行版的详细信息。
1 cat /etc/printcap
 展示你当前安装的打印机。
1 set | more: set|more
组合命令可以给你更多的呢当前的桌面环境变量的信息。单如果你只是想知道你系统环境变量中的某一个或两个,可以执行如下命令:
1 echo $PATH

结束或者说是另一个开始

   这里给你展示的只不过是Linux命令的表皮。成百上千的书和网站可以给你更多更复杂的如何使用Linux命令的展示。本文只是让你在想了解比Linux Gui更深一层时的参考。

如果你想学习更多的关于LInux,推荐你去LinuxCommand.org或者看一看Linux in a Nutshell。如果你想变成一个Linux shell编程的大事,推荐你去学习卡梅伦纽汉姆的 Learning the bash Shell 。

posted @ 2012-05-07 10:36 多彩人生 阅读(393) | 评论 (0)编辑 收藏

一个linux 博客

http://blog.chinaunix.net/uid/20754793/frmd/14822/page/1.html

posted @ 2012-05-07 09:56 多彩人生 阅读(294) | 评论 (0)编辑 收藏

Linux中时间度量

Linux中时间度量

一)ANSI clock函数


1)概述:
clock 函数的返回值类型是clock_t,它除以CLOCKS_PER_SEC来得出时间,一般用两次clock函数来计算进程自身运行的时间.

ANSI clock有三个问题:
1)如果超过一个小时,将要导致溢出.
2)函数clock没有考虑CPU被子进程使用的情况.
3)也不能区分用户空间和内核空间.

所以clock函数在linux系统上变得没有意义.

2)测试
编写test1.c程序,测试采用clock函数的输出与time程序的区别.

vi test1.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main( void )
{
   long i=1000L;
   clock_t start, finish;
   double  duration;
   printf( "Time to do %ld empty loops is ", i );
   start = clock();
   while (--i){
    system("cd");
   }
   finish = clock();
   duration = (double)(finish - start) / CLOCKS_PER_SEC;
   printf( "%f seconds\n", duration );
   return 0;
}

gcc test1.c -o test1

time ./test1
Time to do 1000 empty loops is 0.180000 seconds

real    0m3.492s
user    0m0.512s
sys     0m2.972s

3)总结:
(1)程序调用 system("cd");,这里主要是系统模式子进程的消耗,test1程序不能体现这一点.
(2)0.180000 seconds秒的消耗是两次clock()函数调用除以CLOCKS_PER_SEC.
(3)clock()函数返回值是一个相对时间,而不是绝对时间.
(4)CLOCKS_PER_SEC是系统定义的宏,由GNU标准库定义为1000000.

 


二)times()时间函数

1)概述:

原型如下:
clock_t times(struct tms *buf);

tms结构体如下:
strace tms{
 clock_t tms_utime;
 clock_t tms_stime;
 clock_t tms_cutime;
 clock_t tms_cstime;
}

注释:
tms_utime记录的是进程执行用户代码的时间.
tms_stime记录的是进程执行内核代码的时间.
tms_cutime记录的是子进程执行用户代码的时间.
tms_cstime记录的是子进程执行内核代码的时间.


2)测试:

vi test2.c
#include <sys/times.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

static void do_cmd(char *);
static void pr_times(clock_t, struct tms *, struct tms *);

int main(int argc, char *argv[]){
        int i;
        for(i=1; argv[i]!=NULL; i++){
                do_cmd(argv[i]);
        }
        exit(1);
}
static void do_cmd(char *cmd){
        struct tms tmsstart, tmsend;
        clock_t start, end;
        int status;
        if((start=times(&tmsstart))== -1)
                puts("times error");
        if((status=system(cmd))<0)
                puts("system error");
        if((end=times(&tmsend))== -1)
                puts("times error");
        pr_times(end-start, &tmsstart, &tmsend);
        exit(0);
}
static void pr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend){
        static long clktck=0;
        if(0 == clktck)
                if((clktck=sysconf(_SC_CLK_TCK))<0)
                           puts("sysconf err");
        printf("real:%7.2f\n", real/(double)clktck);
        printf("user-cpu:%7.2f\n", (tmsend->tms_utime - tmsstart->tms_utime)/(double)clktck);
        printf("system-cpu:%7.2f\n", (tmsend->tms_stime - tmsstart->tms_stime)/(double)clktck);
        printf("child-user-cpu:%7.2f\n", (tmsend->tms_cutime - tmsstart->tms_cutime)/(double)clktck);
        printf("child-system-cpu:%7.2f\n", (tmsend->tms_cstime - tmsstart->tms_cstime)/(double)clktck);
}

编译:
gcc test2.c -o test2

测试这个程序:
time ./test2 "dd if=/dev/zero f=/dev/null bs=1M count=10000"
10000+0 records in
10000+0 records out
10485760000 bytes (10 GB) copied, 4.93028 s, 2.1 GB/s
real:   4.94
user-cpu:   0.00
system-cpu:   0.00
child-user-cpu:   0.01
child-system-cpu:   4.82

real    0m4.943s
user    0m0.016s
sys     0m4.828s


3)总结:
(1)通过这个测试,系统的time程序与test2程序输出基本一致了.
(2)(double)clktck是通过clktck=sysconf(_SC_CLK_TCK)来取的,也就是要得到user-cpu所占用的时间, 就要用
(tmsend->tms_utime - tmsstart->tms_utime)/(double)clktck);
(3)clock_t times(struct tms *buf);返回值是过去一段时间内时钟嘀嗒的次数.
(4)times()函数返回值也是一个相对时间.

 

三)实时函数clock_gettime

在POSIX1003.1中增添了这个函数,它的原型如下:
int clock_gettime(clockid_t clk_id, struct timespec *tp);

它有以下的特点:
1)它也有一个时间结构体:timespec ,timespec计算时间次数的单位是十亿分之一秒.
strace timespec{
 time_t tv_sec;
 long tv_nsec;
}

2)clockid_t是确定哪个时钟类型.

CLOCK_REALTIME: 标准POSIX实时时钟
CLOCK_MONOTONIC: POSIX时钟,以恒定速率运行;不会复位和调整,它的取值和CLOCK_REALTIME是一样的.
CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID是CPU中的硬件计时器中实现的.


3)测试:
#include<time.h>
#include<stdio.h>
#include<stdlib.h>

#define MILLION 1000000


int main(void)
{
        long int loop = 1000;
        struct timespec tpstart;
        struct timespec tpend;
        long timedif;

        clock_gettime(CLOCK_MONOTONIC, &tpstart);

        while (--loop){
                system("cd");
        }

        clock_gettime(CLOCK_MONOTONIC, &tpend);
        timedif = MILLION*(tpend.tv_sec-tpstart.tv_sec)+(tpend.tv_nsec-tpstart.tv_nsec)/1000;
        fprintf(stdout, "it took %ld microseconds\n", timedif);

        return 0;
}

编译:
gcc test3.c -lrt -o test3

计算时间:
time ./test3
it took 3463843 microseconds

real    0m3.467s
user    0m0.512s
sys     0m2.936s

 


四)时间函数gettimeofday()

1)概述:
gettimeofday()可以获得当前系统的时间,是一个绝对值

原型如下:
int gettimeofday ( struct timeval * tv , struct timezone * tz )

timeval结型体的原型如下:
struct timeval {
               time_t      tv_sec;    
               suseconds_t tv_usec;   
           };

所以它可以精确到微秒


测试:
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
int
main(){
        int i=10000000;
        struct timeval tvs,tve;
        gettimeofday(&tvs,NULL);
        while (--i);
        gettimeofday(&tve,NULL);
        double span = tve.tv_sec-tvs.tv_sec + (tve.tv_usec-tvs.tv_usec)/1000000.0;
        printf("time: %.12f\n",span);
        return 0;
}

gcc test5.c
./a.out
time: 0.041239000000

 

五)四种时间函数的比较

1)精确度比较:

以下是各种精确度的类型转换:
1秒=1000毫秒(ms), 1毫秒=1/1000秒(s);
1秒=1000000 微秒(μs), 1微秒=1/1000000秒(s);
1秒=1000000000 纳秒(ns),1纳秒=1/1000000000秒(s);


2)
clock()函数的精确度是10毫秒(ms)
times()函数的精确度是10毫秒(ms)
gettimofday()函数的精确度是微秒(μs)
clock_gettime()函数的计量单位为十亿分之一,也就是纳秒(ns)


3)测试4种函数的精确度:

vi test4.c


#include    <stdio.h>
#include    <stdlib.h>
#include    <unistd.h>
#include    <time.h>
#include    <sys/times.h>
#include    <sys/time.h>
#define WAIT for(i=0;i<298765432;i++);
#define MILLION    1000000
    int
main ( int argc, char *argv[] )
{
    int i;
    long ttt;
    clock_t s,e;
    struct tms aaa;



    s=clock();
    WAIT;
    e=clock();
    printf("clock time : %.12f\n",(e-s)/(double)CLOCKS_PER_SEC);


    long tps = sysconf(_SC_CLK_TCK);
    s=times(&aaa);
    WAIT;
    e=times(&aaa);
    printf("times time : %.12f\n",(e-s)/(double)tps);


    struct timeval tvs,tve;
    gettimeofday(&tvs,NULL);
    WAIT;
    gettimeofday(&tve,NULL);
    double span = tve.tv_sec-tvs.tv_sec + (tve.tv_usec-tvs.tv_usec)/1000000.0;
    printf("gettimeofday time: %.12f\n",span);


    struct timespec tpstart;
    struct timespec tpend;

    clock_gettime(CLOCK_REALTIME, &tpstart);
    WAIT;
    clock_gettime(CLOCK_REALTIME, &tpend);
    double timedif = (tpend.tv_sec-tpstart.tv_sec)+(tpend.tv_nsec-tpstart.tv_nsec)/1000000000.0;
    printf("clock_gettime time: %.12f\n", timedif);

    return EXIT_SUCCESS;
}

gcc -lrt test4.c -o test4
debian:/tmp# ./test4
clock time : 1.190000000000
times time : 1.180000000000
gettimeofday time: 1.186477000000
clock_gettime time: 1.179271718000

 

六)内核时钟

默认的Linux时钟周期是100HZ,而现在最新的内核时钟周期默认为250HZ.
如何得到内核的时钟周期呢?
grep ^CONFIG_HZ /boot/config-2.6.26-1-xen-amd64

CONFIG_HZ_250=y
CONFIG_HZ=250

结果就是250HZ.

而用sysconf(_SC_CLK_TCK);得到的却是100HZ
例如:

#include    <stdio.h>
#include    <stdlib.h>
#include    <unistd.h>
#include    <time.h>
#include    <sys/times.h>
#include    <sys/time.h>

int
main ( int argc, char *argv[] )
{

    long tps = sysconf(_SC_CLK_TCK);
    printf("%ld\n", tps);
   
    return EXIT_SUCCESS;
}

为什么得到的是不同的值呢?
因为sysconf(_SC_CLK_TCK)和CONFIG_HZ所代表的意义是不同的.
sysconf(_SC_CLK_TCK)是GNU标准库的clock_t频率.
它的定义位置在:/usr/include/asm/param.h

例如:
#ifndef HZ
#define HZ 100
#endif

最后总结一下内核时间:
内核的标准时间是jiffy,一个jiffy就是一个内部时钟周期,而内部时钟周期是由250HZ的频率所产生中的,也就是一个时钟滴答,间隔时间是4毫 秒(ms).

也就是说:
1个jiffy=1个内部时钟周期=250HZ=1个时钟滴答=4毫秒

每经过一个时钟滴答就会调用一次时钟中断处理程序,处理程序用jiffy来累计时钟滴答数,每发生一次时钟中断就增1.
而每个中断之后,系统通过调度程序跟据时间片选择是否要进程继续运行,或让进程进入就绪状态.

最后需要说明的是每个操作系统的时钟滴答频率都是不一样的,LINUX可以选择(100,250,1000)HZ,而DOS的频率是55HZ.

 

七)为应用程序计时

用time程序可以监视任何命令或脚本占用CPU的情况.

1)bash内置命令time
例如:
time sleep 1

real    0m1.016s
user    0m0.000s
sys     0m0.004s


2)/usr/bin/time的一般命令行
例如:
\time sleep 1
0.00user 0.00system 0:01.01elapsed 0%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (1major+176minor)pagefaults 0swaps

注:
在命令前加上斜杠可以绕过内部命令.
/usr/bin/time还可以加上-v看到更具体的输出:
\time -v sleep 1
        Command being timed: "sleep 1"
        User time (seconds): 0.00
        System time (seconds): 0.00
        Percent of CPU this job got: 0%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.00
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 0
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 178
        Voluntary context switches: 2
        Involuntary context switches: 0
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
       
这里的输出更多来源于结构体rusage.

最后,我们看到real time大于user time和sys time的总和,这说明进程不是在系统调用中阻塞,就是得不到运行的机会.
而sleep()的运用,也说明了这一点

posted @ 2012-05-07 09:51 多彩人生 阅读(1556) | 评论 (0)编辑 收藏

仅列出标题
共25页: First 14 15 16 17 18 19 20 21 22 Last 

导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜