<?xml version="1.0" encoding="gb2312"?>

<!-- RSS generated by oioj.net on 4/16/2004 ; 感谢LeXRus提供 RSS 2.0 文档; 此文件可自由使用，但请保留此行信息 --> 
<!-- Source download URL: http://blogger.org.cn/blog/rss2.asp       -->
<rss version="2.0">

<channel>
<title>FoxWolf</title>
<link>http://blogger.org.cn/blog/blog.asp?name=FoxWolf</link>
<description>FoxWolf的博客</description>
<copyright>blogger.org.cn</copyright>
<generator>W3CHINA Blog</generator>
<webMaster>webmaster@blogger.org.cn</webMaster>
<item>
<title><![CDATA[计算当天是星期几]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37893</link>
<author>FoxWolf</author>
<pubDate>2008/7/19 12:53:32</pubDate>
<description><![CDATA[<P>原理：</P>
<P>蔡勒（Zeller）公式：w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 <BR><BR>公式中的符号含义如下，w：星期；c：世纪-1；y：年（两位数）；m：月（m大于等于3，小于等于14，即在蔡勒公式中，某年的1、2月要看作上一年的13、14月来计算，比如2003年1月1日要看作2002年的13月1日来计算）；d：日；[ ]代表取整，即只要整数部分。 <BR><BR>简单地说，c是年份的前两位，y是年份后两位，m是月份，d是日数。1月和2月要按上一年的13月和 14月来算，这时c和y均按上一年取值。) <BR><BR>算出来的w除以7，余数是几就是星期几。如果余数是0，则为星期日。 <BR><BR>以今天2006年7月21日为例，用蔡勒（Zeller）公式进行计算，过程如下： <BR>w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 <BR>=6+[6/4]+[20/4]-2×20+[26×(7+1)/10]+21-1 <BR>=6+[1.5]+5-40+[20.8]+21-1 <BR>=6+1+5-40+20+20 <BR>=12 (除以7余5) <BR>即2006年7月21日是星期5。 </P>
<P><BR>int get_Weekday(int Year,int Month,int Day)<BR>{<BR>&nbsp; int Weekday=0, Century=0;<BR>&nbsp; if (1 == Month ) {<BR>&nbsp;&nbsp;&nbsp; Month = 13;<BR>&nbsp;&nbsp;&nbsp; Year = Year -1;<BR>&nbsp; }<BR>&nbsp; else if (2 == Month ) {<BR>&nbsp;&nbsp;&nbsp; Month = 14;<BR>&nbsp;&nbsp;&nbsp; Year = Year -1;<BR>&nbsp; }<BR>&nbsp; Century = (Year/100);<BR>&nbsp; Year = Year%100;<BR>&nbsp;<BR>&nbsp; Weekday = (Century/4) - (2*Century) + Year + (Year/4) + (13 * (Month+1) / 5) + Day - 1;<BR>&nbsp; Weekday = Weekday%7;<BR>&nbsp; return Weekday;<BR>}</P>]]></description>
</item><item>
<title><![CDATA[MD5 信息-摘要算法(下)]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37785</link>
<author>FoxWolf</author>
<pubDate>2008/7/16 15:01:04</pubDate>
<description><![CDATA[附录A - 执行参考(文件源代码)<BR><BR>附录中包括取自RSAREF的文件：一个保护私人邮件的加密工具<BR><BR>global.h -- 通用头文件<BR><BR>md5.h -- MD5头文件<BR><BR>md5c.c -- MD5源代码<BR><BR>如果想得到更详细的信息，可以发电子邮件到 rsaref@rsa.com<BR><BR>附录中还包括：<BR>mddriver.c -- MD2, MD4 及 MD5 的测试驱动引擎<BR>此引擎默认是为测试MD5编译使用的，但也可以将MD标志中对C函数编译命令行修改成2或4，来支持对MD2或MD4的测试编译。<BR><BR>　　这种执行具有可移植性，能够在很多不同的平台上实现。当然根据特定平台，对这个执行进行优化设计也不难，这个工作可以留给读者来完成。例如：在 "little-endian"平台上，在一个32-bit的字中位于最内存地址最前的字节，其意义性最小，也没有严格的约束，因此对MD5传输解码的调用完全可以用一个典型的模型来取代。(newlaos:不明白)<BR><BR>　A.1 global.h<BR><BR>/* GLOBAL.H - RSAREF 类型及常数<BR>*/<BR><BR>/* PROTOTYPES should be set to one if and only if the compiler supports<BR>function argument prototyping.<BR>如果使PROTOTYPES没有被C语言编译器定义的话，就默认为0<BR>*/<BR>#ifndef PROTOTYPES<BR>#define PROTOTYPES 0<BR>#endif<BR><BR>/* POINTER 用来定义通用的指针类型 */<BR>typedef unsigned char *POINTER;<BR><BR>/* UINT2 定义一个双字节的字 */<BR>typedef unsigned short int UINT2;<BR><BR>/* UINT4 定义一个四字节的字 */<BR>typedef unsigned long int UINT4;<BR><BR>/* PROTO_LIST 的定义依据上面对PROTOTYPE的定义.<BR>如果用 PROTOTYPES, 那么 PROTO_LIST 返回就列表，否则返回一个空列表<BR>*/<BR>#if PROTOTYPES<BR>#define PROTO_LIST(list) list<BR>#else<BR>#define PROTO_LIST(list) ()<BR>#endif<BR><BR>　A.2 md5.h<BR><BR>/* MD5.H - MD5C.C 的头文件<BR>*/<BR><BR>/* Copyright (C) 1991-2, RSA 数据安全公司版权所有. 1991年<BR><BR>MD5使用授权声明(略)<BR>/* MD5 context. */<BR>typedef struct {<BR>UINT4 state[4]; /* state (ABCD) */<BR>UINT4 count[2]; /* bit数, modulo 2^64 (lsb first) */<BR>unsigned char buffer[64]; /* 输入缓冲 */<BR>} MD5_CTX;<BR><BR>void MD5Init PROTO_LIST ((MD5_CTX *));<BR>void MD5Update PROTO_LIST<BR>((MD5_CTX *, unsigned char *, unsigned int));<BR>void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));<BR><BR>　A.3 md5c.c MD5的源代码<BR>＃i nclude "global.h"<BR>＃i nclude "md5.h"<BR><BR>/* 定义MD5变换过程用到的常量<BR>*/<BR>#define S11 7<BR>#define S12 12<BR>#define S13 17<BR>#define S14 22<BR>#define S21 5<BR>#define S22 9<BR>#define S23 14<BR>#define S24 20<BR>#define S31 4<BR>#define S32 11<BR>#define S33 16<BR>#define S34 23<BR>#define S41 6<BR>#define S42 10<BR>#define S43 15<BR>#define S44 21<BR><BR>static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));<BR>static void Encode PROTO_LIST<BR>((unsigned char *, UINT4 *, unsigned int));<BR>static void Decode PROTO_LIST<BR>((UINT4 *, unsigned char *, unsigned int));<BR>static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));<BR>static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));<BR><BR>static unsigned char PADDING[64] = {<BR>0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,<BR>0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,<BR>0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0<BR>};<BR><BR>/* F, G, H 和 I 为MD5的基本函数<BR>*/<BR>#define F(x, y, z) (((x) &amp; (y)) | ((~x) &amp; (z)))<BR>#define G(x, y, z) (((x) &amp; (z)) | ((y) &amp; (~z)))<BR>#define H(x, y, z) ((x) ^ (y) ^ (z))<BR>#define I(x, y, z) ((y) ^ ((x) | (~z)))<BR><BR>/* ROTATE_LEFT 定义对x进行循环左移，使x只剩下n位bits值.<BR>*/<BR>#define ROTATE_LEFT(x, n) (((x) &lt;&lt; (n)) | ((x) &gt;&gt; (32-(n))))<BR><BR>/* FF, GG, HH 和 II 的分别代表第 1, 2, 3 及 4次循环<BR>位移与加法运算相分离，以防止再运算<BR>*/<BR>#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }<BR>#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }<BR>#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }<BR>#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }<BR><BR>/* MD5 初始化. 开始 MD5 操作, 并写入一个新的内容(context) (newlaos:内容的最终结果就是MD5的信息摘要了)<BR>*/<BR>void MD5Init (context)<BR>MD5_CTX *context; /* context */<BR>{<BR>context-&gt;count[0] = context-&gt;count[1] = 0;<BR>/* 加载初始化常数。<BR>*/<BR>context-&gt;state[0] = 0x67452301;<BR>context-&gt;state[1] = 0xefcdab89;<BR>context-&gt;state[2] = 0x98badcfe;<BR>context-&gt;state[3] = 0x10325476;<BR>}<BR><BR>/* MD5 数据块更新操作。继续MD5 信息摘要操作Continues an MD5 message-digest<BR>operation, processing another message block, and updating the<BR>context.<BR>*/<BR>void MD5Update (context, input, inputLen)<BR>MD5_CTX *context; /* MD5内容 */<BR>unsigned char *input; /* 输入的数据块 */<BR>unsigned int inputLen; /* 输入的数据块长度 */<BR>{<BR>unsigned int i, index, partLen;<BR><BR>/* 计算字节数除以64的余数(字节数 mod 64) */<BR>index = (unsigned int)((context-&gt;count[0] &gt;&gt; 3) &amp; 0x3F);<BR><BR>/* 更新bit位数 */<BR>if ((context-&gt;count[0] += ((UINT4)inputLen &lt;&lt; 3))<BR><BR>&lt; ((UINT4)inputLen &lt;&lt; 3))<BR>context-&gt;count[1]++;<BR>context-&gt;count[1] += ((UINT4)inputLen &gt;&gt; 29);<BR><BR>partLen = 64 - index;<BR><BR>/* 尽可能多地变换<BR>*/<BR>if (inputLen &gt;= partLen) {<BR>MD5_memcpy<BR>((POINTER)&amp;context-&gt;buffer[index], (POINTER)input, partLen);<BR>MD5Transform (context-&gt;state, context-&gt;buffer);<BR><BR>for (i = partLen; i + 63 &lt; inputLen; i += 64)<BR>MD5Transform (context-&gt;state, &amp;input[i]);<BR><BR>index = 0;<BR>}<BR>else<BR>i = 0;<BR><BR>/* 对剩下的数据进行缓存 */<BR>MD5_memcpy<BR>((POINTER)&amp;context-&gt;buffer[index], (POINTER)&amp;input[i],<BR>inputLen-i);<BR>}<BR><BR>/* MD5 结束. 结束一个生成 MD5 信息摘要过程的操作, 将信息摘要写入内容Context.<BR>*/<BR>void MD5Final (digest, context)<BR>unsigned char digest[16]; /* 信息摘要 */<BR>MD5_CTX *context; /* 内容context */<BR>{<BR>unsigned char bits[8];<BR>unsigned int index, padLen;<BR><BR>/* 保存bit位数 */<BR>Encode (bits, context-&gt;count, 8);<BR><BR>/* 将数据补长至对64求模(余数)为56<BR>*/<BR>index = (unsigned int)((context-&gt;count[0] &gt;&gt; 3) &amp; 0x3f);<BR>padLen = (index &lt; 56) ? (56 - index) : (120 - index);<BR>MD5Update (context, PADDING, padLen);<BR><BR>/* 增加长度 (补长前) */<BR>MD5Update (context, bits, 8);<BR><BR>/* 保存摘要状态 */<BR>Encode (digest, context-&gt;state, 16);<BR><BR>/* 将敏感信息至为最初状态<BR>*/<BR>MD5_memset ((POINTER)context, 0, sizeof (*context));<BR>}<BR><BR>/* MD5 基本变换. 基于块(512位)的变换<BR>*/<BR>static void MD5Transform (state, block)<BR>UINT4 state[4];<BR>unsigned char block[64];<BR>{<BR>UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];<BR><BR>Decode (x, block, 64);<BR><BR>/* 第 1 轮循环*/<BR>FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */<BR>FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */<BR>FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */<BR>FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */<BR>FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */<BR>FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */<BR>FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */<BR>FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */<BR>FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */<BR>FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */<BR>FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */<BR>FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */<BR>FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */<BR>FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */<BR>FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */<BR>FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */<BR><BR>/* 第 2 轮循环*/<BR>GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */<BR>GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */<BR>GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */<BR>GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */<BR>GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */<BR>GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */<BR>GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */<BR>GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */<BR>GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */<BR>GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */<BR>GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */<BR>GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */<BR>GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */<BR>GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */<BR>GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */<BR>GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */<BR><BR>/* 第 3 轮循环*/<BR>HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */<BR>HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */<BR>HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */<BR>HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */<BR>HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */<BR>HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */<BR>HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */<BR>HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */<BR>HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */<BR>HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */<BR>HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */<BR>HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */<BR>HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */<BR>HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */<BR>HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */<BR>HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */<BR><BR>/* 第 4 轮循环*/<BR>II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */<BR>II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */<BR>II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */<BR>II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */<BR>II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */<BR>II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */<BR>II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */<BR>II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */<BR>II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */<BR>II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */<BR>II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */<BR>II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */<BR>II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */<BR>II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */<BR>II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */<BR>II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */<BR><BR>state[0] += a;<BR>state[1] += b;<BR>state[2] += c;<BR>state[3] += d;<BR><BR>/* Zeroize sensitive information.<BR>*/<BR>MD5_memset ((POINTER)x, 0, sizeof (x));<BR>}<BR><BR>/* 将(UINT4)输入加密为(unsigned char)输出。假定长度为4的整数倍<BR>*/<BR>static void Encode (output, input, len)<BR>unsigned char *output;<BR>UINT4 *input;<BR>unsigned int len;<BR>{<BR>unsigned int i, j;<BR><BR>for (i = 0, j = 0; j &lt; len; i++, j += 4) {<BR>output[j] = (unsigned char)(input[i] &amp; 0xff);<BR>output[j+1] = (unsigned char)((input[i] &gt;&gt; 8) &amp; 0xff);<BR>output[j+2] = (unsigned char)((input[i] &gt;&gt; 16) &amp; 0xff);<BR>output[j+3] = (unsigned char)((input[i] &gt;&gt; 24) &amp; 0xff);<BR>}<BR>}<BR><BR>/*将 (unsigned char)输入解密为(UINT4)输出。假定长度为4的整数倍。<BR>*/<BR>static void Decode (output, input, len)<BR>UINT4 *output;<BR>unsigned char *input;<BR>unsigned int len;<BR>{<BR>unsigned int i, j;<BR><BR>for (i = 0, j = 0; j &lt; len; i++, j += 4)<BR>output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) &lt;&lt; 8) |<BR>(((UINT4)input[j+2]) &lt;&lt; 16) | (((UINT4)input[j+3]) &lt;&lt; 24);<BR>}<BR><BR>/* Note: Replace "for loop" with standard memcpy if possible.<BR>*/<BR><BR>static void MD5_memcpy (output, input, len)<BR>POINTER output;<BR>POINTER input;<BR>unsigned int len;<BR>{<BR>unsigned int i;<BR><BR>for (i = 0; i &lt; len; i++)<BR><BR>output[i] = input[i];<BR>}<BR><BR>/* Note: Replace "for loop" with standard memset if possible.<BR>*/<BR>static void MD5_memset (output, value, len)<BR>POINTER output;<BR>int value;<BR>unsigned int len;<BR>{<BR>unsigned int i;<BR><BR>for (i = 0; i &lt; len; i++)<BR>((char *)output)[i] = (char)value;<BR>}<BR><BR>　A.4 mddriver.c<BR><BR>/* MDDRIVER.C - MD2, MD4 及 MD5 的测试驱动<BR>*/<BR><BR>/* 如果没有用C语言编译器的标志定义的话，默认是进行MD5测试驱动<BR>*/<BR>#ifndef MD<BR>#define MD MD5<BR>#endif<BR><BR>＃i nclude &lt;stdio.h&gt;<BR>＃i nclude &lt;time.h&gt;<BR>＃i nclude &lt;string.h&gt;<BR>＃i nclude "global.h"<BR>#if MD == 2<BR>＃i nclude "md2.h"<BR>#endif<BR>#if MD == 4<BR>＃i nclude "md4.h"<BR>#endif<BR>#if MD == 5<BR>＃i nclude "md5.h"<BR>#endif<BR><BR>/* 测试块的长度, 测试块的数目。<BR>*/<BR>#define TEST_BLOCK_LEN 1000<BR>#define TEST_BLOCK_COUNT 1000<BR><BR>static void MDString PROTO_LIST ((char *));<BR>static void MDTimeTrial PROTO_LIST ((void));<BR>static void MDTestSuite PROTO_LIST ((void));<BR>static void MDFile PROTO_LIST ((char *));<BR>static void MDFilter PROTO_LIST ((void));<BR>static void MDPrint PROTO_LIST ((unsigned char [16]));<BR><BR>#if MD == 2<BR>#define MD_CTX MD2_CTX<BR>#define MDInit MD2Init<BR>#define MDUpdate MD2Update<BR>#define MDFinal MD2Final<BR>#endif<BR>#if MD == 4<BR>#define MD_CTX MD4_CTX<BR>#define MDInit MD4Init<BR>#define MDUpdate MD4Update<BR>#define MDFinal MD4Final<BR>#endif<BR>#if MD == 5<BR>#define MD_CTX MD5_CTX<BR>#define MDInit MD5Init<BR>#define MDUpdate MD5Update<BR>#define MDFinal MD5Final<BR>#endif<BR><BR>/* 主驱动<BR><BR>参数定义 (可以合起来使用):<BR>-sstring - 摘要字符串<BR>-t - 进行时间测试<BR>-x - 运行测试脚本<BR>filename - 摘要文件<BR>(none) - 摘要标准输入<BR>*/<BR>int main (argc, argv)<BR>int argc;<BR>char *argv[];<BR>{<BR>int i;<BR><BR>if (argc &gt; 1)<BR>for (i = 1; i &lt; argc; i++)<BR>if (argv[i][0] == ‘-‘ &amp;&amp; argv[i][1] == ‘s‘)<BR>MDString (argv[i] + 2);<BR>else if (strcmp (argv[i], "-t") == 0)<BR>MDTimeTrial ();<BR>else if (strcmp (argv[i], "-x") == 0)<BR>MDTestSuite ();<BR>else<BR>MDFile (argv[i]);<BR>else<BR>MDFilter ();<BR><BR>return (0);<BR>}<BR><BR>/* 计算一个字符串信息摘要，并输出结果<BR>*/<BR>static void MDString (string)<BR>char *string;<BR>{<BR>MD_CTX context;<BR>unsigned char digest[16];<BR>unsigned int len = strlen (string);<BR><BR>MDInit (&amp;context);<BR>MDUpdate (&amp;context, string, len);<BR>MDFinal (digest, &amp;context);<BR><BR>printf ("MD%d (\"%s\") = ", MD, string);<BR>MDPrint (digest);<BR>printf ("\n");<BR>}<BR><BR>/* 测量对 TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte 数据块进行摘要计算所需的时间<BR>*/<BR>static void MDTimeTrial ()<BR>{<BR>MD_CTX context;<BR>time_t endTime, startTime;<BR>unsigned char block[TEST_BLOCK_LEN], digest[16];<BR>unsigned int i;<BR>printf<BR>("MD%d time trial. Digesting %d %d-byte blocks ...", MD,<BR>TEST_BLOCK_LEN, TEST_BLOCK_COUNT);<BR><BR>/* Initialize block */<BR>for (i = 0; i &lt; TEST_BLOCK_LEN; i++)<BR>block[i] = (unsigned char)(i &amp; 0xff);<BR><BR>/* Start timer */<BR>time (&amp;startTime);<BR><BR>/* Digest blocks */<BR>MDInit (&amp;context);<BR>for (i = 0; i &lt; TEST_BLOCK_COUNT; i++)<BR>MDUpdate (&amp;context, block, TEST_BLOCK_LEN);<BR>MDFinal (digest, &amp;context);<BR><BR>/* Stop timer */<BR>time (&amp;endTime);<BR><BR>printf (" done\n");<BR>printf ("Digest = ");<BR>MDPrint (digest);<BR>printf ("\nTime = %ld seconds\n", (long)(endTime-startTime));<BR>printf<BR>("Speed = %ld bytes/second\n",<BR>(long)TEST_BLOCK_LEN * (long)TEST_BLOCK_COUNT/(endTime-startTime));<BR>}<BR><BR>/* 计算一系列字符串的信息摘要，并输出结果<BR>*/<BR>static void MDTestSuite ()<BR>{<BR>printf ("MD%d test suite:\n", MD);<BR><BR>MDString ("");<BR>MDString ("a");<BR>MDString ("abc");<BR>MDString ("message digest");<BR>MDString ("abcdefghijklmnopqrstuvwxyz");<BR>MDString<BR>("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");<BR>MDString<BR>("12345678901234567890123456789012345678901234567890123456789012345678901234567890");<BR>}<BR><BR>/* 计算一个文件的信息摘要，并输出结果<BR>*/<BR>static void MDFile (filename)<BR>char *filename;<BR>{<BR>FILE *file;<BR>MD_CTX context;<BR>int len;<BR>unsigned char buffer[1024], digest[16];<BR><BR>if ((file = fopen (filename, "rb")) == NULL)<BR>printf ("%s can‘t be opened\n", filename);<BR><BR>else {<BR>MDInit (&amp;context);<BR>while (len = fread (buffer, 1, 1024, file))<BR>MDUpdate (&amp;context, buffer, len);<BR>MDFinal (digest, &amp;context);<BR><BR>fclose (file);<BR><BR>printf ("MD%d (%s) = ", MD, filename);<BR>MDPrint (digest);<BR>printf ("\n");<BR>}<BR>}<BR><BR>/* 计算标准输入的信息摘要，并输出结果<BR>*/<BR>static void MDFilter ()<BR>{<BR>MD_CTX context;<BR>int len;<BR>unsigned char buffer[16], digest[16];<BR><BR>MDInit (&amp;context);<BR>while (len = fread (buffer, 1, 16, stdin))<BR>MDUpdate (&amp;context, buffer, len);<BR>MDFinal (digest, &amp;context);<BR><BR>MDPrint (digest);<BR>printf ("\n");<BR>}<BR><BR>/* 以16进制的形式输出一份信息摘要<BR>*/<BR>static void MDPrint (digest)<BR>unsigned char digest[16];<BR>{<BR>unsigned int i;<BR><BR>for (i = 0; i &lt; 16; i++)<BR>printf ("%02x", digest[i]);<BR>}<BR><BR>　A.5 对编写好的MD5程序进行一系列测试<BR><BR>MD5 系列测试(驱动参数 "-x") 得到的结果应该为:<BR><BR>MD5 系列测试:<BR>MD5 ("") = d41d8cd98f00b204e9800998ecf8427e<BR>MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661<BR>MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72<BR>MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0<BR>MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b<BR>MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =<BR>d174ab98d277d9f5a5611c2c9f419d9f<BR>MD5 ("123456789012345678901234567890123456789012345678901234567890123456<BR>78901234567890") = 57edf4a22be3c955ac49da2e2107b67a<BR><BR>安全考虑及常用的攻击方式<BR><BR>在这份备忘录里讨论的MD5安全级别，主要是考虑将MD5加密算法与公钥加密系统相配合，构成混合的签名认证机制，将具有极高的安全。<BR><BR>　　MD5是被用来单向变换用户口令和对信息签名的单向散列算法。一种单向散列的强度体现在它能把任意的输入随机化到什么程度并且能产生唯一的输出。对单向散列的直接攻击可以分为普通直接攻击和“生日”攻击。 <BR><BR>● 对MD5的普通直接攻击 <BR><BR>　　所谓直接攻击又叫野蛮攻击。攻击者为了找到一份和原始明文 m 散列结果相同的明文 m‘ ，就是 H(m)=H(m‘) 。普通直接攻击，顾名思义就是穷举可能的明文去产生一个和 H(m) 相同的散列结果。对MD5来说散列结果为128-bits，就是说如果攻击者有一台每秒尝试1,000,000,000条明文的机器需要算约10^22 年，同时兴许会同时发现m本身:))。<BR><BR>● 对MD5的生日攻击 <BR><BR>　　生日攻击实际上只是为了找到两条能产生同样散列结果的明文。记得那个有名的概率生日问题吗？在 N 个人中至少有两个人生日相同的概率是多少？所谓生日攻击实际上只是用概率来指导散列冲突的发现，对于MD5来说如果尝试2^64条明文，那么它们之间至少有一对发生冲突的概率就是 50％。仅此而已，对当今的科技能力来说，它也是不可能的。一台上面谈到的机器平均需要运行585年才能找到一对，而且并不能马上变成实际的攻击成果。因为码长和速度的关系，对crypt(3)的生日攻击就成功得多。<BR><BR>● 其他对MD5的攻击 <BR><BR>　　微分攻击被证明对MD5的一次循环是有效的，但对全部4次循环无效。（微分攻击是通过比较分析有特定区别的明文在通过加密后的变化传播情况来攻击加密体系的。）有一种成功的MD5攻击，不过它是对MD5代码本身做了手脚，是一种crack而不是hack更算不上cryptanalysis了。<BR><BR>● 口令长度和信息论 <BR><BR>　　根据传统信息论，英语的每个8-bits字母的信息熵为1.3bits。如果口令足够长，MD5的结果就会足够随机。对 128-bits 的MD5输出来说，一个长达98个字符的口令将给出一个随机的密匙。(8/1.3)*(128/8) = 98.46 chars可是谁会用一个象下面这样长的口令呢？ <BR><BR>12345678901234567890123456789012345678901235678901234567890 <BR>1234567890123456789012345678 <BR><BR>1.3 bits 的信息熵来自于英语语法的规律性这个事实，字母出现概率的不等造成了熵的减小。如果26个拉丁字母出现的概率均等，信息熵将会增至log(26)/log(2) = 4.7 bits　<BR><BR>这样一个随机密匙所需口令长度就减为 27.23 chars 了，如果再加上大小写和几个符号还可以减少。关于选择口令的问题可以参考任何关于安全性的书籍，它们都适用。 作者通讯地址(newlaos:未翻译:)<BR><BR>Ronald L. Rivest<BR>Massachusetts Institute of Technology<BR>Laboratory for Computer Science<BR>NE43-324<BR>545 Technology Square<BR>Cambridge, MA 02139-1986<BR><BR>Phone: (617) 253-5880<BR>EMail: rivest@theory.lcs.mit.edu]]></description>
</item><item>
<title><![CDATA[MD5 信息-摘要算法(上)]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37784</link>
<author>FoxWolf</author>
<pubDate>2008/7/16 15:01:04</pubDate>
<description><![CDATA[
<P>英文原文(<SPAN class=h1><STRONG>The MD5 Message-Digest Algorithm </STRONG></SPAN><SPAN class=grey><A href="http://tools.ietf.org/html/rfc1321"><FONT color=#777777>RFC 1321</FONT></A></SPAN>)，翻译如下。</P>
<P>可以查看知识库: <A href="http://en.wikipedia.org/wiki/MD5">http://en.wikipedia.org/wiki/MD5</A></P>
<P><FONT color=#ff0000 size=6>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MD5 信息-摘要算法<BR><BR></FONT><FONT size=3>翻译：newlaos[DFCG][CCG]<BR>备忘录说明：<BR>&nbsp;&nbsp; 　这篇备忘录讲述的是因特网通讯方面的内容，并不是定义一个因特网标准，因此传播此文件，将不受任何限制。<BR><BR>内容列表：<BR>1、总述<BR>2、术语及符号<BR>3、MD5算法描述<BR>4、概要<BR>5、MD4与MD5的不同<BR>参考<BR>附录 A - 执行参考(文件源代码)<BR>MD5安全考虑及常用攻击方式(newlaos收集补充)<BR>作者地址<BR><BR>1、总述<BR><BR>　　这个篇文章将详细描述 MD5 信息-摘要算法。这种算法可以将任意长度的信息处理成一个128bit(定长)的“指纹”或“信息摘要”。任意两个不同信息不可能处理成同样的信息摘要，同时根据得到的信息摘要也不可能逆推出原始信息。MD5算法主要用于数字签名，出于安全的考虑，任何信息(明文)在用公钥加密系统(例如RSA)加密传送前，都要用MD5算法处理出一个信息摘要。(newlaos: 一个需要传输的明文，先用MD5算法得到一个信息摘要，再用RSA的传送方私钥和接收方公钥对明文进行加密。然后传送方将密文、RSA的传送方公钥及信息摘要传输给接收方。接收方用RSA的接收方私钥和传送方公钥将密文还原成明文，再通过MD5算法得出解密后明文的信息摘要，用它来和传送方发过来的信息摘要对比，如果相同，就说明密文在传送过程中没有被人篡改。)<BR><BR>　　MD5 算法是为32位计算机设计的，它在32位计算机上处理起来非常快，并且MD5算法不需要任何大的替代表，在编程实现方面也十分简洁易行。<BR><BR>　　MD5 算法是MD4算法的扩展，仅比MD4稍慢一点，但在设计上更为谨慎。MD4虽然快，但是存在一种漏洞，它导致对不同的内容进行加密却可能得到相同的加密后结果，因而促就了MD5的产生。MD5为了获得更好安全性，在运算速度上做了小小的让步。MD5算法作为一个标准，被至于大众领域以吸取各种好的建议，并进行适当的优化和改善。<BR><BR>2、术语及符号<BR><BR>　　在这篇文章里，“word(字)”为32-bit的数值，“byte(字节)”为8-bit的数值。一个bit数据流通常解释为byte数据流，每8 -bit数据被解释为1-byte，且按高位在前的顺序排列。同理，一个byte数据流通常解释为word(字)数据流，每4-byte被解释为1- word，按的是低位在前的顺序排列。<BR><BR>　　x_i表示"x sub i"，如果写在下方的(下标)是一个表达式，我就用括号将它括起来x_{i+1}。同理，我们用 ^ 代表上标(求幂)，那么x^i代表的就是x的i次方。<BR><BR>　　"+"代表是word(字)的加法运算。X &lt;&lt;&lt; s表示一个32-bit的数值X向左循环移动s位。not(X)表示对X按bit(位)求反，X v Y表示X与Y按bit(位)<FONT color=#48dd22>做或运算</FONT>,X xor Y表示X与Y按bit(位)做异或运算，XY表示X与Y按bit(位)做且运算<BR><BR>3、MD5算法描述<BR><BR>　　我们假设有一个b-bit类型的数据，我们希望找到它的信息搞要。在这里b是任意一个无符号整数；b可能为0，不一定是8的倍数，也可能它的数值十分的大。我们设想这个bits(位)型的数据按下面的格式书写：<BR>m_0 m_1 ... m_{b-1} (newlaos：还是记得吗,m_0代表的是第1位(bit)的数据，m下标0，类似数组中的第一个单元)<BR><BR>接下来的5步就是计算这个数据的信息搞要。<BR><BR>　3.1 第1步 按位补充数据(补位)<BR><BR>　　这个数据按(bit)位补充，要求最终的(bit)位数对512求模的结果为448(newlaos：数据的bit位数 mod 512=448)。也就是说数据补位后，其位数长度只差64(bit)位就是512的整数倍(newlaos：数据的bit位数 + 64 = n*512)。补位的过程必须做，即便是这个数据的位数对512求模的结果正好是448。<BR><BR>　　补位的实现过程：首先在数据后补一个1(bit),接着在后面补上一堆0(bit)，直到整个数据的位数对512求模的结果正好为448。总之，至少补1位，而最多可能补512位。<BR><BR>　3.2 第2步 扩展长度<BR><BR>　　在完成前1步的补位工作后，又将一个表示数据b原始长度的64-bit数(newlaos:这是对b没有补位前长度的描述，2进制来表示)补在最后。很极少情况下,数据b的长度会大于2^64，所以只用一个64-bit(低位在前顺序)数据来表示数据b长度就可以了。(这些bit值是作为两个32-bit字补充的)<BR><BR>　　当完成补位及补充数据b的描述后（有些书将这两步合称为：数据填充），得到的结果数据长度正好是512的整数倍。也就是说长度正好是16个(32-bit)字的整数倍(newlaos:也就是64个字节的数据长度)。M[0 ... N-1]代表结果数据中的数组单元，N是16的倍数。<BR><BR>　3.3 第3步 初始化 MD Buffer(缓冲)<BR><BR>　　一个四字(four-word)缓冲(A，B，C，D)被用来计算信息摘要。这里A，B，C，D分别代表一个32-bit寄存器。这些寄存器值初始化为下列的数值：(16进制、低位在前)<BR><BR>word A: 01 23 45 67<BR>word B: 89 ab cd ef<BR>word C: fe dc ba 98<BR>word D: 76 54 32 10<BR><BR>　3.4 第4步　以16*32bit(word)信息块方式处理信息<BR>　　首先，我们定义四个辅助的函数，每一个函数以三个32-bit数值作为参数，处理输出为一个32-bit数值。<BR>F(X,Y,Z) = XY v not(X)Z　　　　　　　　　　　　　　<BR>G(X,Y,Z) = XZ v Y not(Z)<BR>H(X,Y,Z) = X xor Y xorZ<BR>I(X,Y,Z) = Y xor (X v not(Z))<BR>*************************<BR>newlaos: 用VB表示：<BR>F(X,Y,Z) = (X and Y) or ((not X) and Z)　　　　　　　　　　　　　　<BR>G(X,Y,Z) = (X and Z) or (Y and (not Z))<BR>H(X,Y,Z) = X xor Y xor Z<BR>I(X,Y,Z) = Y xor (X and (not Z)<BR><BR>newlaos: 用VC表示：<BR>F(x, y, z) = (((x) &amp; (y)) | ((~x) &amp; (z)))<BR>G(x, y, z) = (((x) &amp; (z)) | ((y) &amp; (~z)))<BR>H(x, y, z) = ((x) ^ (y) ^ (z))<BR>I(x, y, z) = ((y) ^ ((x) | (~z)))<BR>*************************<BR><BR>　　F函数是逐位(bit)函数，即：如果X成立，结果就是Y，否则就是Z。如果 XY 和 not(X)Z 运算在同一bit位上永远都不同为1的话，那么F函数就能被定义为 + ，而不是 v 。这是十分有趣的，即如果X，Y，Z数值的所有bit位独立且平均分布的话(newlaos:1和0在数值各二进制位上分布比较平均),那F(X，Y，Z)函数结果在bit位上的1，0分布也将是独立且平均的。<BR><BR>　　G，H，I 函数与F函数比较类似，都是根据X，Y，Z的值进行处理，并实现其函数结果在bit位的独立且平均的作用。注意：H函数是bit位的"XOR"或"parity(奇偶)"函数<BR><BR>　　这一步将用到一个有64个元素的表T[1 ... 64](newlaos:也就是一维数组了)，而这个表又是由正弦函数构造出来的。T[i]表示表内的第i个元素，它的值等于函数abs(sin(i))的4294967296次方，再取整后的值。newlaos:// T[i]=int(abs(sin(i))^4294967296) // 。这里i就代表弧度。数组元素(表元素)将在附录中给出。。(newlaos:4294967296等于2^32)　　照着下面去做：<BR><BR>/* 处理每一个16*32bit的信息块(512bit) */<BR>For i = 0 to N/16-1 do<BR><BR>/* 将信息块 i 拷贝入 X */<BR>For j = 0 to 15 do<BR>Set X[j] to M[i*16+j]. (newlaos: 每一次，把数据原文存放在16个元素的数组X中。等效于 X[j]=M[i*16+j])<BR>end /* 循环 */<BR><BR>/* 将A存为AA，B存为BB，C存为CC，将D存为DD */<BR>AA = A<BR>BB = B<BR>CC = C<BR>DD = D<BR><BR>/* 循环 1 */<BR>/* [abcd k s i] 代表操作<BR>a = b + ((a + F(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */<BR>/* 做接下来的16步操作 */<BR>[ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4]<BR>[ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8]<BR>[ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12]<BR>[ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]<BR><BR>/* 循环 2 */<BR>/* [abcd k s i] 代表操作<BR>a = b + ((a + G(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */<BR>/* 做接下来的16步操作 */<BR>[ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20]<BR>[ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24]<BR>[ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28]<BR>[ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]<BR><BR>/* 循环 3. */<BR>/* [abcd k s t] 代表操作<BR>a = b + ((a + H(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */<BR>/* 做接下来的16步操作 */<BR>[ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36]<BR>[ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40]<BR>[ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44]<BR>[ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]<BR><BR>/* 循环 4. */<BR>/* [abcd k s t] 代表操作<BR>a = b + ((a + I(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */<BR>/* 做接下来的16步操作 */<BR>[ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52]<BR>[ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56]<BR>[ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60]<BR>[ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]<BR><BR>/* 接下就是做下面的加法. (每个寄存器加上初始化时的值） */<BR>A = A + AA<BR>B = B + BB<BR>C = C + CC<BR>D = D + DD<BR><BR>end /* 大循环结束 */<BR><BR>　3.5　第5步 输出<BR>　　作息摘要最终处理成以A，B，C，D的形式输出。也就是开始于A的低位在前的顺序字节，结束于D的高位在前的顺序字节。<BR>到这里对MD5的算法描述就完了。对于C的执行参考将在附录中给出。<BR><BR>4、概要<BR><BR>　　MD5 信息-摘要算法简便易行，并且能够给出任意长度信息的“指纹”或信息摘要。据猜测MD5的难度就在于每两个信息有同样信息摘要的可能性是2^64分之一，而任意一个给定的信息其得出的信息摘要将是2^128分之一。M5算法针对其弱点是经过十分细致设计的，当然如果相对新一些的算法与进一步的安全性分析，将能对这个排列顺序进行更好的调整。<BR><BR>5、MD4 与 MD5 的不同<BR><BR>下面列出了 MD4 与 MD5之间的区别：<BR>1、增加了第四次循环<BR>2、每一步采有唯一不同的加法常数<BR>3、在第2次循环中，G函数由(XY v XZ v YZ)改为(XZ v Y not(Z))，以使G函数的平衡性更低<BR>4、每一步增加了前一步的计算结果，这导致更快的雪崩效应。<BR>5、改变了第二轮和第三轮中访问消息子分组的次序，使得这个模型彼此更不相似。<BR>6、在每次循环中的位移量被近似优化，导致更快的雪崩效应。在不同循环中的位移量截然不同。<BR><BR>参考：<BR><BR>[1] Rivest, R., "MD4 信息摘要算法", RFC 1320, MIT and<BR>RSA Data Security, Inc., 1992年四月.<BR><BR>[2] Rivest, R., "MD4 信息摘要算法", in A.J. Menezes<BR>and S.A. Vanstone, editors, Advances in Cryptology - CRYPTO ‘90<BR>Proceedings, pages 303-311, Springer-Verlag, 1991.<BR><BR>[3] CCITT Recommendation X.509 (1988), "The Directory -<BR>Authentication Framework."</FONT></P>]]></description>
</item><item>
<title><![CDATA[一个进程能够打开最大文件句柄数的设置]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37765</link>
<author>FoxWolf</author>
<pubDate>2008/7/15 15:48:55</pubDate>
<description><![CDATA[<A><FONT size=3>　</FONT></A><SPAN class=a14c id=zoom><FONT size=3> </FONT>
<P style="TEXT-INDENT: 2em"><FONT size=3>在Linux下，我们使用ulimit -n命令可以看到单个进程能够打开的最大文件句柄数量(socket连接也算在里面)。系统默认值1024。 </FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3>对于一般的应用来说(象Apache、系统进程)1024完全足够使用。但是如何象 squid、mysql、java等单进程处理大量请求的应用来说就有点捉襟见肘了。如果单个进程打开的文件句柄数量超过了系统定义的值，就会提到 “too many files open”的错误提示。如何知道当前进程打开了多少个文件句柄呢？下面一段小脚本可以帮你查看：lsof -n |awk '{print }'|sort|uniq -c |sort -nr|more　　 </FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3>在系统访问高峰时间以root用户执行上面的脚本，可能出现的结果如下：# lsof -n|awk '{print }'|sort|uniq -c |sort -nr|more　　　　131 24204　　 57 24244　　 57 24231　　 56 24264 </FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3>其中第一行是打开的文件句柄数量，第二行是进程号。得到进程号后，我们可以通过ps 命令得到进程的详细内容。ps -aef|grep 24204 mysql　　24204 24162 99 16:15 ?　　　　00:24:25 /usr/sbin/mysqld </FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3>哦，原来是mysql进程打开最多文件句柄数量。但是他目前只打开了131个文件句柄数量，远远底于系统默认值1024。 </FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3>但是如果系统并发特别大，尤其是squid服务器，很有可能会超过1024。这时候就必须要调整系统参数，以适应应用变化。Linux有硬性限制和软性限制。可以通过ulimit来设定这两个参数。方法如下，以root用户运行以下命令：ulimit -HSn 4096 </FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3>以上命令中，H指定了硬性大小，S指定了软性大小，n表示设定单个进程最大的打开文件句柄数量。个人觉得最好不要超过4096，毕竟打开的文件句柄数越多响应时间肯定会越慢。设定句柄数量后，系统重启后，又会恢复默认值。如果想永久保存下来，可以修改.bash_profile文件，可以修改 /etc/profile 把上面命令加到最后。(findsun提出的办法比较合理) </FONT></P>
<P><FONT size=3></FONT>&nbsp;</P>
<P><FONT size=3>Ulimit命令<BR>设置限制&nbsp;&nbsp;&nbsp;&nbsp; 可以把命令加到profile文件里，也可以在/etc/security/limits.conf文件中定义<BR>限制。<BR>命令参数<BR>-a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 显示所有限制<BR>-c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; core文件大小的上限<BR>-d&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 进程数据段大小的上限<BR>-f&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shell所能创建的文件大小的上限<BR>-m&nbsp;&nbsp;&nbsp;&nbsp; 驻留内存大小的上限<BR>-s&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 堆栈大小的上限<BR>-t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每秒可占用的CPU时间上限<BR>-p&nbsp;&nbsp;&nbsp;&nbsp; 管道大小<BR>-n&nbsp;&nbsp;&nbsp;&nbsp; 打开文件数的上限<BR>-u&nbsp;&nbsp;&nbsp;&nbsp; 进程数的上限<BR>-v&nbsp;&nbsp;&nbsp;&nbsp; 虚拟内存的上限<BR></FONT></P>
<P style="TEXT-INDENT: 2em"><FONT size=3><BR></FONT></P></SPAN>]]></description>
</item><item>
<title><![CDATA[IBM开发者论坛_Posix线程编程指南]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37757</link>
<author>FoxWolf</author>
<pubDate>2008/7/15 9:37:45</pubDate>
<description><![CDATA[<P><A><FONT size=3>　</FONT></A><A><FONT size=3>　文档来自IBM开发者论坛中国防科技大学杨沙洲博士的5个Linux Thread详解系</FONT></A></P>
<P><FONT size=3>列.虽然其名字是取的POSIX Thread线程,但其内容是Linux Thread的,并且很多源</FONT></P>
<P><FONT size=3>自内核级的评述,让人受益菲浅.链接地址如下:<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</FONT><A href="http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part1/index.html" target=_blank><FONT size=3>线程创建与取消</FONT></A><A><BR><FONT size=3>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</FONT></A><A href="http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/index.html" target=_blank><FONT size=3>线程私有数据</FONT></A><A><BR><FONT size=3>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</FONT></A><A href="http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part3/index.html" target=_blank><FONT size=3>线程同步</FONT></A><A><BR><FONT size=3>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</FONT></A><A href="http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part4/index.html" target=_blank><FONT size=3>线程终止</FONT></A><A><BR><FONT size=3>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</FONT></A><A href="http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part5/index.html" target=_blank><FONT size=3>线程杂项</FONT></A><A><BR></P></A>]]></description>
</item><item>
<title><![CDATA[Linux系统调用--getitimer/setitimer函数详解]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37401</link>
<author>FoxWolf</author>
<pubDate>2008/7/4 16:13:22</pubDate>
<description><![CDATA[
<TABLE style="BORDER-COLLAPSE: collapse; WORD-WRAP: break-word" cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD align=middle>
<TABLE style="BORDER-COLLAPSE: collapse; WORD-WRAP: break-word" cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD width="100%">
<DIV id=art style="MARGIN: 15px" width="100%">
<P>【getitimer/setitimer系统调用】 <BR>&nbsp; <BR><FONT size=4>功能描述：<BR>获取或设定间歇计时器的值。系统为进程提供三种类型的计时器，每一类以不同的时间域递减其值。当计时器超时，信号被发送到进程，之后计时器重启动。</FONT></P>
<P><BR><FONT size=4>用法： <BR>#include &lt;sys/time.h&gt;</FONT></P>
<P><FONT size=4>int getitimer(int which, struct itimerval *value);<BR>int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); </FONT></P>
<P><FONT size=4>&nbsp; <BR>参数： <BR>which：间歇计时器类型，有三种选择</FONT></P>
<P><FONT size=4>ITIMER_REAL //数值为0，计时器的值实时递减，发送的信号是SIGALRM。<BR>ITIMER_VIRTUAL //数值为1，进程执行时递减计时器的值，发送的信号是SIGVTALRM。<BR>ITIMER_PROF //数值为2，进程和系统执行时都递减计时器的值，发送的信号是SIGPROF。</FONT></P>
<P><FONT size=4>value，ovalue：时间参数，原型如下</FONT></P>
<P><FONT size=4>struct itimerval {<BR>&nbsp;&nbsp;&nbsp; struct timeval it_interval; /* 计时器重启动的间歇值 */<BR>&nbsp;&nbsp;&nbsp; struct timeval it_value;&nbsp;&nbsp;&nbsp; /* 计时器安装后首先启动的初始值 */<BR>};</FONT></P>
<P><FONT style="BACKGROUND-COLOR: #eeeeee" color=#3d11ee size=4>我的备注：</FONT></P>
<P><FONT style="BACKGROUND-COLOR: #eeeeee" color=#3d11ee size=4>&nbsp;&nbsp;&nbsp;&nbsp; itimerval结构中的it_value是减少的时间,当这个值为0的时候就发出相应的信号了. 然后再将it_value设置为it_interval值. 也就是先处理it_value中设置的值，为0后发送信号(根据which来判断发送什么信号),之后都是根据it_interval的值发送信号。若it_value和it_interval都为0，也就没有相应的信号产生了</FONT></P>
<P><FONT size=4>struct timeval {<BR>&nbsp;&nbsp;&nbsp; long tv_sec;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 秒 */<BR>&nbsp;&nbsp;&nbsp; long tv_usec;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 微妙(1/1000000) */<BR>};</FONT></P>
<P><FONT size=4>getitimer()用计时器的当前值填写value指向的结构体。<BR>setitimer()将value指向的结构体设为计时器的当前值，如果ovalue不是NULL，将返回计时器原有值。</FONT></P>
<P><BR><FONT size=4>返回说明： <BR>成功执行时，返回0。失败返回-1，errno被设为以下的某个值 <BR>EFAULT：value或ovalue是不有效的指针<BR>EINVAL：其值不是ITIMER_REAL，ITIMER_VIRTUAL 或 ITIMER_PROF之一</FONT></P>
<P>&nbsp;我的测试程序例子：<IMG src="http://blogger.org.cn/blog/images/file/zip.gif" border=0><A href="http://blogger.org.cn/blog/uploadfile/200874162918195.RAR" target=_blank>mysetiitimer.rar</A></P></DIV></TD></TR></TBODY></TABLE>
<P style="MARGIN: 5px; LINE-HEIGHT: 150%"></P></TD></TR>
<TR>
<TD height=25>&nbsp;<FONT color=#000099><B>原文地址</B></FONT> <A href="http://club.cn.yahoo.com/bbs/threadview/1200062866_125__pn.html" target=_blank>http://club.cn.yahoo.com/bbs/threadview/1200062866_125__pn.html</A> </TD></TR></TBODY></TABLE>]]></description>
</item><item>
<title><![CDATA[LAMP 服务器安装配置]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37394</link>
<author>FoxWolf</author>
<pubDate>2008/7/3 20:27:36</pubDate>
<description><![CDATA[<P>LAMP 服务器安装配置,参考：<A href="http://wiki.ubuntu.org.cn/LAMP_%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE">http://wiki.ubuntu.org.cn/LAMP_%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE</A></P>
<P>在安装<SPAN class=mw-headline>PDO时出现的问题解决方法：安装二个包</SPAN></P>
<P><SPAN class=mw-headline>sudo apt-get install php-pear<BR>sudo apt-get install php5-dev</SPAN></P>
<P><SPAN class=mw-headline>"cannot find mysql header files under" 安装 apt-get install libmysqlclient15-dev</SPAN></P>]]></description>
</item><item>
<title><![CDATA[linux shell编程 trap命令]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37325</link>
<author>FoxWolf</author>
<pubDate>2008/7/2 11:31:01</pubDate>
<description><![CDATA[<P style="TEXT-INDENT: 18pt"><FONT size=3>trap命令用于指定在接收到信号后将要采取的行 动，我们将在本书后面的内容中详细介绍信号。trap命令的一种常见用途是在脚本程序被中断时完成清理工作。历史上，shell总是用数字来代表信号，而 新的脚本程序应该使用信号的名字，它们保存在用#include命令包含进来的signal.h头文件中，在使用信号名时需要省略SIG前缀。你可以在命 令提示符下输入命令trap -l来查看信号编号及其关联的名称。</FONT></P>
<DIV style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: rgb(153,153,153) 1pt solid; PADDING-LEFT: 0cm; PADDING-BOTTOM: 4pt; MARGIN-LEFT: 0cm; BORDER-LEFT: medium none; MARGIN-RIGHT: 5.2pt; PADDING-TOP: 4pt; BORDER-BOTTOM: 1pt solid" twffan="done">
<P class=aff4 style="MARGIN: 5.25pt 0cm 5.25pt 28.5pt; TEXT-INDENT: -28.5pt"><FONT size=3>对于那些不熟悉信号的人们来说，“信号”是指那些被异步发送到一个程序的事件。默认情况下，它们通常会终止一个程序的运行。</FONT></P></DIV>
<P style="TEXT-INDENT: 18pt"><FONT size=3><FONT color=#ff0000>trap命令的参数分为两部分，前一部分是接收到指定信号时将要采取的行动，后一部分是要处理的信号名</FONT>。</FONT></P>
<P class=-1 style="MARGIN: 5.25pt 0cm 5.25pt 19.3pt"><FONT size=3><IMG height=10 src="http://book.csdn.net/BookFiles/353/img/image151.jpg" width=118 twffan="done"></FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>请记住，脚本程序通常是以从上到下的顺序解释执行的，<FONT color=#ff0000>所以必须在你想保护的那部分代码以前指定trap命令。</FONT></FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>如果要重置某个信号的处理条件到其默认值，只需简单的将command设置为-。如果要忽略某个信号，就把command设置为空字符串‘’。一个不带参数的trap命令将列出当前设置的信号及其行动的清单。</FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>表2-11列出了X/Open规范里面规定的能够被捕获的比较重要的一些信号（括号里面的数字是传统的信号编号）。更多细节请参考signal在线手册的第七部分（man 7 signal）。</FONT></P>
<P class=ae style="MARGIN: 7.5pt 0cm 3pt"><FONT size=3>表 2-11</FONT></P>
<DIV align=center twffan="done">
<TABLE style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; WIDTH: 416.65pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 width=556 border=1>
<TBODY>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: 1pt solid" vAlign=top width=280>
<P style="MARGIN: 1.5pt 0cm; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: center" align=center>信&nbsp;&nbsp;&nbsp;&nbsp; 号</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: 1pt solid" vAlign=top width=275>
<P style="MARGIN: 1.5pt 0cm; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: center" align=center>说&nbsp;&nbsp;&nbsp;&nbsp; 明</P></TD></TR>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=280>
<P style="MARGIN: 1.2pt 0cm 1.2pt 87.1pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>HUP(1)</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=275>
<P style="MARGIN: 1.2pt 0cm 1.2pt 24.8pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>挂起，通常因终端掉线或用户退出而引发</P></TD></TR>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=280>
<P style="MARGIN: 1.2pt 0cm 1.2pt 87.1pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>INT(2)</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=275>
<P style="MARGIN: 1.2pt 0cm 1.2pt 24.8pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>中断，通常因按下Ctrl+C组合键而引发</P></TD></TR>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=280>
<P style="MARGIN: 1.2pt 0cm 1.2pt 87.1pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>QUIT(3)</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=275>
<P style="MARGIN: 1.2pt 0cm 1.2pt 24.8pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>退出，通常因按下Ctrl+\组合键而引发</P></TD></TR>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=280>
<P style="MARGIN: 1.2pt 0cm 1.2pt 87.1pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>ABRT(6)</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=275>
<P style="MARGIN: 1.2pt 0cm 1.2pt 24.8pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>中止，通常因某些严重的执行错误而引发</P></TD></TR>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=280>
<P style="MARGIN: 1.2pt 0cm 1.2pt 87.1pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>ALRM(14)</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: medium none" vAlign=top width=275>
<P style="MARGIN: 1.2pt 0cm 1.2pt 24.8pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>报警，通常用来处理超时</P></TD></TR>
<TR>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 210.25pt; PADDING-TOP: 0cm; BORDER-BOTTOM: 1pt solid" vAlign=top width=280>
<P style="MARGIN: 1.2pt 0cm 1.2pt 87.1pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>TERM(15)</P></TD>
<TD style="BORDER-RIGHT: medium none; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 206.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: 1pt solid" vAlign=top width=275>
<P style="MARGIN: 1.2pt 0cm 1.2pt 24.8pt; VERTICAL-ALIGN: baseline; TEXT-INDENT: 0cm; TEXT-ALIGN: left" align=left>终止，通常在系统关机时发送</P></TD></TR></TBODY></TABLE></DIV>
<P class=ad style="MARGIN: 11.25pt 2pt 7.5pt; TEXT-INDENT: 18pt"><FONT size=3>实验：信号处理</FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>下面的脚本演示了一些简单的信号处理方法：</FONT></P>
<P class=a1 style="MARGIN: 4.7pt 0.5pt 0pt 1.4pt"><FONT size=3><IMG height=80 src="http://book.csdn.net/BookFiles/353/img/image152.jpg" width=557 twffan="done"></FONT></P>
<P class=a1 style="MARGIN: 0cm 0.5pt 4.7pt 1.4pt"><FONT size=3><IMG height=190 src="http://book.csdn.net/BookFiles/353/img/image153.jpg" width=557 twffan="done"></FONT></P>
<P class=a1 style="MARGIN: 0cm 0.5pt 4.7pt 1.4pt"><FONT size=3><IMG height=69 src="http://book.csdn.net/BookFiles/353/img/image154.jpg" width=557 twffan="done"></FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>运行这个脚本，在每次循环时按下Ctrl+C组合键（或任何你系统上设定的中断键），我们将得到如下所示的输出：</FONT></P>
<P class=-1 style="MARGIN: 5.25pt 0cm 5.25pt 19.3pt"><FONT size=3><IMG height=168 src="http://book.csdn.net/BookFiles/353/img/image155.jpg" width=263 twffan="done"></FONT></P>
<P class=ad style="MARGIN: 11.25pt 2pt 7.5pt; TEXT-INDENT: 18pt"><FONT size=3>实验解析</FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>在这个脚本程序中，我们先用trap命令安排它在出 现一个INT（中断）信号时执行rm –f /tmp/my_tmp_file_$$命令删除临时文件。脚本程序然后进入一个while循环，只要临时文件存在，循环就一直持续下去。当用户按下 Ctrl+C组合键时，就会执行rm –f /tmp/my_tmp_file_$$语句，然后继续下一个循环。因为临时文件现在已经被删除了，所以第一个while循环将正常退出。</FONT></P>
<P style="TEXT-INDENT: 19pt"><FONT size=3>接下来，脚本程序再次调用trap命令，这次是指定 当一个INT信号出现时不执行任何命令。脚本程序然后重新创建临时文件并进入第二个while循环。这次当用户按下Ctrl+C组合键时，没有语句被指定 执行，所以采取默认处理方式，即立即终止脚本程序。因为脚本程序被立即终止了，所以永远也不会执行最后的echo和exit语句。</FONT></P>]]></description>
</item><item>
<title><![CDATA[十年学会编程]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37310</link>
<author>FoxWolf</author>
<pubDate>2008/7/1 13:48:28</pubDate>
<description><![CDATA[<A><FONT color=#000000>　</FONT></A><A><FONT color=#000000 size=3>　</FONT><SPAN class=tpc_content><FONT color=#000000 size=3>十年学会编程<BR>著者： Peter Norvig<BR><BR>翻译： Dai Yuwen<BR><BR><BR>--------------------------------------------------------------------------------<BR><BR>为何人人都这么着急？<BR>信步走进任何一家书店，你会看到名为《如何在7天内学会Java》的书，还有各种各样类似的书：在几天内或几小时内学会Visual Basic, Windows, Internet等等，一眼望不到尽头。我在Amazon 上做了如下的 强力检索 ： <BR>&nbsp; pubdate: after 1992 and title: days and &nbsp; &nbsp; (title: learn or title: teach yourself)<BR>得到了248个结果。前78个都是计算机类书籍（第79个是 Learn Bengali in 30 days）。我用"hours"替换"days"，得到了类似的结果：更多的253书。前77本是计算机类书籍，第78本是 Teach Yourself Grammar and Style in 24 Hours。在前200本书中，有96% 是计算机类书籍。 <BR>结论是：要么人们都在急急忙忙地学习计算机，要么计算机比其它任何东西都容易学。没有书籍教你在几天内学会古典音乐、量子物理，或者是养狗。 <BR><BR>让我们分析一下，象一本名为《三天内学会Pascal》的书意味着什么： <BR><BR>学习： 在三天里，你没有时间写一些重大的程序，并从成功或失败中得益。你没有时间与有经验的程序员合作，并理解在那样的环境下工作是怎么回事。一句话，你不会有时间学到太多东西。因此他们只能谈论一些肤浅的东西，而不是深入的理解。正如亚力山大教皇所说，浅尝辄止是危险的事情。 <BR><BR>Pascal： 在三天时间里，你可能学会Pascal的语法（如果你已经学过类似的语言），但你学不到更多的如何使用这些语法的知识。也就是说，假如你曾是个BASIC程序员，你可以学着用Pascal语法写出BASIC风格的程序，但你不可能了解Pascal真正的好处（和坏处）。那么关键是什么？ Alan Perlis 说过：“一种不改变你编程的思维方式的语言，不值得去学。” 一种可能的情况是：你必须学一点儿Pascal（或可能性更大的象Visual Basic 或 JavaScript之类），因为你为了完成某种特定的任务，需要与一个现存的工具建立接口。不过那不是学习如何编程，而是在学习如何完成那个任务。 <BR><BR>三天内： 很不幸，这不够，原因由下一节告诉我们。 <BR>在十年里学会编程<BR>研究表明 (Hayes，Bloom)在任何一种领域内，象下棋、作曲、绘画、钢琴演奏、游泳、网球、以及原子物理学和拓扑学，等等，要达到专家水平大约都要化十年时间。没有真正的捷径：即使是莫扎特，4岁时就是音乐神童，13年后才开始写出世界级的作品。在另一方面，披头士似乎在1964年的Ed Sullivan表演上一炮走红。但他们从1957年就开始表演，在获得大众青睐后，他们的第一个重大成功，Sgt. Peppers，是1967年发行的。Samuel Johnson （塞缪尔·约翰逊，英国辞典编纂家及作家）认为要花比十年更长的时间：“在任何领域中出类拔萃都要用毕生的劳作来取得；它不可能用较低的代价获得。” 而Chaucer（乔叟，英国诗人）感叹到：“人生短暂，学海无涯。” <BR>这是我为编程成功开出的方子： <BR><BR>设法对编程感兴趣，并且因为它有趣而编一些程序。确保编程一直充满足够乐趣，这样你才愿意投入十年宝贵时间。 <BR><BR>与其他程序员交流； 阅读其它程序。这比任何书本或训练课程都重要。 <BR><BR>写程序。 最好的学习方式是 从实践中学习。 用更技术性的话说，“在一个给定的领域内，个人的最大能力不是自动地由扩展了的经验取得的，但即使是高度有经验的人也可以通过有意识的努力来提高自己的能力” (p. 366) 和 “最有效的学习需要因人而异的适当难度，目标明确的任务，丰富的信息反馈，以及重复的机会和错误修正。” (p. 20-21) 此书 Cognition in Practice: Mind，Mathematics，and Culture in Everyday Life 是阐明此观点的令人感兴趣的参考文献。 <BR><BR>如果愿意，在大学里呆上4年或更长（在研究生院里）。你会接触到一些需要学历证明的工作，你会对此领域有更深的理解。如果你不喜欢学校，你可以（通过一些贡献）在工作中获得相似的经验。在任何情况下，光啃书本是不够的。Eric Raymond，The New Hacker's Dictionary一书的作者，说过，“计算机科学不能把任何人变成编程专家，就象光研究刷子和颜料不会使人变成画家一样。” 我雇佣过的最好的程序员之一仅有高中程度；他做出了许多优秀的 软件，有他自己的新闻组，而且通过股票期权，他无疑比我富有的多。 <BR><BR>和其他程序员一起做项目。在其中的一些项目中作为最好的程序员； 而在另一些项目中是最差的。当你是最好的，你能测试领导项目的能力，用你的观点激发别人。当你是最差的，你学习杰出者是怎么做的，了解他们不喜欢做什么（因为他们吩咐你做事）。 <BR><BR>在其他程序员 之后接手项目。使自己理解别人写的程序。当程序的原作者不在的时候，研究什么需要理解并且修改它。思考如何设计你的程序以便后来者的维护。 <BR><BR>学习至少半打的编程语言。包括一种支持类抽象的语言（象Java 或C++），一种支持函数化抽象的语言（象Lisp或ML），一种支持语法抽象的语言（象 Lisp），一种支持声明规格说明的语言（象Prolog或C++ 的模板），一种支持共行程序（coroutine）的语言（象Icon或Scheme），一种支持并行的语言（象Sisal）。 <BR><BR>请记住“计算机科学”中有“计算机”一词。了解你的计算机要花多长时间执行一条指令，从内存中取一个字（有cache），从磁盘中读取连续的字，和在磁盘中找到新的位置。（答案） <BR><BR>参与一种语言标准化的工作。它可以是ANSI C++委员会，也可以是决定你周围小范围内的编程风格是应该两个还是四个空格缩进。通过任何一种方式，你了解到其他人在某种语言中的想法，他们的理解深度，甚至一些他们这样想的原因。 <BR><BR>找到适当的理由尽快地从语言标准化的努力中脱身。 <BR>明白了这些，仅从书本中你能得到多少就成了一个问题。在我第一个孩子出生前，我读了所有的（关于育儿的）How to 书籍，仍然感觉是个手足无措的新手。30个月以后，我的第二个孩子快要出生了，我回头温习这些书了吗？ 没有。相反，我依靠我的个人经验，它比专家写的数千页书更有用和可靠。 <BR><BR>Fred Brooks在他的随笔 《没有银弹》 中定出了一个寻找优秀软件设计者的三步计划： <BR><BR>尽可能早地，有系统地识别顶级的设计人员。 <BR><BR>为设计人员指派一位职业导师，负责他们技术方面的成长，仔细地为他们规划职业生涯。 <BR><BR>为成长中的设计人员提供相互交流和学习的机会。 <BR><BR>此计划假设某些人已经具备了杰出设计者的必要才能； 要做的只是如何恰当地诱导他们。 Alan Perlis 说得更简明扼要：“每个人都能被教会雕刻：对米开朗其罗而言，反倒是告诉他哪些事不要做。同样的道理也适用于优秀的程序员。” <BR>所以尽管买那本Java的书吧。你可能会从中学到点儿东西。但作为一个程序员，你不会在几天内或24小时内，哪怕是几个月内改变你的人生，或你实际的水平。 <BR><BR><BR>参考文献<BR>Bloom, Benjamin (ed.) Developing Talent in Young People, Ballantine, 1985. <BR><BR>Brooks, Fred, No Silver Bullets, IEEE Computer, vol. 20, no. 4, 1987, p. 10-19. <BR><BR>Hayes, John R., Complete Problem Solver Lawrence Erlbaum, 1989. <BR><BR>Lave, Jean, Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life, Cambridge University Press, 1988. <BR><BR>答案<BR>2001年夏天典型的1GHz PC的各种操作要花的时间 <BR><BR>执行一条指令 1 nsec = (1/1,000,000,000) sec <BR>从L1 cache memory 中取一个字 2 nsec <BR>从内存中取一个字 10 nsec <BR>从磁盘的连续位置取一个字 200 nsec <BR>从磁盘的新位置取一个字(seek) 8,000,000nsec = 8msec <BR><BR><BR>附录：语言的选择<BR>不少人问我，他们首先该学哪种编程语言。没有绝对的答案，不过请考虑以下几点： <BR><BR>用你的朋友的。当被问起“我该用哪种操作系统，Windows，Unix，还是Mac？”，我总是回答：“你朋友用什么，你就用什么。” 你从朋友那能学到知识，这种优势可以抵销不同操作系统或语言之间本质的差异。也考虑你将来的朋友：程序员社区 — 你将成为它的一部分如果你继续往前走的话。你选择的语言是否有一个成长中的社区，还是人数不多、即将消亡？ 有没有书籍、网站、在线论坛回答你的问题？ 你喜欢论坛里的那些人吗？ <BR>Keep it simple, stupid. 象C++和Java这样的语言是为经验丰富的程序员组成的团队进行专业开发而设计的，他们专注于代码运行时的效率。因此，这些语言有些部分非常复杂。 而你关注的是如何编程，不需要那些复杂性。你需要的是这样的语言： 对单个的编程新手来说，它易学易记。 <BR>练习。你偏爱哪种学弹钢琴的方式：通常的交互式的方式，你一按下琴键就能听到音符；还是“批量”模式，你只有弹完整首曲子才能听到音符？显然，用交互模式学习弹钢琴更容易些，编程也一样。坚持用交互模式学习并使用一种语言。 <BR>有了上面的准则，我推荐的第一个编程语言是Python或Scheme。因人而异，还有其它好的选择。如果你的年纪是10岁以下，你可能更喜欢Alice。关键是你要选择并开始实践。 <BR><BR>附录：书籍和其它资源<BR>不少人问我，他们该从什么书籍或网页开始学起。我重申“仅从书本里学习是不够的。” 但我还是推荐： <BR><BR>Scheme: Structure and Interpretation of Computer Programs (Abelson &amp; Sussman)可能是最好的计算机科学的入门书，而且它的确把讲授编程作为理解计算机科学的一种方法。但它具有挑战性，会让许多通过其它方式可能成功的人望而却步。 <BR>Scheme: How to Design Programs (Felleisen et al.)是关于如何用一种优美的、函数化的方式设计程序的最好的书之一。 <BR>Python: Python Programming: An Intro to CS (Zelle)是优秀的Python入门指导。 <BR>Python: Python.org上有许多在线指导。 <BR>Oz: Concepts, Techniques, and Models of Computer Programming (Van Roy &amp; Haridi) 被视为Abelson &amp; Sussman的当代继承者。它是对编程的高层次概念的巡视。涉及的范围比Abelson &amp; Sussman更广，同时可能更容易学习和跟进。 它用了叫做Oz的语言，不太知名，却可以作为学习其它语言的基础。</FONT></SPAN></A>]]></description>
</item><item>
<title><![CDATA[一些值得IT人一看的人生感悟]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37309</link>
<author>FoxWolf</author>
<pubDate>2008/7/1 13:34:58</pubDate>
<description><![CDATA[<A><FONT size=3>　</FONT></A> 
<DIV class=postcontent>
<P><FONT size=3>1. “白天是为了生存，晚上是为了发展”</FONT></P>
<P><FONT size=3>一个人白天辛苦工作大都是为了解决生存问题，而晚上还努力工作或继续学习的人大都为了发展.</FONT></P>
<P><FONT size=3>2.”一个人对他的工作热爱不热爱，看看下班时间到的时候他的表现就知道了。如果你很清楚什么时候下班时间到了，说明你根本不热爱你的工作。”</FONT></P>
<P><FONT size=3>一位曾经跨国企业老总对我说。</FONT></P>
<P><FONT size=3>3. “不要说客户罗嗦，客户刁难，那只能说明你失败。如果你是他儿子，就是一份卖身协议他也许都毫不犹豫。”</FONT></P>
<P><FONT size=3>一次面见客户出来，我怪客户罗嗦的时候，一位前辈当成教训我的话</FONT></P>
<P><FONT size=3>4. “你是我见过的最好的业务员，因为你是第一个真正为我们考虑的人”</FONT></P>
<P><FONT size=3>这句话一直鼓励我，比任何时候拿业绩第一名都开心。</FONT></P>
<P><FONT size=3>5. ” 来听别人的课程，听别人的培训，你不要奢望学到太多东西。如果你记得他讲个的一句简单有用的话。那就证明这堂课是值得的”</FONT></P>
<P><FONT size=3>广州元盛的一位培训成员2004年在一次经验交流会上说。</FONT></P>
<P><FONT size=3>6.”三流的企业卖产品，二流的企业卖服务，一流的企业卖理念”</FONT></P>
<P><FONT size=3>不知什么时候起，也不知道看了那本书，我突然如此感叹</FONT></P>
<P><FONT size=3>7. “看世界悲观一点，你就会坦然面对一切”</FONT></P>
<P><FONT size=3>受罗刚的影响，我相信这句话。天助自助者。不要奢望别人对好或者给你帮助，不要奢望奇迹。这世界从来就没有奇迹，只有艰辛才能换来成功。</FONT></P>
<P><FONT size=3>8。“付出不一定有收获，但别人有收获一定是因为他付出了”</FONT></P>
<P><FONT size=3>当我失败的时候，以这句话来慰籍心灵</FONT></P>
<P><FONT size=3>9。“如果这一次的挫折失败我都经历过了，我以后也许就再也没有什么问题可以难倒我了”</FONT></P>
<P><FONT size=3>有时候，我认为自己遇到的事情，是一辈子最艰难的（至少在当时是那样想的），我就会这样想。我就会怕挫折和苦难当作一种历练。经历以后，就再也没有东西可以打倒我。</FONT></P>
<P><FONT size=3>10。” 你想不想成功，你是好象要，还是要，应该要，还是一定要？只有一定要的人你才会成功”</FONT></P>
<P><FONT size=3>一个上午读完了陈安之的几本书，唯一得出的感悟就是这一句。只有一定要的人才能成功。</FONT></P>
<P><FONT size=3>11。“不要找借口，说是不可能完成的事情。如果我现在要求你们在一个小时内背下100个单词，你们大部分的人都完不成这个任务，但如果我再加一个条件，如果背不出来的，全部枪毙，我估计大多数人能背下来了”</FONT></P>
<P><FONT size=3>听胡敏谈英语学习记下的一句话。</FONT></P>
<P><FONT size=3>12。人们对你的期望就是唐僧的紧箍咒，你有多大的本事，你有多高的地位，人们对你的期望也随之而长。所以你想快乐必须学会自我欣赏。不然你永远活在别人的期望之中而不得快乐。</FONT></P>
<P><FONT size=3>从用来安慰别人，尤其是那些说要是有钱了，我就快乐就没有压力了的人。一个博士和一个农民，他们出生的时候是一样的，给予的条件也几乎一样的。但如果20年 后，博士每个月3000块的工资，亲人朋友会嘲笑，如果农民每个月3000元，周围的人会说他了不起。谁都忘记了，其实曾经给予的条件和起点都是一样的。</FONT></P></DIV>]]></description>
</item><item>
<title><![CDATA[揭秘成为最牛程序员的五大要诀]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=37308</link>
<author>FoxWolf</author>
<pubDate>2008/7/1 13:05:57</pubDate>
<description><![CDATA[<A><FONT size=3>　</FONT></A><A><FONT size=3>　看了一下，感觉不错。放在这里，不断领悟！ </FONT>
<P><FONT size=3>想成为最牛程序员吗？以下几点有助你实现这个目标。 </FONT></P>
<P><FONT color=#5050f3 size=3>Tip 1 要方法而不是记忆</FONT></P>
<P><FONT size=3>我的一个程序员朋友常跟我说记住超过200个C++函数是多么的有帮助。“我从来不必去查找函数的定义，因此我可以比其他程序员编程快上50%。”他自豪的说。可结果是什么？难道他不知道编译器的代码自动完成功能可以节约大量查找函数及输入函数的时间吗，另外当C#发布出来后，他在记忆函数上面的努力就白费了。当然，编程中对函数的熟记是一件必需的事情，但是你应当花费更多的时间在学习做事的方法上，比如说创建一个数据库连接，如何产生RSS源等，然后是关注于代码是如何实现的。学习做事的正确方法远比死记硬背重要。 </FONT></P>
<P><FONT color=#5050f3 size=3>Tip 2 建立属于你自己的资源库</FONT></P>
<P><FONT size=3>我们都会有因为这样或者那样原因而不得不建立的代码集。我从来不记得连接数据库的准确代码语句，所以我每次都不得不在代码集中花10分钟去查询它。为了解决这个问题，我创建了一个用于记录代码片段的Word文档，以帮助我记忆和查找。我的一个同事建了个记录链接的书签，另外一个同事在他的邮件中存储了这些内容。无论你的方法是什么，都是一种可以使你方便查找到文件或内容的好习惯。当你建立你的知识库后，你会发现它将极大的帮助你去把代码写得更好和更快。 </FONT></P>
<P><FONT size=3><FONT color=#5050f3>Tip 3</FONT> 知道做什么而不是怎样做很多初级程序员问我“我怎样做这个，或者我怎样做那个？”我总是会跟他们说“你想做什么呢？”听闻此言后，他们会死盯着我，就好像我跟他们的妈妈约会了一样。这就是我的下一个观点，绝不要在知道你想做什么之前去学习怎样做，比如一个程序员想要搜索一个文本文件中是否存在的某个特定的词汇。下面是用C#来实现该目的： </FONT></P>
<P><FONT size=3>view plaincopy to clipboardprint? </FONT></P>
<P><FONT size=3>string&nbsp;fileContent; &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>System.IO.FileStream&nbsp;myStream&nbsp;=&nbsp;new&nbsp;FileStream("c:\\aa.txt",&nbsp;FileMode.Open); &nbsp;&nbsp; System.IO.StreamReader&nbsp;myStreamReader&nbsp;=&nbsp;new&nbsp;StreamReader(myStream);&nbsp; &nbsp;&nbsp; fileContent&nbsp;=&nbsp;myStreamReader.ReadToEnd(); &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>myStreamReader.Close(); &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>int&nbsp;idx&nbsp;=&nbsp;fileContent. &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>IndexOf("string");&nbsp; &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>if&nbsp;(idx) &nbsp;&nbsp; { &nbsp;&nbsp; return&nbsp;true&nbsp;&nbsp; }&nbsp;&nbsp;string fileContent; </FONT></P>
<P><FONT size=3>System.IO.FileStream myStream = new FileStream("c:\\aa.txt", FileMode.Open); System.IO.StreamReader myStreamReader = new StreamReader(myStream); </FONT></P>
<P><FONT size=3>fileContent = myStreamReader.ReadToEnd(); </FONT></P>
<P><FONT size=3>myStreamReader.Close(); </FONT></P>
<P><FONT size=3>int idx = fileContent. </FONT></P>
<P><FONT size=3>IndexOf("string"); </FONT></P>
<P><FONT size=3>if (idx) { return true } </FONT></P>
<P><FONT size=3>现在我给他这些代码去做这件事，但是更重要的是理解自己正在试着做的是什么。在这个例子中我们想做的是： 1.&nbsp;打开一个文件 2.&nbsp;读其中的内容 3.&nbsp;关闭文件 4.&nbsp;搜索字串 5.&nbsp;如果找到了则输出结果用这个方法来解决事情产生了以下结果： 1.&nbsp;它使语言无关 2.&nbsp;使你的精力集中在需要做什么上 3.&nbsp;使你的代码更易读和有效知道要做什么将使你的代码更有目的性。现在在C++、PHP、VB.NET、Ruby on Rails中编写上述代码是很容易的事情了，因为你理解了要做什么而不是怎样去做。 </FONT></P>
<P><FONT color=#5050f3 size=3>Tip 4 创建适合你的注释风格</FONT></P>
<P><FONT size=3>每一个程序员都讨厌注释，但是为了写出更有质量和易读的代码，我们需要注释。问题是大多数程序员常被告知如何注释，一些公司希望每一行代码都有注释，另外一些则想要在每个函数前面有一段注释，还有的规定在不同的代码块前注释。我并不同意这种强制性的规定，只要代码是可用的、易读的和有效的，那么程序员应当可以用其个人喜好的格式来注释。对我来说在每一行都注释将破坏代码的节奏，我更喜欢在函数的前面注释，罗列我接下来一步步将要做什么，然后在函数中参考注释中所写的步骤进行编程。这是适合我的模式，这样可以在我编程前帮助我组织设计，也保持了我的节奏，使我不会因为需要注释而在编程时中断，也有助于其他人阅读我的代码。</FONT></P>
<P><FONT size=3>下面是我怎样注释的例子： </FONT></P>
<P><FONT size=3>view plaincopy to clipboardprint? </FONT></P>
<P><FONT size=3>/*&nbsp;1.&nbsp;Open&nbsp;File*&nbsp; &nbsp; </FONT></P>
<P><FONT size=3>2.&nbsp;Read&nbsp;file&nbsp;into&nbsp;string*&nbsp; &nbsp; </FONT></P>
<P><FONT size=3>3.&nbsp;Close&nbsp;file*&nbsp; &nbsp; </FONT></P>
<P><FONT size=3>4.&nbsp;Search&nbsp;for&nbsp;key&nbsp;word*&nbsp; &nbsp; </FONT></P>
<P><FONT size=3>5.&nbsp;If&nbsp;fond&nbsp;return&nbsp;true; &nbsp; </FONT></P>
<P><FONT size=3>*/&nbsp;&nbsp; </FONT></P>
<P><FONT size=3>string&nbsp;fileContent; &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>//1. &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>System.IO.FileStream&nbsp;myStream&nbsp;=&nbsp;new&nbsp;FileStream("c:\\aa.txt",&nbsp;FileMode.Open); &nbsp;&nbsp; System.IO.StreamReader&nbsp;myStreamReader&nbsp;=&nbsp;new&nbsp;StreamReader(myStream); &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>//2. &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>fileContent&nbsp;=&nbsp;myStreamReader.ReadToEnd(); &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>//3. &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>myStreamReader.Close();&nbsp; &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>//4. &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>int&nbsp;idx&nbsp;=&nbsp;fileContent.IndexOf("string"); &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>if&nbsp;(idx)&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;</FONT></P>
<P><FONT size=3>&nbsp;//5. &nbsp;&nbsp; return&nbsp;true; &nbsp;&nbsp; </FONT></P>
<P><FONT size=3>}&nbsp;&nbsp;</FONT></P>
<P><FONT size=3>&nbsp;这种注释风格使我和大多数程序员可以容易的阅读它。那么，找一个适合你的注释风格吧。 </FONT></P>
<P><FONT size=3><FONT color=#5050f3>Tip 5 精通one，学习another，关注next</FONT> </FONT></P>
<P><FONT size=3>有时有程序员发email问我他应该学习什么语言，什么是最好的编程语言等等。你至少应该精通一门编程语言，可以相当好的去编写代码，然后再去学习掌握另外一门，逐渐的成长。以我自己为例，我精通C#，擅长PHP，并且已经开始使用Ruby on Rails大概有一两个月了。为什么呢？精通一门语言可以使你进步，在进步中写更好的代码，找到完成任务更好的方法等。进步也是我作为一个程序员年复一年的工作，却仍没有觉得枯燥的原因。</FONT></P></A>]]></description>
</item><item>
<title><![CDATA[内存芯片：Bank与芯片容量的新表示]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36941</link>
<author>FoxWolf</author>
<pubDate>2008/6/19 9:21:48</pubDate>
<description><![CDATA[<P><A><FONT size=2>　</FONT></A><FONT size=2>计算机技术的发展对存储容量的需求越来越大，这首先要求单个存储芯片具有更大的容量。从芯片制造工艺考虑，增加容量的一种方法是增加存储矩阵的个数，由传统的一个增至2个、4个、8个或更多，称这些存储矩阵为Bank。这些Bank使用公共的地址缓冲、译码、数据缓冲及读／写等控制逻辑，每一时刻只有一个Bank工作，Bank的选择是由新增的Bank编码输入线决定。为和内存条的Bank相区别，称之为逻辑Bank。</FONT></P>
<P><FONT size=2>&nbsp;&nbsp;&nbsp; 图11．7所示是一个容量为32 MB(256 Mbit)的存储芯片内部结构示意图。从中可看出，它有4个存储矩阵，即4个Bank，设置了两根Bank编码输入引脚BA0、BAl；它的数据线为8根，即它的芯片字为8位宽。图中Bank都标有“8 M×8”字样，表示一个Bank容量为8 M个8 位(8 MB)。于是，4个Bank即整个芯片的容量为8 M×8×4＝256 Mbit＝32 MB。对于多Bank芯片，面向用户的存储容量以“A×B×C”形式表示，其中A为每个Bank的芯片字的个数，B为芯片字位数，即芯片数据宽度，C为Bank的个数。这种形式也可体现出芯片内部的结构形式。例如，对于目前常用的64 Mbit芯片，有三种结构形式，即三个型号的芯片：4 M</FONT></P>
<P><FONT size=2>×4(bit)×4(Bank)、2 M×8(bit)×4(Bank)&nbsp; 1 M×16(bit)×4(Bank)。</FONT></P>
<P><IMG style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="http://blogger.org.cn/blog/uploadfile/200861992140215.JPG" border=0></P>]]></description>
</item><item>
<title><![CDATA[父亲赞]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36860</link>
<author>FoxWolf</author>
<pubDate>2008/6/16 13:45:47</pubDate>
<description><![CDATA[<A><FONT size=2>　</FONT></A>
<P><FONT size=2>今天是父亲节，地震中父亲们坚毅的背影清晰可见。父爱如山，稳重厚实而威严；父爱如水，平静舒缓而绵长；父爱如火，熠熠燃烧而温暖；父爱如天，宽广蔚蓝而博大.</FONT></P>
<P><FONT size=2>父亲是一座山，如山般屹立，是榜样，是丰碑，永远给我们以坚毅的力量。人们常说“母爱如海，父爱如山”，山是无言的，父爱也是无言的。</FONT></P>
<P><FONT size=2>父亲，担当社会角色，呵护孩子成长，帮助妻子理家，承担男人责任。高尔基说：“父爱是一部震撼心灵的巨著，读懂了它你就读懂了整个人生！”。父亲是信念，父爱就是那座耸立在孩子心中的大山；父亲是依靠，父爱就是给人温暖、归宿和安全的港湾；父亲是力量，父爱就是滋生、输出坚强和勇敢的烈火；父爱是蓝天，父爱就是那本色的蔚蓝。</FONT></P>
<P><FONT size=2>感谢父亲吧，他和母亲一起养育了你，给你成才立世的根本；欣赏父亲吧，他的才干和能力，哪些怕看上去微不足道，但他已经尽力；礼赞父亲吧，他燃烧自己，向亲人、他人和社会奉献了光和热；呵护父亲吧，他的无助、困顿、忧愁与痛苦，同样需要理解、倾听与分担；孝敬父亲吧，就凭你们血脉相连，就赁他爱子如命、舍己心甘；原谅父亲吧，体谅他的难处，谅解他的痛处......</FONT></P>]]></description>
</item><item>
<title><![CDATA[使用 getopt() 进行命令行处理]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36801</link>
<author>FoxWolf</author>
<pubDate>2008/6/13 13:06:30</pubDate>
<description><![CDATA[
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<H1>使用 getopt() 进行命令行处理</H1>
<P id=subtitle>轻松处理复杂命令行</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192><IMG height=18 alt=developerWorks src="http://www.ibm.com/developerworks/i/dw.gif" width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>文档选项</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www.ibm.com/developerworks/secure/email-it.jsp cM1="1"><INPUT type=hidden value="所有 UNIX(R) 程序，甚至那些具有图形用户界面（graphical user interface，GUI）的程序，都能接受和处理命令行选项。对于某些程序，这是与其他程序或用户进行交互的主要手段。如果具有可靠的复杂命令行参数处理机制，会使得您的应用程序更好、更有用。不过很多开发人员都将其宝贵的时间花在了编写自己的命令行解析器，却不使用 getopt()，而后者是一个专门设计来减轻命令行处理负担的库函数。请阅读本文，以了解如何让 getopt() 在全局结构中记录命令参数，以便之后随时在整个程序中使用。" name=body cM1="1" cM3 cM2="0"><INPUT type=hidden value="使用 getopt() 进行命令行处理" name=subject cM1="1" cM3 cM2="1"><INPUT type=hidden value=cn name=lang cM1="1" cM3 cM2="2">
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
 
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();" cmImpressionSent="1"><B><FONT color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT><tr valign="top"><td width="8"><img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small" width="122"><p><span class="ast">未显示需要 JavaScript 的文档选项</span></p></td></tr></NOSCRIPT></FORM>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7 size=2><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7 size=2><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/dn.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#download" cmImpressionSent="1"><B><FONT color=#996699 size=2>样例代码</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><!--END RESERVED FOR FUTURE USE INCLUDE FILES--><BR></TD></TR></TBODY></TABLE>
<P>级别： 中级</P>
<P><A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#author" cmImpressionSent="1"><FONT color=#996699>Chris Herborth</FONT></A> (<A href="mailto:chrish@pobox.com?subject=使用 getopt() 进行命令行处理" cmImpressionSent="1"><FONT color=#5c81a7>chrish@pobox.com</FONT></A>), 自由撰稿人, 作家<BR></P>
<P>2006 年 5 月 25 日</P>
<BLOCKQUOTE>所有 UNIX&reg; 程序甚至那些具有图形用户界面（graphical user interface，GUI）的程序，都能接受和处理命令行选项。对于某些程序，这是与其他程序或用户进行交互的主要手段。具有可靠的复杂命令行参数处理机制，会使得您的应用程序更好、更有用。不过很多开发人员都将其宝贵的时间花在了编写自己的命令行解析器，却不使用 <CODE>getopt()</CODE>，而后者是一个专门设计来减轻命令行处理负担的库函数。请阅读本文，以了解如何让 <CODE>getopt()</CODE> 在全局结构中记录命令参数，以便随后随时在整个程序中使用。</BLOCKQUOTE><!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
<P><A name=N10075><SPAN class=atitle>引言</SPAN></A></P>
<P>在早期的 UNIX&reg; 中，其命令行环境（当时的唯一用户界面）包含着数十种小的文本处理工具。这些工具非常小，通常可很好地完成一项工作。这些工具通过较长的命令管道链接在一起，前面的程序将其输出传递给下一个程序以作为输入，整个过程由各种命令行选项和参数加以控制。 </P>
<P>正是 UNIX 的这方面的特征使其成为了极为强大的处理基于本文的数据的环境，而这也是其在公司环境中的最初用途之一。在命令管道的一端输入一些文本，然后在另一端检索经过处理的输出。 </P>
<P>命令行选项和参数控制 UNIX 程序，告知它们如何动作。作为开发人员，您要负责从传递给您程序的 <CODE>main()</CODE> 函数的命令行发现用户的意图。本文将演示如何使用标准 <CODE>getopt()</CODE> 和 <CODE>getopt_long()</CODE> 函数来简化命令行处理工作，并讨论了一项用于跟踪命令行选项的技术。</P>
<P><A name=N10093><SPAN class=atitle>开始之前</SPAN></A></P>
<P>本文包含的示例代码（请参见<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#download" cmImpressionSent="1"><FONT color=#996699>下载</FONT></A>）是使用 C 开发工具（C Development Tooling，CDT）在 Eclipse 3.1 中编写的；<I>getopt_demo</I> 和 <I>getopt_long_demo</I> 项目是 <I>Managed Make</I> 项目，均使用 CDT 的程序生成规则构建。在项目中没有包含 Makefile，如果需要在 Eclipse 外编译代码，可以自己方便地生成一个。 </P>
<P>如果尚未尝试过 Eclipse（请参阅<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#resources" cmImpressionSent="1"><FONT color=#996699>参考资料</FONT></A>），真的应该尝试一下——这是一个优秀的集成开发环境（integrated development environment，IDE），其每个新版本都有较大的提升。这是来自“强硬派” EMACS 和 Makefile 开发人员的作品。</P>
<P><A name=N100B1><SPAN class=atitle>命令行</SPAN></A></P>
<P>在编写新程序时，首先遇到的障碍之一就是如何处理控制其行为的命令行参数。这包括从命令行传递给您程序的 <CODE>main()</CODE> 函数的一个整数计数（通常名为 <I>argc</I>）和一个指向字符串的指针数组（通常名为 <I>argv</I>）.可以采用两种实质一样的方式声明标注 <CODE>main()</CODE> 函数，如<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing1" cmImpressionSent="1"><FONT color=#996699>清单 1</FONT></A> 中所示。</P><BR><A name=listing1><B>清单 1. 声明 main() 函数的两种方式</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
int main( int argc, char *argv[] );
int main( int argc, char **argv );
</PRE></TD></TR></TBODY></TABLE><BR>
<P>第一种方式使用的是指向 <CODE>char</CODE> 指针数组，现在似乎很流行这种方式，比第二种方式（其指针指向多个指向 <CODE>char</CODE> 的指针）略微清楚一些。由于某些原因，我使用第二种方式的时间更多一些，这可能源于我在高中时艰难学习 C 指针的经历。对于所有的用途和目的，这两种方法都是一样的，因此可以使用其中您自己最喜欢的方式。</P>
<P>当 C 运行时库的程序启动代码调用您的 <CODE>main()</CODE> 时，已经对命令行进行了处理。<CODE>argc</CODE> 参数包含参数的计数值，而 <CODE>argv</CODE> 包含指向这些参数的指针数组。对于 C 运行时库，<I>arguments</I> 是程序的名称，程序名后的任何内容都应该使用空格加以分隔。</P>
<P>例如，如果使用参数 <CODE>-v bar www.ibm.com</CODE> 运行一个名为 <I>foo</I> 程序，您的 argc 将设置为 4，<CODE>argv</CODE> 的设置情况将如<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing2" cmImpressionSent="1"><FONT color=#996699>清单 2</FONT></A> 中所示。 </P><BR><A name=listing2><B>清单 2. argv 的内容</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
argv[0] - foo
argv[1] - -v
argv[2] - bar
argv[3] - www.ibm.com
</PRE></TD></TR></TBODY></TABLE><BR>
<P>一个程序仅有一组命令行参数，因此我要将此信息存储在记录选项和设置的全局结构中。对程序有意义的要跟踪的任何内容都可以记录到此结构中，我将使用结构来帮助减少全局变量的数量。正如我在网络服务设计文章（请参阅<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#resources" cmImpressionSent="1"><FONT color=#996699>参考资料</FONT></A>）所提到的，全局变量非常不适合用于线程化编程中，因此要谨慎使用。</P>
<P>示例代码将演示一个假想的 doc2html 程序的命令行处理。该 doc2html 程序将某种类型的文档转换为 HTML，具体由用户指定的命令行选项控制。它支持以下选项：</P>
<UL>
<LI><CODE>-I</CODE>——不创建关键字索引。 
<LI><CODE>-l lang</CODE>——转换为使用语言代码 <CODE>lang</CODE> 指定的语言。 
<LI><CODE>-o outfile.html</CODE>——将经过转换的文档写入到 outfile.html，而不是打印到标准输出。 
<LI><CODE>-v</CODE>——进行转换时提供详细信息；可以多次指定，以提高诊断级别。 
<LI>将使用其他文件名称来作为输入文档。 </LI></UL>
<P>您还将支持 <CODE>-h</CODE> 和 <CODE>-?</CODE>，以打印帮助消息来提示各个选项的用途。</P>
<P><A name=N1014F><SPAN class=atitle>简单命令行处理： getopt() </SPAN></A></P>
<P><CODE>getopt()</CODE> 函数位于 unistd.h 系统头文件中，其原型如<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing3" cmImpressionSent="1"><FONT color=#996699>清单 3</FONT></A> 中所示：</P><BR><A name=listing3><B>清单 3. getopt() 原型</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
int getopt( int argc, char *const argv[], const char *optstring );
</PRE></TD></TR></TBODY></TABLE><BR>
<P>给定了命令参数的数量 (<CODE>argc</CODE>)、指向这些参数的数组 (<CODE>argv</CODE>) 和选项字符串 (<CODE>optstring</CODE>) 后，<CODE>getopt()</CODE> 将返回第一个选项，并设置一些全局变量。使用相同的参数再次调用该函数时，它将返回下一个选项，并设置相应的全局变量。如果不再有识别到的选项，将返回 <CODE>-1</CODE>，此任务就完成了。</P>
<P><CODE>getopt()</CODE> 所设置的全局变量包括：</P>
<UL>
<LI><CODE>optarg</CODE>——指向当前选项参数（如果有）的指针。 
<LI><CODE>optind</CODE>——再次调用 <CODE>getopt()</CODE> 时的下一个 argv 指针的索引。 
<LI><CODE>optopt</CODE>——最后一个已知选项。 </LI></UL>
<P>对于每个选项，选项字符串 (<CODE>optstring</CODE>) 中都包含一个对应的字符。具有参数的选项（如示例中的 <CODE>-l</CODE> 和 <CODE>-o</CODE> 选项）后面跟有一个 <CODE>:</CODE> 字符。示例所使用的 <CODE>optstring</CODE> 为 <CODE>Il:o:vh?</CODE>（前面提到，还要支持最后两个用于打印程序的使用方法消息的选项）。</P>
<P>可以重复调用 <CODE>getopt()</CODE>，直到其返回 <CODE>-1</CODE> 为止；任何剩下的命令行参数通常视为文件名或程序相应的其他内容。 </P>
<P><A name=N101D3><SPAN class=atitle>getopt() 的使用</SPAN></A></P>
<P>让我们对 getopt_demo 项目的代码进行一下深入分析；为了方便起见，我在此处将此代码拆分为多个部分，但您可以在可下载源代码部分获得完整的代码（请参见<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#download" cmImpressionSent="1"><FONT color=#996699>下载</FONT></A>）。</P>
<P>在<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing4" cmImpressionSent="1"><FONT color=#996699>清单 4</FONT></A> 中，可以看到系统演示程序所使用的系统头文件；标准 <CODE>stdio.h</CODE> 提供标准 I/O 函数原型，<CODE>stdlib.h</CODE> 提供 <CODE>EXIT_SUCCESS</CODE> 和<CODE>EXIT_FAILURE</CODE>，<CODE>unistd.h</CODE> 提供 <CODE>getopt()</CODE>。</P><BR><A name=listing4><B>清单 4. 系统头文件 </B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
</PRE></TD></TR></TBODY></TABLE><BR>
<P><A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing5" cmImpressionSent="1"><FONT color=#996699>清单 5</FONT></A> 显示了我所创建的 <CODE>globalArgs</CODE> 结构，用于以合理的方式存储命令行选项。由于这是个全局变量，程序中任何位置的代码都可以访问这些变量，以确定是否创建关键字索引、生成何种语言等等事项。最好让 <CODE>main()</CODE> 函数外的代码将此结构视为一个常量、只读存储区，因为程序的任何部分都可以依赖于其内容。</P>
<P>每个命令行选择都有一个对应的选项，而其他变量用于存储输出文件名、指向输入文件列表的指针和输入文件数量。 </P><BR><A name=listing5><B>清单 5. 全局参数存储和选项字符串</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
struct globalArgs_t {
    int noIndex;                /* -I option */
    char *langCode;             /* -l option */
    const char *outFileName;    /* -o option */
    FILE *outFile;
    int verbosity;              /* -v option */
    char **inputFiles;          /* input files */
    int numInputFiles;          /* # of input files */
} globalArgs;

static const char *optString = "Il:o:vh?";
</PRE></TD></TR></TBODY></TABLE><BR>
<P>选项字符串 <CODE>optString</CODE> 告知 <CODE>getopt()</CODE> 可以处理哪个选项以及哪个选项需要参数。如果在处期间遇到了其他选项，<CODE>getopt()</CODE> 将显示一个错误消息，程序将在显示了使用方法消息后退出。</P>
<P>下面的<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing6" cmImpressionSent="1"><FONT color=#996699>清单 6</FONT></A> 包含一些从 <CODE>main()</CODE> 引用的用法消息函数和文档转换函数的小存根。可以对这些存根进行自由更改，以用于更为有用的目的。 </P><BR><A name=listing6><B>清单 6. 存根</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
void display_usage( void )
{
    puts( "doc2html - convert documents to HTML" );
    /* ... */
    exit( EXIT_FAILURE );
}

void convert_document( void )
{
    /* ... */
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>最后，如<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing7" cmImpressionSent="1"><FONT color=#996699>清单 7</FONT></A> 中所示，在 <CODE>main()</CODE> 函数中使用此结构。和优秀的开发人员一样，您需要首先初始化 <CODE>globalArgs</CODE> 结构，然后才开始处理命令行参数。在您的程序中，可以借此设置在一定情况下合理的缺省值，以便在以后有更合适的缺省值时更方便地对其进行调整。</P><BR><A name=listing7><B>清单 7. 初始化</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
int main( int argc, char *argv[] )
{
    int opt = 0;
    
    /* Initialize globalArgs before we get to work. */
    globalArgs.noIndex = 0;     /* false */
    globalArgs.langCode = NULL;
    globalArgs.outFileName = NULL;
    globalArgs.outFile = NULL;
    globalArgs.verbosity = 0;
    globalArgs.inputFiles = NULL;
    globalArgs.numInputFiles = 0;
</PRE></TD></TR></TBODY></TABLE><BR>
<P><A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing8" cmImpressionSent="1"><FONT color=#996699>清单 8</FONT></A> 中的 <CODE>while</CODE> 循环和 <CODE>switch</CODE> 语句是用于本程序的命令行处理的代码部分。只要 <CODE>getopt()</CODE> 发现选项，<CODE>switch</CODE> 语句将确定找到的是哪个选项，将能在 <CODE>globalArgs</CODE> 结构中看到具体情况。当 <CODE>getopt()</CODE> 最终返回 <CODE>-1</CODE> 时，就完成了选项处理过程，剩下的都是您的输入文件了。</P><BR><A name=listing8><B>清单 8. 使用 getopt() 处理 argc/argv</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
opt = getopt( argc, argv, optString );
    while( opt != -1 ) {
        switch( opt ) {
            case 'I':
                globalArgs.noIndex = 1; /* true */
                break;
                
            case 'l':
                globalArgs.langCode = optarg;
                break;
                
            case 'o':
                globalArgs.outFileName = optarg;
                break;
                
            case 'v':
                globalArgs.verbosity++;
                break;
                
            case 'h':   /* fall-through is intentional */
            case '?':
                display_usage();
                break;
                
            default:
                /* You won't actually get here. */
                break;
        }
        
        opt = getopt( argc, argv, optString );
    }
    
    globalArgs.inputFiles = argv + optind;
    globalArgs.numInputFiles = argc - optind;
</PRE></TD></TR></TBODY></TABLE><BR>
<P>既然已经完成了参数和选项的收集工作，接下来就可以执行程序所设计的任何功能（在本例中是进行文档转换），然后退出（<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing9" cmImpressionSent="1"><FONT color=#996699>清单 9</FONT></A>）。</P><BR><A name=listing9><B>清单 9. 开始工作</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
convert_document();
    
    return EXIT_SUCCESS;
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>好，工作完成，非常漂亮。现在就可以不再往下读了。不过，如果您希望程序符合 90 年代末期的标准并支持 GNU 应用程序中流行的<I>长</I> 选项，则请继续关注下面的内容。</P>
<P><A name=N102AF><SPAN class=atitle>复杂命令行处理： getopt_long() </SPAN></A></P>
<P>在 20 世纪 90 年代（如果没有记错的话），UNIX 应用程序开始支持长选项，即一对短横线（而不是普通<I>短</I> 选项所使用的单个短横线）、一个描述性选项名称还可以包含一个使用等号连接到选项的参数。</P>
<P>幸运的是，可以通过使用 <CODE>getopt_long()</CODE> 向程序添加长选项支持。您可能已经猜到了，<CODE>getopt_long()</CODE> 是同时支持长选项和短选项的 <CODE>getopt()</CODE> 版本。</P>
<P><CODE>getopt_long()</CODE> 函数还接受其他参数，其中一个是指向 <CODE>struct option</CODE> 对象数组的指针。此结构相当直接，如<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing10" cmImpressionSent="1"><FONT color=#996699>清单 10</FONT></A> 中所示。</P><BR><A name=listing10><B>清单 10. getopt_long() 的选项</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
struct option {
    char *name;
    int has_arg;
    int *flag;
    int val;
};
</PRE></TD></TR></TBODY></TABLE><BR>
<P><CODE>name</CODE> 成员是指向长选项名称（带两个短横线）的指针。<CODE>has_arg</CODE> 成员设置为 <CODE>no_argument</CODE>、<CODE>optional_argument</CODE>, 或 <CODE>required_argument</CODE>（均在 <CODE>getopt.h</CODE> 中定义）之一，以指示选项是否具有参数。如果 flag 成员未设置为 NULL，在处理期间遇到此选项时，会使用 <CODE>val</CODE> 成员的值填充它所指向的 <CODE>int</CODE> 值。如果 flag 成员为 <CODE>NULL</CODE>，在 <CODE>getopt_long()</CODE> 遇到此选项时，将返回 <CODE>val</CODE> 中的值；通过将 <CODE>val</CODE> 设置为选项的 <CODE>short</CODE> 参数，可以在不添加任何其他代码的情况下使用 <CODE>getopt_long()</CODE>——处理 <CODE>while loop</CODE> 和 <CODE>switch</CODE> 的现有 <CODE>getopt()</CODE> 将自动处理此选项。</P>
<P>这已经变得更为灵活了，因为各个选项现在可以具有可选参数了。更重要的是，仅需要进行很少的工作，就可以方便地放入现有代码中。</P>
<P>让我们看看如何使用 <CODE>getopt_long()</CODE> 来对示例程序进行更改（getopt_long_demo 项目可从<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#download" cmImpressionSent="1"><FONT color=#996699>下载</FONT></A>部分获得）。</P>
<P><A name=N10341><SPAN class=atitle>使用 getopt_long() </SPAN></A></P>
<P>由于 getopt_long_demo 几乎与刚刚讨论的 getopt_demo 代码一样，因此我将仅对更改的代码进行说明。由于现在已经有了更大的灵活性，因此还将添加对 <CODE>--randomize</CODE> 选项（没有对应的短选项）的支持。</P>
<P><CODE>getopt_long()</CODE> 函数在 <CODE>getopt.h</CODE> 头文件（而非 <CODE>unistd.h</CODE>）中，因此将需要将该头文件包含进来（请参见<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing11" cmImpressionSent="1"><FONT color=#996699>清单 11</FONT></A>）。我还包含了 <CODE>string.h</CODE>，因为将稍后使用 <CODE>strcmp()</CODE> 来帮助确定处理的是哪个长参数。</P><BR><A name=listing11><B>清单 11. 其他头文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
#include &lt;getopt.h&gt;
#include &lt;string.h&gt;
</PRE></TD></TR></TBODY></TABLE><BR>
<P>您已经为 <CODE>--randomize</CODE> 选项在 <CODE>globalArgs</CODE> 中添加了一个标志（请参见<A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing12" cmImpressionSent="1"><FONT color=#996699>清单 12</FONT></A>），并创建了 <CODE>longOpts</CODE> 数组来存储关于此程序支持的长选项的信息。除了 <CODE>--randomize</CODE> 外，所有的参数都与现有短选项对应（例如，<CODE>--no-index</CODE> 等同于 <CODE>-I</CODE>）。通过在选项结构中包含其短选项等效项，可以在不向程序添加任何其他代码的情况下处理等效的长选项。</P><BR><A name=listing12><B>清单 12. 扩展后的参数</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
struct globalArgs_t {
    int noIndex;                /* -I option */
    char *langCode;             /* -l option */
    const char *outFileName;    /* -o option */
    FILE *outFile;
    int verbosity;              /* -v option */
    char **inputFiles;          /* input files */
    int numInputFiles;          /* # of input files */
    int randomized;             /* --randomize option */
} globalArgs;

static const char *optString = "Il:o:vh?";

static const struct option longOpts[] = {
    { "no-index", no_argument, NULL, 'I' },
    { "language", required_argument, NULL, 'l' },
    { "output", required_argument, NULL, 'o' },
    { "verbose", no_argument, NULL, 'v' },
    { "randomize", no_argument, NULL, 0 },
    { "help", no_argument, NULL, 'h' },
    { NULL, no_argument, NULL, 0 }
};
</PRE></TD></TR></TBODY></TABLE><BR>
<P><A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#listing13" cmImpressionSent="1"><FONT color=#996699>清单 13</FONT></A> 将 <CODE>getop()</CODE> 调用更改为了 <CODE>getopt_long()</CODE>，除了 <CODE>getopt()</CODE> 的参数外，它还接受 <CODE>longOpts</CODE> 数组和 <CODE>int</CODE> 指针 (<CODE>longIndex</CODE>)。当 <CODE>getopt_long()</CODE> 返回 <CODE>0</CODE> 时，<CODE>longIndex</CODE> 所指向的整数将设置为当前找到的长选项的索引。 </P><BR><A name=listing13><B>清单 13. 新的经改进的选项处理</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width=572 border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>                
opt = getopt_long( argc, argv, optString, longOpts, &amp;longIndex );
    while( opt != -1 ) {
        switch( opt ) {
            case 'I':
                globalArgs.noIndex = 1; /* true */
                break;
                
            case 'l':
                globalArgs.langCode = optarg;
                break;
                
            case 'o':
                globalArgs.outFileName = optarg;
                break;
                
            case 'v':
                globalArgs.verbosity++;
                break;
                
            case 'h':   /* fall-through is intentional */
            case '?':
                display_usage();
                break;

            case 0:     /* long option without a short arg */
                if( strcmp( "randomize", longOpts[longIndex].name ) == 0 ) {
                    globalArgs.randomized = 1;
                }
                break;
                
            default:
                /* You won't actually get here. */
                break;
        }
        
        opt = getopt_long( argc, argv, optString, longOpts, amp;longIndex );
    }
</PRE></TD></TR></TBODY></TABLE><BR>
<P>我还添加了 <CODE>0</CODE> 的 case，以便处理任何不与现有短选项匹配的长选项。在此例中，只有一个长选项，但代码仍然使用 <CODE>strcmp()</CODE> 来确保它是预期的那个选项。</P>
<P>这样就全部搞定了；程序现在支持更为详细（对临时用户更加友好）的长选项。 </P>
<P><A name=N103E4><SPAN class=atitle>总结</SPAN></A></P>
<P>UNIX 用户始终依赖于命令行参数来修改程序的行为，特别是那些设计作为<I>小工具集合</I> （UNIX 外壳环境）的一部分使用的实用工具更是如此。程序需要能够快速处理各个选项和参数，且要求不会浪费开发人员的太多时间。毕竟，几乎没有程序设计为仅处理命令行参数，开发人员更应该将精力放在程序所实际进行的工作上。</P>
<P><CODE>getopt()</CODE> 函数是一个标准库调用，可允许您使用直接的 while/switch 语句方便地逐个处理命令行参数和检测选项（带或不带附加的参数）。与其类似的 <CODE>getopt_long()</CODE> 允许在几乎不进行额外工作的情况下处理更具描述性的长选项，这非常受开发人员的欢迎。</P>
<P>既然已经知道了如何方便地处理命令行选项，现在就可以集中精力改进您的程序的命令行，可以添加长选项支持，或添加之前由于不想向程序添加额外的命令行选项处理而搁置的任何其他选项。</P>
<P>不要忘记在某处记录您所有的选项和参数，并提供某种类型的内置帮助函数来为健忘的用户提供帮助。</P><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><BR><IMG height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD><IMG height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html#main" cmImpressionSent="1"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><SPAN class=atitle><A name=download>下载</A></SPAN></P>
<TABLE class=data-table-1 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TH scope=col>描述</TH>
<TH scope=col>名字</TH>
<TH scope=col align=right>大小</TH>
<TH scope=col>下载方法</TH></TR>
<TR>
<TH class=tb-row scope=row>Sample getopt() program</TH>
<TD noWrap>au-getopt_demo.zip</TD>
<TD noWrap align=right>23KB</TD>
<TD noWrap><A class=fbox href="http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/au-getopt_demo.zip" cmImpressionSent="1"><B><FONT color=#5c81a7>HTTP</FONT></B></A></TD></TR>
<TR>
<TH class=tb-row scope=row>Sample getopt_long() program</TH>
<TD noWrap>au-getopt_long_demo.zip</TD>
<TD noWrap align=right>24KB</TD>
<TD noWrap><A class=fbox href="http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/au-getopt_long_demo.zip" cmImpressionSent="1"><B><FONT color=#5c81a7>HTTP</FONT></B></A></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD colSpan=5><FONT color=#5c81a7><IMG height=12 alt="" src="http://www.ibm.com/i/c.gif" width=12 border=0></FONT></TD></TR>
<TR>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www.ibm.com/developerworks/cn/whichmethod.html" cmImpressionSent="1"><FONT color=#5c81a7>关于下载方法的信息</FONT></A></TD>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=50></FONT></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle>参考资料 </SPAN></A></P><B>学习</B><BR>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www.ibm.com/developerworks/eserver/library/es-unix-fsoft.html" target=_blank cmImpressionSent="1"><FONT color=#5c81a7>英文原文</FONT></A> <BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/opensource/library/os-eclipse.html" cmImpressionSent="1"><FONT color=#5c81a7>What is Eclipse, and how do I use it?</FONT></A>”（developerWorks，2001 年 11 月）：有关 Eclipse 的相关问题及答案，请一定阅读此文。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/opensource/top-projects/eclipse-starthere.html" cmImpressionSent="1"><FONT color=#5c81a7>Get started now with Eclipse</FONT></A>：下载并开始使用 Eclipse。<BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/opensource/library/os-ecc/" cmImpressionSent="1"><FONT color=#5c81a7>C/C++ development with the Eclipse Platform</FONT></A>”（developerWorks，2006 年 3 月）：了解如何将 C++ 与 Eclipse 一起使用。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/eserver/library/es-unixforks/" cmImpressionSent="1"><FONT color=#5c81a7>Network services</FONT></A>：了解遗留设计与线程化设计的区别。<BR><BR>
<LI>“<A href="http://www.ibm.com/developerworks/eserver/library/es-unix-eclipse/" cmImpressionSent="1"><FONT color=#5c81a7>Build UNIX software with Eclipse</FONT></A>”（developerWorks，2006 年 3 月）：阅读此文以了解使用 Eclipse 构建 UNIX 软件的基础知识。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/offers/techbriefings/?S_TACT=105AGY06&amp;S_CMP=art" cmImpressionSent="1"><FONT color=#5c81a7>developerWorks 技术活动和网络广播</FONT></A>：了解关于 developerWorks 技术活动和网络广播的最新消息。<BR><BR>
<LI><A href="http://www.ibm.com/developerworks/aix/" cmImpressionSent="1"><FONT color=#5c81a7>AIX and UNIX</FONT></A>：想了解更多内容吗？developerWorks 的 AIX and UNIX 专区提供数百篇关于 AIX 和 UNIX 的文章以及入门级、中级和高级教程，将让您大开眼界。</LI></UL><BR><B>讨论</B><BR>
<UL>
<LI>参与 <A href="http://www.ibm.com/developerworks/blogs/" cmImpressionSent="1"><FONT color=#5c81a7>developerWorks 博客</FONT></A>，从而加入到 developerWorks 社区中来。<BR><BR></LI></UL><BR><BR>
<P><A name=author><SPAN class=atitle>关于作者</SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=3><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><IMG height=80 alt="Chris Herborth 的照片" src="http://www.ibm.com/developerworks/i/p-cherborth.jpg" width=64 align=left border=0></P></TD>
<TD><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></TD>
<TD width="100%">
<P>Chris Herborth 是一位屡获殊荣的高级技术作者，十余年来撰写了许多篇操作系统和编程方面的文章。在陪伴儿子 Alex 或妻子 Lynette 之余，他设计、编写和研究（也就是玩）视频游戏。</P></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE><A href="http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html">http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html</A>]]></description>
</item><item>
<title><![CDATA[tzset()与localtime()]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36797</link>
<author>FoxWolf</author>
<pubDate>2008/6/13 10:59:48</pubDate>
<description><![CDATA[<A><FONT size=3>　</FONT></A><FONT size=3>一直以来应用Linux也就是随便的写点程序，构建一下服务器，很少关注一个基本的设置——时区。我相信大部分的爱好者们都是如此的，我们生活在一个地方，一个国家，一个地区，至少不会频繁改变。so...我们的机器时间设置是很少变化的，再加上现在很多情况下都有UTP——时间网络同步协议了，更不要说去改变时区。<BR>&nbsp;&nbsp;&nbsp; 然而对于一个应用Linux作为平台的产品而言，它却是可能会被改变时区的，即便机会不多，但对于设计人员、工程师、项目经理而言，这一部分不容忽视。于是，在第二次遇到这个问题的时候，我选择将它彻底弄清楚，所以有了这样一篇记录。问题的描述是这样的：我们可以使用time调用获取当前的时间，注意，这是以UTC表示的机器时间——自1970年1月1日0点以来的秒数，接着我们用localtime调用可以将time获取的时间转换为本地时间，从UTC转换到本地时间会依靠时区信息进行调整。对于一个daemon进程而言，如果每隔一段时间用time和localtime调用就可以定期获取当前时间的数值，但是如果在这个期间发生了时区设置转换会怎样呢？<BR>&nbsp;&nbsp;&nbsp; 或许你会觉得，那一定会出大问题——时区变了，localtime也会出现很大的调整。嗯，接下来的结论就是后续的程序需要小心处理这种变化......<BR>&nbsp;&nbsp;&nbsp; 很不幸，我们的感觉是错的，如果时区的设置变化了，localtime转换的时间依然与之前没有什么不同，除非程序再次启动。Linux的时区设置通过几个不同的途径完成。一方面可以通过设置环境变量TZ来指定时区，例如TZ=Asia/Shanghai，可以将时区指定为上海所在的时区，时区的配置出现在/usr/share/zoneinfo/Asia/Shanghai文件当中（Redhat环境）；如果没有指定TZ环境变量，那么缺省的时区配置文件可以用/etc/localtime来获得，这个文件可能是一个符号链接指向真实的文件，也有可能就是将/usr/share/zoneinfo下的文件复制过来达到所要的结果。由于环境变量由各个进程组单独继承，那么在设置时区之后很难改变其他进程组的环境变量，所以一般的系统很少直接设置TZ环境变量，而是由/etc/localtime文件来指示时区位置。<BR>&nbsp;&nbsp;&nbsp; 既然如此，为什么设置了时区以后，已经运行的daemon程序在使用localtime函数调用时没有使用新时区呢？这个可以通过glibc的源码来回答。localtime等涉及到本地所在时区的函数在调用的时候会先调用tzset这个函数，这一点可以通过tzset函数的manpage看出来。tzset完成的工作是把当前时区信息（通过TZ环境变量或者/etc/localtime）读入并缓冲。事实上tzset在实现的时候是通过内部的tzset_internal函数来完成的，显式的调用tzset会以显式的方式告知tzset_internal，而单独调用localtime的时候是以隐式的方式告知tzset_internal，前者将强制tzset不管何种情况一律重新加载TZ信息或者/etc/localtime，而后者则是只有在TZ发生变化，或者加载文件名发生变化的时候才会再次加载时区信息。因此，如果只是/etc/localtime的内容发生了变化，而文件名"/etc/localtime"没有变化，则不会再次加载时区信息，导致localtime函数调用仍然以老时区转换UTC时间到本地时间。<BR>&nbsp;&nbsp;&nbsp; 结论：对localtime的反复调用，如果要考虑时区变化的因素，最好显式的调用一次tzset。或许今后的glibc库会修正这个bug？至少我在glibc-2.3.5和2.4的版本上还看到这个问题。<BR><BR></FONT>
<P id=TBPingURL><FONT size=3>Trackback: http://tb.donews.net/TrackBack.aspx?PostId=1287733</FONT></P>]]></description>
</item><item>
<title><![CDATA[GCC的一些参数介绍、gdb遇到段错误的一般调试方法]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36746</link>
<author>FoxWolf</author>
<pubDate>2008/6/11 17:55:10</pubDate>
<description><![CDATA[<A><FONT size=3>　</FONT></A><FONT size=3> </FONT>
<P><FONT size=3>在为Linux开发应用程序时,绝大多数情况下使用的都是C语言,因此几乎每一位Linux程序员面临的首要问题都是如何</FONT></P>
<P><FONT size=3>灵活运用C编译器.目前Linux下最常用的C语言编译器是GCC(GNU Compiler Collection),它是GNU项目中符合ANSI C</FONT></P>
<P><FONT size=3>标准的编译系统,能够编译用C、C++和Object C等语言编写的程序.GCC不仅功能非常强大,结构也异常灵活.最值得称</FONT></P>
<P><FONT size=3>道的一点就是它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等. </FONT></P>
<P><FONT size=3>开放、自由和灵活是Linux的魅力所在,而这一点在GCC上的体现就是程序员通过它能够更好地控制整个编译过程.在</FONT></P>
<P><FONT size=3>使用GCC编译程序时,编译过程可以被细分为四个阶段: </FONT></P>
<P><FONT size=3>◆ 预处理(Pre-Processing) </FONT></P>
<P><FONT size=3>◆ 编译(Compiling) </FONT></P>
<P><FONT size=3>◆ 汇编(Assembling) </FONT></P>
<P><FONT size=3>◆ 链接(Linking) </FONT></P>
<P><FONT size=3>Linux程序员可以根据自己的需要让GCC在编译的任何阶段结束,以便检查或使用编译器在该阶段的输出信息,或者对</FONT></P>
<P><FONT size=3>最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备.和其它常用的</FONT></P>
<P><FONT size=3>编译器一样,GCC也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码. </FONT></P>
<P><FONT size=3>GCC提供了30多条警告信息和三个警告级别,使用它们有助于增强程序的稳定性和可移植性.此外,GCC还对标准的C和</FONT></P>
<P><FONT size=3>C++语言进行了大量的扩展,提高程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量. </FONT></P>
<P><FONT size=3>GCC起步 </FONT></P>
<P><FONT size=3>在学习使用GCC之前,下面的这个例子能够帮助用户迅速理解GCC的工作原理,并将其立即运用到实际的项目开发中去.</FONT></P>
<P><FONT size=3>首先用熟悉的编辑器输入清单1所示的代码: </FONT></P>
<P><FONT size=3>清单1:hello.c </FONT></P>
<P><FONT size=3>#include &lt;stdio.h&gt;<BR>int main(void)<BR>{<BR>&nbsp;printf (Hello world, Linux programming!\\n);<BR>&nbsp;return 0;<BR>}<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>然后执行下面的命令编译和运行这段程序: </FONT></P>
<P><FONT size=3># gcc hello.c -o hello<BR># ./hello<BR>Hello world, Linux programming!<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>从程序员的角度看,只需简单地执行一条GCC命令就可以了,但从编译器的角度来看,却需要完成一系列非常繁杂的工</FONT></P>
<P><FONT size=3>作.首先,GCC需要调用预处理程序cpp,由它负责展开在源文件中定义的宏,并向其中插入“#include”语句所包含的</FONT></P>
<P><FONT size=3>内容；接着,GCC会调用ccl和as将处理后的源代码编译成目标代码；最后,GCC会调用链接程序ld,把生成的目标代码</FONT></P>
<P><FONT size=3>链接成一个可执行程序. </FONT></P>
<P><FONT size=3>为了更好地理解GCC的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果.第一步是进行</FONT></P>
<P><FONT size=3>预编译,使用-E参数可以让GCC在预处理结束后停止编译过程: </FONT></P>
<P><FONT size=3>#&nbsp; gcc -E hello.c -o hello.i<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>此时若查看hello.cpp文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而其它应当被预处理的宏定义也</FONT></P>
<P><FONT size=3>都做了相应的处理.下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成: </FONT></P>
<P><FONT size=3>#&nbsp; gcc -c hello.i -o hello.o<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>GCC默认将.i文件看成是预处理后的C语言源代码,因此上述命令将自动跳过预处理步骤而开始执行编译过程,也可以</FONT></P>
<P><FONT size=3>使用-x参数让GCC从指定的步骤开始编译.最后一步是将生成的目标文件链接成可执行文件: </FONT></P>
<P><FONT size=3>#&nbsp; gcc hello.o -o hello<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使</FONT></P>
<P><FONT size=3>用GCC能够很好地管理这些编译单元.假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并</FONT></P>
<P><FONT size=3>最终生成可执行程序foo,可以使用下面这条命令: </FONT></P>
<P><FONT size=3>#&nbsp; gcc foo1.c foo2.c -o foo<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行.如果深究起来,上面这条命令大</FONT></P>
<P><FONT size=3>致相当于依次执行如下三条命令: </FONT></P>
<P><FONT size=3># gcc -c foo1.c -o foo1.o<BR># gcc -c foo2.c -o foo2.o<BR># gcc foo1.o foo2.o -o foo<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>在编译一个包含许多源文件的工程时,若只用一条GCC命令来完成编译是非常浪费时间的.假设项目中有100个源文件</FONT></P>
<P><FONT size=3>需要编译,并且每个源文件中都包含10000行代码,如果像上面那样仅用一条GCC命令来完成编译工作,那么GCC需要将</FONT></P>
<P><FONT size=3>每个源文件都重新编译一遍,然后再全部连接起来.很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某</FONT></P>
<P><FONT size=3>一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的.要解决这个</FONT></P>
<P><FONT size=3>问题,关键是要灵活运用GCC,同时还要借助像Make这样的工具. </FONT></P>
<P><FONT size=3>警告提示功能 </FONT></P>
<P><FONT size=3>GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码.先来读读清单2所示</FONT></P>
<P><FONT size=3>的程序,这段代码写得很糟糕,仔细检查一下不难挑出很多毛病: </FONT></P>
<P><FONT size=3>◆main函数的返回值被声明为void,但实际上应该是int； </FONT></P>
<P><FONT size=3>◆使用了GNU语法扩展,即使用long long来声明64位整数,不符合ANSI/ISO C语言标准； </FONT></P>
<P><FONT size=3>◆main函数在终止前没有调用return语句. </FONT></P>
<P><FONT size=3>清单2:illcode.c </FONT></P>
<P><FONT size=3>#include &lt;stdio.h&gt;<BR>void main(void)<BR>{<BR>&nbsp; long long int var = 1;<BR>&nbsp; printf(It is not standard C code!\\n);<BR>}<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>下面来看看GCC是如何帮助程序员来发现这些错误的.当GCC在编译不符合ANSI/ISO C语言标准的源代码时,如果加上</FONT></P>
<P><FONT size=3>了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息: </FONT></P>
<P><FONT size=3># gcc -pedantic illcode.c -o illcode<BR>illcode.c: In function `main':<BR>illcode.c:9: ISO C89 does not support `long long'<BR>illcode.c:8: return type of `main' is not `int'<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>需要注意的是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅只能用来帮助Linux</FONT></P>
<P><FONT size=3>程序员离这个目标越来越近.或者换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,</FONT></P>
<P><FONT size=3>但不是全部,事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况,才有可能被GCC发现并提出警告. </FONT></P>
<P><FONT size=3>除了-pedantic之外,GCC还有一些其它编译选项也能够产生有用的警告信息.这些选项大多以-W开头,其中最有价值的</FONT></P>
<P><FONT size=3>当数-Wall了,使用它能够使GCC产生尽可能多的警告信息: </FONT></P>
<P><FONT size=3># gcc -Wall illcode.c -o illcode<BR>illcode.c:8: warning: return type of `main' is not `int'<BR>illcode.c: In function `main':<BR>illcode.c:9: warning: unused variable `var'<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>GCC给出的警告信息虽然从严格意义上说不能算作是错误,但却很可能成为错误的栖身之所.一个优秀的Linux程序员</FONT></P>
<P><FONT size=3>应该尽量避免产生警告信息,使自己的代码始终保持简洁、优美和健壮的特性. </FONT></P>
<P><FONT size=3>在处理警告方面,另一个常用的编译选项是-Werror,它要求GCC将所有的警告当成错误进行处理,这在使用自动编译工</FONT></P>
<P><FONT size=3>具(如Make等)时非常有用.如果编译时带上-Werror选项,那么GCC会在所有产生警告的地方停止编译,迫使程序员对自</FONT></P>
<P><FONT size=3>己的代码进行修改.只有当相应的警告信息消除时,才可能将编译过程继续朝前推进.执行情况如下: </FONT></P>
<P><FONT size=3># gcc -Wall -Werror illcode.c -o illcode<BR>cc1: warnings being treated as errors<BR>illcode.c:8: warning: return type of `main' is not `int'<BR>illcode.c: In function `main':<BR>illcode.c:9: warning: unused variable `var'<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>对Linux程序员来讲,GCC给出的警告信息是很有价值的,它们不仅可以帮助程序员写出更加健壮的程序,而且还是跟踪</FONT></P>
<P><FONT size=3>和调试程序的有力工具.建议在用GCC编译源代码时始终带上-Wall选项,并把它逐渐培养成为一种习惯,这对找出常见</FONT></P>
<P><FONT size=3>的隐式编程错误很有帮助. </FONT></P>
<P><FONT size=3>库依赖 </FONT></P>
<P><FONT size=3>在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持</FONT></P>
<P><FONT size=3>才能够完成相应的功能.从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合.虽然</FONT></P>
<P><FONT size=3>Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的</FONT></P>
<P><FONT size=3>情况都是这样.正因如此,GCC在编译时必须有自己的办法来查找所需要的头文件和库文件. </FONT></P>
<P><FONT size=3>GCC采用搜索目录的办法来查找所需要的文件,-I选项可以向GCC的头文件搜索路径中添加新的目录.例如,如果</FONT></P>
<P><FONT size=3>在/home/xiaowp/include/目录下有编译时所需要的头文件,为了让GCC能够顺利地找到它们,就可以使用-I选项: </FONT></P>
<P><FONT size=3># gcc foo.c -I /home/xiaowp/include -o foo<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>同样,如果使用了不在标准位置的库文件,那么可以通过-L选项向GCC的库文件搜索路径中添加新的目录.例如,如果在</FONT></P>
<P><FONT size=3>/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so,为了让GCC能够顺利地找到它,可以使用下面的命令: </FONT></P>
<P><FONT size=3># gcc foo.c -L /home/xiaowp/lib -lfoo -o foo<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>值得好好解释一下的是-l选项,它指示GCC去连接库文件libfoo.so.Linux下的库文件在命名时有一个约定,那就是应</FONT></P>
<P><FONT size=3>该以lib三个字母开头,由于所有的库文件都遵循了同样的规范,因此在用-l选项指定链接的库文件名时可以省去lib</FONT></P>
<P><FONT size=3>三个字母,也就是说GCC在对-lfoo进行处理时,会自动去链接名为libfoo.so的文件. </FONT></P>
<P><FONT size=3>Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),两者的差别仅在程序</FONT></P>
<P><FONT size=3>执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的.默认情况下,GCC在链接时优先使用动态链接库,</FONT></P>
<P><FONT size=3>只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接</FONT></P>
<P><FONT size=3>库.例如,如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a,为了让GCC在链接时只用</FONT></P>
<P><FONT size=3>到静态链接库,可以使用下面的命令: </FONT></P>
<P><FONT size=3># gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo<BR>&nbsp;<BR>代码优化 </FONT></P>
<P><FONT size=3>代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执</FONT></P>
<P><FONT size=3>行性能.GCC提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的</FONT></P>
<P><FONT size=3>整数.对于不同版本的GCC来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或</FONT></P>
<P><FONT size=3>3. </FONT></P>
<P><FONT size=3>编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1.在这一级别上能够进行的优化类</FONT></P>
<P><FONT size=3>型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化.选</FONT></P>
<P><FONT size=3>项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等.选项-O3则</FONT></P>
<P><FONT size=3>除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作.通常来说,数字越大优</FONT></P>
<P><FONT size=3>化的等级越高,同时也就意味着程序的运行速度越快.许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译</FONT></P>
<P><FONT size=3>时间和代码大小之间,取得了一个比较理想的平衡点. </FONT></P>
<P><FONT size=3>下面通过具体实例来感受一下GCC的代码优化功能,所用程序如清单3所示. </FONT></P>
<P><FONT size=3>清单3:optimize.c </FONT></P>
<P><FONT size=3>#include &lt;stdio.h&gt; <BR>int main(void)<BR>{<BR>&nbsp; double counter;<BR>&nbsp; double result;<BR>&nbsp; double temp;<BR>&nbsp; for (counter = 0; <BR>&nbsp;&nbsp; counter &lt; 2000.0 * 2000.0 * 2000.0&nbsp; / 20.0 + 2020; <BR>&nbsp;&nbsp; counter += (5 - 1) / 4) {<BR>&nbsp;&nbsp;&nbsp; temp = counter / 1979;<BR>&nbsp;&nbsp;&nbsp; result&nbsp; = counter;&nbsp;&nbsp;&nbsp; <BR>&nbsp; }<BR>&nbsp; printf(Result is %lf\\n, result);<BR>&nbsp; return 0;<BR>}<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>首先不加任何优化选项进行编译: </FONT></P>
<P><FONT size=3># gcc -Wall optimize.c -o optimize<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间: </FONT></P>
<P><FONT size=3># time ./optimize<BR>Result is 400002019.000000<BR>real&nbsp;&nbsp;&nbsp; 0m14.942s<BR>user&nbsp;&nbsp;&nbsp; 0m14.940s<BR>sys&nbsp;&nbsp;&nbsp;&nbsp; 0m0.000s<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>接下去使用优化选项来对代码进行优化处理: </FONT></P>
<P><FONT size=3># gcc -Wall -O optimize.c -o optimize<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>在同样的条件下再次测试一下运行时间: </FONT></P>
<P><FONT size=3># time ./optimize<BR>Result is 400002019.000000<BR>real&nbsp;&nbsp;&nbsp; 0m3.256s<BR>user&nbsp;&nbsp;&nbsp; 0m3.240s<BR>sys&nbsp;&nbsp;&nbsp;&nbsp; 0m0.000s<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的14秒缩短到了3秒.这个例子是</FONT></P>
<P><FONT size=3>专门针对GCC的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变.尽管GCC的代码优化功能非常强</FONT></P>
<P><FONT size=3>大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码.如果编写的代码简短,并且逻辑性</FONT></P>
<P><FONT size=3>强,编译器就不会做更多的工作,甚至根本用不着优化. </FONT></P>
<P><FONT size=3>优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码: </FONT></P>
<P><FONT size=3>◆ 程序开发的时候 优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软</FONT></P>
<P><FONT size=3>件发行或开发结束的时候,才考虑对最终生成的代码进行优化. </FONT></P>
<P><FONT size=3>◆ 资源受限的时候 一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如</FONT></P>
<P><FONT size=3>一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果. </FONT></P>
<P><FONT size=3>◆ 跟踪调试的时候 在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,</FONT></P>
<P><FONT size=3>从而使跟踪和调试变得异常困难. </FONT></P>
<P><FONT size=3>调试 </FONT></P>
<P><FONT size=3>一个功能强大的调试器不仅为程序员提供了跟踪程序执行的手段,而且还可以帮助程序员找到解决问题的方法.对于</FONT></P>
<P><FONT size=3>Linux程序员来讲,GDB(GNU Debugger)通过与GCC的配合使用,为基于Linux的软件开发提供了一个完善的调试环境. </FONT></P>
<P><FONT size=3>默认情况下,GCC在编译时不会将调试符号插入到生成的二进制代码中,因为这样会增加可执行文件的大小.如果需要</FONT></P>
<P><FONT size=3>在编译时生成调试符号信息,可以使用GCC的-g或者-ggdb选项.GCC在产生调试符号时,同样采用了分级的思路,开发人</FONT></P>
<P><FONT size=3>员可以通过在-g选项后附加数字1、2或3来指定在代码中加入调试信息的多少.默认的级别是2(-g2),此时产生的调试</FONT></P>
<P><FONT size=3>信息包括扩展的符号表、行号、局部或外部变量信息.级别3(-g3)包含级别2中的所有调试信息,以及源代码中定义的</FONT></P>
<P><FONT size=3>宏.级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用.回溯跟踪指的是</FONT></P>
<P><FONT size=3>监视程序在运行过程中的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都</FONT></P>
<P><FONT size=3>是经常用到的调试手段. </FONT></P>
<P><FONT size=3>GCC产生的调试符号具有普遍的适应性,可以被许多调试器加以利用,但如果使用的是GDB,那么还可以通过-ggdb选项</FONT></P>
<P><FONT size=3>在生成的二进制代码中包含GDB专用的调试信息.这种做法的优点是可以方便GDB的调试工作,但缺点是可能导致其它</FONT></P>
<P><FONT size=3>调试器(如DBX)无法进行正常的调试.选项-ggdb能够接受的调试级别和-g是完全一样的,它们对输出的调试符号有着</FONT></P>
<P><FONT size=3>相同的影响. </FONT></P>
<P><FONT size=3>需要注意的是,使用任何一个调试选项都会使最终生成的二进制文件的大小急剧增加,同时增加程序在执行时的开销,</FONT></P>
<P><FONT size=3>因此调试选项通常仅在软件的开发和调试阶段使用.调试选项对生成代码大小的影响从下面的对比过程中可以看出来</FONT></P>
<P><FONT size=3>: </FONT></P>
<P><FONT size=3># gcc optimize.c -o optimize<BR># ls optimize -l<BR>-rwxrwxr-x&nbsp; 1 xiaowp&nbsp;&nbsp; xiaowp&nbsp; 11649 Nov 20 08:53 optimize&nbsp; (未加调试选项)<BR># gcc -g optimize.c -o optimize<BR># ls optimize -l<BR>-rwxrwxr-x&nbsp; 1 xiaowp&nbsp;&nbsp; xiaowp&nbsp; 15889 Nov 20 08:54 optimize&nbsp; (加入调试选项)<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>虽然调试选项会增加文件的大小,但事实上Linux中的许多软件在测试版本甚至最终发行版本中仍然使用了调试选项</FONT></P>
<P><FONT size=3>来进行编译,这样做的目的是鼓励用户在发现问题时自己动手解决,是Linux的一个显著特色. </FONT></P>
<P><FONT size=3>为调试编译代码(Compiling Code for Debugging)<BR>为了使 gdb 正常工作, 你必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可</FONT></P>
<P><FONT size=3>执行文件里的地址映射以及源代码的行号. gdb 利用这些信息使源代码和机器码相关联. <BR>◆在编译时用 -g 选项打开调试选项. <BR>&nbsp; </FONT></P>
<P><FONT size=3>gdb 基本命令<BR>&nbsp;&nbsp;&nbsp;&nbsp; gdb 支持很多的命令使你能实现不同的功能. 这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复</FONT></P>
<P><FONT size=3>杂命令, 表27.1列出了你在用 gdb 调试时会用到的一些命令. 想了解 gdb 的详细使用请参考 gdb 的指南页. <BR>&nbsp;&nbsp;表 27.1. 基本 gdb 命令.<BR>命&nbsp;&nbsp; 令 描&nbsp; 述 <BR>file 装入想要调试的可执行文件. <BR>kill 终止正在调试的程序. <BR>list 列出产生执行文件的源代码的一部分. <BR>next 执行一行源代码但不进入函数内部. <BR>step 执行一行源代码而且进入函数内部. <BR>run 执行当前被调试的程序 <BR>quit 终止 gdb <BR>watch 使你能监视一个变量的值而不管它何时被改变. <BR>break 在代码里设置断点, 这将使程序执行到这里时被挂起. <BR>make 使你能不退出 gdb 就可以重新产生可执行文件. <BR>shell 使你能不离开 gdb 就执行 UNIX shell 命令.&nbsp; </FONT></P>
<P><FONT size=3>gdb 支持很多与 UNIX shell 程序一样的命令编辑特征. 你能象在 bash 或 tcsh里那样按 Tab 键让gdb 帮你补齐</FONT></P>
<P><FONT size=3>一个唯一的命令, 如果不唯一的话 gdb 会列出所有匹配的命令. 你也能用光标键上下翻动历史命令. </FONT></P>
<P><BR><FONT size=3>下面还是通过一个具体的实例说明如何利用调试符号来分析错误,所用程序见清单4所示. </FONT></P>
<P><FONT size=3>清单4:crash.c </FONT></P>
<P><FONT size=3>#include &lt;stdio.h&gt; <BR>int main(void)<BR>{<BR>&nbsp; int input =0;<BR>&nbsp; printf(Input an integer:);<BR>&nbsp; scanf(%d, input);<BR>&nbsp; printf(The integer you input is %d\\n, input);<BR>&nbsp; return 0;<BR>}<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>编译并运行上述代码,会产生一个严重的段错误(Segmentation fault)如下: </FONT></P>
<P><FONT size=3># gcc -g crash.c -o crash<BR># ./crash<BR>Input an integer:10<BR>Segmentation fault<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>为了更快速地发现错误所在,可以使用GDB进行跟踪调试,方法如下: </FONT></P>
<P><FONT size=3># gdb crash<BR>GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)<BR>……<BR>(gdb)<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>当GDB提示符出现的时候,表明GDB已经做好准备进行调试了,现在可以通过run命令让程序开始在GDB的监控下运行: </FONT></P>
<P><FONT size=3>(gdb) run<BR>Starting program: /home/xiaowp/thesis/gcc/code/crash<BR>Input an integer:10</FONT></P>
<P><FONT size=3>Program received signal SIGSEGV, Segmentation fault.<BR>0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>仔细分析一下GDB给出的输出结果不难看出,程序是由于段错误而导致异常中止的,说明内存操作出了问题,具体发生</FONT></P>
<P><FONT size=3>问题的地方是在调用_IO_vfscanf_internal ( )的时候.为了得到更加有价值的信息,可以使用GDB提供的回溯跟踪命</FONT></P>
<P><FONT size=3>令backtrace,执行结果如下: </FONT></P>
<P><FONT size=3>(gdb) backtrace<BR>#0&nbsp; 0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6<BR>#1&nbsp; 0xbffff0c0 in ?? ()<BR>#2&nbsp; 0x4008e0ba in scanf () from /lib/libc.so.6<BR>#3&nbsp; 0x08048393 in main () at crash.c:11<BR>#4&nbsp; 0x40042917 in __libc_start_main () from /lib/libc.so.6<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>跳过输出结果中的前面三行,从输出结果的第四行中不难看出,GDB已经将错误定位到crash.c中的第11行了.现在仔细</FONT></P>
<P><FONT size=3>检查一下: </FONT></P>
<P><FONT size=3>(gdb) frame 3<BR>#3&nbsp; 0x08048393 in main () at crash.c:11<BR>11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scanf(%d, input);<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>使用GDB提供的frame命令可以定位到发生错误的代码段,该命令后面跟着的数值可以在backtrace命令输出结果中的</FONT></P>
<P><FONT size=3>行首找到.现在已经发现错误所在了,应该将 </FONT></P>
<P><FONT size=3>scanf(%d, input);<BR>改为<BR>scanf(%d, &amp;input);<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>完成后就可以退出GDB了,命令如下: </FONT></P>
<P><FONT size=3>(gdb) quit<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>GDB的功能远远不止如此,它还可以单步跟踪程序、检查内存变量和设置断点等. </FONT></P>
<P><FONT size=3>调试时可能会需要用到编译器产生的中间结果,这时可以使用-save-temps选项,让GCC将预处理代码、汇编代码和目</FONT></P>
<P><FONT size=3>标代码都作为文件保存起来.如果想检查生成的代码是否能够通过手工调整的办法来提高执行性能,在编译过程中生</FONT></P>
<P><FONT size=3>成的中间文件将会很有帮助,具体情况如下: </FONT></P>
<P><FONT size=3># gcc -save-temps foo.c -o foo<BR># ls foo*<BR>foo&nbsp; foo.c&nbsp; foo.i&nbsp; foo.s<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>GCC支持的其它调试选项还包括-p和-pg,它们会将剖析(Profiling)信息加入到最终生成的二进制代码中.剖析信息对</FONT></P>
<P><FONT size=3>于找出程序的性能瓶颈很有帮助,是协助Linux程序员开发出高性能程序的有力工具.在编译时加入-p选项会在生成的</FONT></P>
<P><FONT size=3>代码中加入通用剖析工具(Prof)能够识别的统计信息,而-pg选项则生成只有GNU剖析工具(Gprof)才能识别的统计信</FONT></P>
<P><FONT size=3>息. </FONT></P>
<P><FONT size=3>最后提醒一点,虽然GCC允许在优化的同时加入调试符号信息,但优化后的代码对于调试本身而言将是一个很大的挑战</FONT></P>
<P><FONT size=3>.代码在经过优化之后,在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环</FONT></P>
<P><FONT size=3>语句有可能因为循环展开而变得到处都有,所有这些对调试来讲都将是一场噩梦.建议在调试的时候最好不使用任何</FONT></P>
<P><FONT size=3>优化选项,只有当程序在最终发行的时候才考虑对其进行优化.</FONT></P>
<P><BR><FONT size=3>上次的培训园地中介绍了GCC的编译过程、警告提示功能、库依赖、代码优化和程序调试六个方面的内容.这期是最</FONT></P>
<P><FONT size=3>后的一部分内容. </FONT></P>
<P><FONT size=3>加速 </FONT></P>
<P><FONT size=3>在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接.这些过程实际上是由</FONT></P>
<P><FONT size=3>不同的程序负责完成的.大多数情况下GCC可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理. </FONT></P>
<P><FONT size=3>这样做有一个很明显的缺点,就是GCC在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,</FONT></P>
<P><FONT size=3>从而无形中导致处理速度变慢.例如,GCC在处理一个源文件时,可能需要一个临时文件来保存预处理的输出、一个临</FONT></P>
<P><FONT size=3>时文件来保存编译器的输出、一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间.当</FONT></P>
<P><FONT size=3>软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很沉重. </FONT></P>
<P><FONT size=3>解决的办法是,使用Linux提供的一种更加高效的通信方式—管道.它可以用来同时连接两个程序,其中一个程序的输</FONT></P>
<P><FONT size=3>出将被直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存. </FONT></P>
<P><FONT size=3>在编译过程中使用管道是由GCC的-pipe选项决定的.下面的这条命令就是借助GCC的管道功能来提高编译速度的: </FONT></P>
<P><FONT size=3># gcc -pipe foo.c -o foo<BR>&nbsp;</FONT></P>
<P><BR><FONT size=3>在编译小型工程时使用管道,编译时间上的差异可能还不是很明显,但在源代码非常多的大型工程中,差异将变得非常</FONT></P>
<P><FONT size=3>明显. </FONT></P>
<P><BR><FONT size=3>文件扩展名 </FONT></P>
<P><FONT size=3>在使用GCC的过程中,用户对一些常用的扩展名一定要熟悉,并知道其含义.为了方便大家学习使用GCC,在此将这些扩</FONT></P>
<P><FONT size=3>展名罗列如下: </FONT></P>
<P><FONT size=3>.c C原始程序； </FONT></P>
<P><FONT size=3>.C C++原始程序； </FONT></P>
<P><FONT size=3>.cc C++原始程序； </FONT></P>
<P><FONT size=3>.cxx C++原始程序； </FONT></P>
<P><FONT size=3>.m Objective-C原始程序； </FONT></P>
<P><FONT size=3>.i 已经过预处理的C原始程序； </FONT></P>
<P><FONT size=3>.ii 已经过预处理之C++原始程序； </FONT></P>
<P><FONT size=3>.s 组合语言原始程序； </FONT></P>
<P><FONT size=3>.S 组合语言原始程序； </FONT></P>
<P><FONT size=3>.h 预处理文件(标头文件)； </FONT></P>
<P><FONT size=3>.o 目标文件； </FONT></P>
<P><FONT size=3>.a 存档文件. </FONT></P>
<P><FONT size=3>GCC常用选项 </FONT></P>
<P><FONT size=3>GCC作为Linux下C/C++重要的编译环境,功能强大,编译选项繁多.为了方便大家日后编译方便,在此将常用的选项及说</FONT></P>
<P><FONT size=3>明罗列出来如下: </FONT></P>
<P><FONT size=3>-c 通知GCC取消链接步骤,即编译源码并在最后生成目标文件； </FONT></P>
<P><FONT size=3>-Dmacro 定义指定的宏,使它能够通过源码中的#ifdef进行检验； </FONT></P>
<P><FONT size=3>-E 不经过编译预处理程序的输出而输送至标准输出； </FONT></P>
<P><FONT size=3>-g3 获得有关调试程序的详细信息,它不能与-o选项联合使用； </FONT></P>
<P><FONT size=3>-Idirectory 在包含文件搜索路径的起点处添加指定目录； </FONT></P>
<P><FONT size=3>-llibrary 提示链接程序在创建最终可执行文件时包含指定的库； </FONT></P>
<P><FONT size=3>-O、-O2、-O3 将优化状态打开,该选项不能与-g选项联合使用； </FONT></P>
<P><FONT size=3>-S 要求编译程序生成来自源代码的汇编程序输出； </FONT></P>
<P><FONT size=3>-v 启动所有警报； </FONT></P>
<P><FONT size=3>-Wall 在发生警报时取消编译操作,即将警报看作是错误； </FONT></P>
<P><FONT size=3>-Werror 在发生警报时取消编译操作,即把报警当作是错误； </FONT></P>
<P><FONT size=3>-w 禁止所有的报警. </FONT></P>
<P><FONT size=3>小结 </FONT></P>
<P><FONT size=3>GCC是在Linux下开发程序时必须掌握的工具之一.本文对GCC做了一个简要的介绍,主要讲述了如何使用GCC编译程序</FONT></P>
<P><FONT size=3>、产生警告信息、调试程序和加快GCC的编译速度.对所有希望早日跨入Linux开发者行列的人来说,GCC就是成为一名</FONT></P>
<P><FONT size=3>优秀的Linux程序员的起跑线.<BR></FONT></P>]]></description>
</item><item>
<title><![CDATA[简说XML的解析方式(DOM,SAX,StAX)]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36731</link>
<author>FoxWolf</author>
<pubDate>2008/6/11 14:49:47</pubDate>
<description><![CDATA[
<H4 id=subjcns!4001c604af3f011!471 style="MARGIN-BOTTOM: 0px"><FONT size=4>简说XML的解析方式(DOM,SAX,StAX)</FONT></H4>
<DIV class=bvMsg id=msgcns!4001c604af3f011!471><FONT size=3>一般来说,解析XML文件存在着两种方式,一种是event-based API，比如说象SAX,XNI. 第二种是tree-based API,比如说DOM,JDOM,DOM4j等等. 一般来说,读取配置文件时,我们一般比较喜欢应用tree-based API这种方式,就是把xml文件读入,变成DOM形式的一棵树,然后进行查找，获取自己说想要的东西. 但是,这种方式有个缺点,那就是如果你这个XML文件很大的话,你需要占用很大的内存.<BR>所以对于很大的一个xml文件,又不需要进行随机查找的时候,比较适合采用event-based API,那就是说他解析xml文件,如果是START_ELEMENT，那么他就调用startElement()的回调方法..他遍历过了就过了，不能再回去. <BR>在event-based API中又存在两种方式: 一个是PUSH的方式,就比如说是SAX. 另外一种是PULL的方式,比如StAX. <BR>怎么来理解PUSH和PULL的区别呢. 先假设有这么三个角色: application, xmlFile, xmlParser. 那么,如果我们采用PUSH的方式,步骤为:<BR>&nbsp;1. 创建一个xmlParser.<BR>&nbsp;2. 把我们的application处理xml的注册到xmlParser.<BR>&nbsp;3. xmlParser遍历xmlFile,然后来调用application.<BR>这里面,用的是Observer的模式,就是接收到event的时候,去调用event的callback函数, 这里面有个很不好的地方就是,你application反而是被Parser控制了.<BR>于是,就出现了PULL方式的解析.<BR>&nbsp;1. 创建一个xmlParser<BR>&nbsp;2. xmlParser打开一个xmlFile<BR>&nbsp;3. application调用这个xmlParser, 来获取xmlParser打开xmlFile所得到的一系列event.<BR>这里,用到了Iterator的模式. 最主要的一点是: 这个时候application控制了xmlParser.<BR>StAX有两种API,一种是cursor-based,一种是iterator-based. 这两种详细的比较参考: </FONT><A href="http://java.sun.com/webservices/docs/1.6/tutorial/doc/SJSXP3.html#wp102139"><FONT color=#006629 size=3>http://java.sun.com/webservices/docs/1.6/tutorial/doc/SJSXP3.html#wp102139</FONT></A><BR><BR><FONT size=3>这里,SAX和StAX的另外一点区别是: SAX只能读xml文件. StAX不但能读xml文件,而且还能写xml文件.<BR><BR>参考资料:<BR></FONT><A href="http://www.xml.com/pub/a/2003/09/17/stax.html?page=1"><FONT color=#006629 size=3>An Introduction to StAX</FONT></A><BR><A href="http://www.topxml.com/java/articles/sax_xml/default.asp"><FONT color=#006629 size=3>Having Good SAX with Java</FONT></A></DIV>]]></description>
</item><item>
<title><![CDATA[(转)boa服务器make错误]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=36142</link>
<author>FoxWolf</author>
<pubDate>2008/5/29 13:52:05</pubDate>
<description><![CDATA[编译一个linux下的c系统，包含词法和语法分析模块，Linux上用bison和flex。
<DIV>yacc是一个文法分析器的生成器,bison即是yacc的GNU版本.Lex和YACC是用于构造词法分析机和语法解释器的工具，利用Lex和YACC你可以轻松的构造一个语法解释器。</DIV>
<DIV>&nbsp;</DIV>
<DIV>yacc(Yet Another Compiler Compiler)，是Unix/Linux上一个用来生成编译器的编译器（编译器代码生成器）。yacc生成的编译器主要是用C语言写成的语法解析器（Parser），需要与词法解析器Lex一起使用，再把两部份产生出来的C程序一并编译。yacc本来只在Unix系统上才有，但现时已普遍移植往Windows及其他平台。<BR></DIV>
<DIV>&nbsp;</DIV>
<DIV>一开始make</DIV>
<DIV>错误1：</DIV>
<DIV>debian:/home/a/boa-0.94.13/src# make<BR>yacc&nbsp; -d boa_grammar.y<BR>make: yacc: Command not found<BR>make: *** [y.tab.c] Error 127</DIV>
<DIV>&nbsp;</DIV>
<DIV>解决方法：<BR>debian:/home/a/boa-0.94.13/src# apt-get install bison<BR></DIV>
<DIV>错误2：</DIV>
<DIV>debian:/home/a/boa-0.94.13/src# make<BR>lex&nbsp; boa_lexer.l<BR>make: lex: Command not found<BR>make: *** [lex.yy.c] Error 127<BR></DIV>
<DIV>解决方法：</DIV>
<DIV>debian:/home/a/boa-0.94.13/src# apt-get install flex</DIV>
<DIV>&nbsp;</DIV>
<DIV>错误3：</DIV>
<DIV>debian:/home/a/sss/boa-0.94.13/src# make<BR>gcc&nbsp; -g -O2 -pipe -Wall -I.&nbsp;&nbsp; -c -o util.o util.c<BR>util.c:100:1: error: pasting "t" and "-&gt;" does not give a valid preprocessing token<BR>make: *** [util.o] Error 1</DIV>
<DIV>解决方法：</DIV>
<DIV>修改 src/compat.h<BR>找到<BR>#define TIMEZONE_OFFSET(foo) <A name=baidusnap0></A><B style="COLOR: black; BACKGROUND-COLOR: #ffff66">foo##-</B>&gt;<A name=baidusnap4></A><B style="COLOR: black; BACKGROUND-COLOR: #ff66ff">tm_gmtoff</B><BR>修改成<BR>#define TIMEZONE_OFFSET(foo) (foo)-&gt;<B style="COLOR: black; BACKGROUND-COLOR: #ff66ff">tm_gmtoff</B><BR></DIV>
<DIV>然后</DIV>
<DIV>debian:/home/a/sss/boa-0.94.13/src# make clean</DIV>
<DIV>debian:/home/a/sss/boa-0.94.13/src# make</DIV>
<DIV>&nbsp;</DIV>
<DIV>出现：</DIV>
<DIV>debian:/home/a/sss/boa-0.94.13/src# make</DIV>
<DIV>make: Nothing to be done for `all'.</DIV>
<DIV>
<DIV>说明make没有检查到需要编译的东西，库或者应用程序已经编译好了。<BR>make主要检查的是时间戳， 只要target比依赖的文件时间靠后它就认为不需要编译</DIV></DIV>]]></description>
</item><item>
<title><![CDATA[转：GDB 的文档]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=35791</link>
<author>FoxWolf</author>
<pubDate>2008/5/23 17:42:23</pubDate>
<description><![CDATA[<DIV class="subtable altbg2 t_msg" style="WIDTH: auto; HEIGHT: auto"><B>转：GDB 的文档</B><BR><BR>在CSDN上看到一篇GDB的文档，挺适合我，不知对你有没有用...&nbsp; &nbsp;&nbsp;&nbsp;<BR>/****************************************************/<BR>[code]用GDB调试程序<BR>GDB概述&nbsp; &nbsp; &nbsp; &nbsp; 2<BR>使用GDB&nbsp; &nbsp; &nbsp; &nbsp; 5<BR>GDB中运行UNIX的shell程序&nbsp; &nbsp; &nbsp; &nbsp; 8<BR>在GDB中运行程序&nbsp; &nbsp; &nbsp; &nbsp; 8<BR>调试已运行的程序 两种方法：&nbsp; &nbsp; &nbsp; &nbsp; 9<BR>暂停 / 恢复程序运行&nbsp; &nbsp; &nbsp; &nbsp; 9<BR>一、设置断点（BreakPoint）&nbsp; &nbsp; &nbsp; &nbsp; 9<BR>二、设置观察点（WatchPoint）&nbsp; &nbsp; &nbsp; &nbsp; 10<BR>三、设置捕捉点（CatchPoint）&nbsp; &nbsp; &nbsp; &nbsp; 10<BR>四、维护停止点&nbsp; &nbsp; &nbsp; &nbsp; 11<BR>五、停止条件维护&nbsp; &nbsp; &nbsp; &nbsp; 12<BR>六、为停止点设定运行命令&nbsp; &nbsp; &nbsp; &nbsp; 12<BR>七、断点菜单&nbsp; &nbsp; &nbsp; &nbsp; 13<BR>八、恢复程序运行和单步调试&nbsp; &nbsp; &nbsp; &nbsp; 13<BR>九、信号（Signals）&nbsp; &nbsp; &nbsp; &nbsp; 14<BR>十、线程（Thread Stops）&nbsp; &nbsp; &nbsp; &nbsp; 15<BR>查看栈信息&nbsp; &nbsp; &nbsp; &nbsp; 16<BR>查看源程序&nbsp; &nbsp; &nbsp; &nbsp; 18<BR>一、显示源代码&nbsp; &nbsp; &nbsp; &nbsp; 18<BR>二、搜索源代码&nbsp; &nbsp; &nbsp; &nbsp; 19<BR>三、指定源文件的路径&nbsp; &nbsp; &nbsp; &nbsp; 19<BR>四、源代码的内存&nbsp; &nbsp; &nbsp; &nbsp; 20<BR>查看运行时数据&nbsp; &nbsp; &nbsp; &nbsp; 21<BR>一、表达式&nbsp; &nbsp; &nbsp; &nbsp; 21<BR>二、程序变量&nbsp; &nbsp; &nbsp; &nbsp; 21<BR>三、数组&nbsp; &nbsp; &nbsp; &nbsp; 22<BR>四、输出格式&nbsp; &nbsp; &nbsp; &nbsp; 23<BR>五、查看内存&nbsp; &nbsp; &nbsp; &nbsp; 23<BR>六、自动显示&nbsp; &nbsp; &nbsp; &nbsp; 24<BR>七、设置显示选项&nbsp; &nbsp; &nbsp; &nbsp; 25<BR>GDB中关于显示的选项比较多，这里我只例举大多数常用的选项。&nbsp; &nbsp; &nbsp; &nbsp; 25<BR>八、历史记录&nbsp; &nbsp; &nbsp; &nbsp; 27<BR>九、GDB环境变量&nbsp; &nbsp; &nbsp; &nbsp; 28<BR>十、查看寄存器&nbsp; &nbsp; &nbsp; &nbsp; 28<BR>改变程序的执行&nbsp; &nbsp; &nbsp; &nbsp; 29<BR>一、修改变量值&nbsp; &nbsp; &nbsp; &nbsp; 29<BR>二、跳转执行&nbsp; &nbsp; &nbsp; &nbsp; 29<BR>三、产生信号量&nbsp; &nbsp; &nbsp; &nbsp; 30<BR>四、强制函数返回&nbsp; &nbsp; &nbsp; &nbsp; 30<BR>五、强制调用函数&nbsp; &nbsp; &nbsp; &nbsp; 30<BR>在不同语言中使用GDB&nbsp; &nbsp; &nbsp; &nbsp; 31<BR>后记&nbsp; &nbsp; &nbsp; &nbsp; 32<BR><BR>GDB概述<BR>GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许，各位比较喜欢那种图形界面方式的，像VC、BCB等IDE的调试，但如果你是在UNIX平台下做软件，你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓“寸有所长，尺有所短”就是这个道理。<BR><BR>一般来说，GDB主要帮忙你完成下面四个方面的功能：<BR><BR>&nbsp; &nbsp; 1、启动你的程序，可以按照你的自定义的要求随心所欲的运行程序。<BR>&nbsp; &nbsp; 2、可让被调试的程序在你所指定的调置的断点处停住。（断点可以是条件表达式）<BR>&nbsp; &nbsp; 3、当程序被停住时，可以检查此时你的程序中所发生的事。<BR>&nbsp; &nbsp; 4、动态的改变你程序的执行环境。<BR><BR>从上面看来，GDB和一般的调试工具没有什么两样，基本上也是完成这些功能，不过在细节上，你会发现GDB这个调试工具的强大，大家可能比较习惯了图形化的调试工具，但有时候，命令行的调试工具却有着图形化工具所不能完成的功能。让我们一一看来。<BR><BR>一个调试示例<BR>——————<BR><BR>源程序：tst.c<BR>&nbsp; &nbsp;&nbsp;&nbsp;1 #include &lt;stdio.h&gt;;<BR>&nbsp; &nbsp;&nbsp;&nbsp;2<BR>&nbsp; &nbsp;&nbsp;&nbsp;3 int func(int n)<BR>&nbsp; &nbsp;&nbsp;&nbsp;4 {<BR>&nbsp; &nbsp;&nbsp;&nbsp;5&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;int sum=0,i;<BR>&nbsp; &nbsp;&nbsp;&nbsp;6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for(i=0; i&lt;n; i++)<BR>&nbsp; &nbsp;&nbsp;&nbsp;7&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp;&nbsp;8&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;sum+=i;<BR>&nbsp; &nbsp;&nbsp;&nbsp;9&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp; 10&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return sum;<BR>&nbsp; &nbsp; 11 }<BR>&nbsp; &nbsp; 12<BR>&nbsp; &nbsp; 13<BR>&nbsp; &nbsp; 14 main()<BR>&nbsp; &nbsp; 15 {<BR>&nbsp; &nbsp; 16&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;int i;<BR>&nbsp; &nbsp; 17&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;long result = 0;<BR>&nbsp; &nbsp; 18&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for(i=1; i&lt;=100; i++)<BR>&nbsp; &nbsp; 19&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp; 20&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;result += i;<BR>&nbsp; &nbsp; 21&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp; 22<BR>&nbsp; &nbsp; 23&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;printf("result[1-100] = %d \n", result );<BR>&nbsp; &nbsp; 24&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;printf("result[1-250] = %d \n", func(250) );<BR>&nbsp; &nbsp; 25 }<BR><BR>编译生成执行文件：（Linux下）<BR>&nbsp; &nbsp; hchen/test&gt;; cc -g tst.c -o tst<BR><BR>使用GDB调试：<BR><BR>hchen/test&gt;; gdb tst&nbsp;&nbsp;&lt;---------- 启动GDB<BR>GNU gdb 5.1.1<BR>Copyright 2002 Free Software Foundation, Inc.<BR>GDB is free software, covered by the GNU General Public License, and you are<BR>welcome to change it and/or distribute copies of it under certain conditions.<BR>Type "show copying" to see the conditions.<BR>There is absolutely no warranty for GDB.&nbsp;&nbsp;Type "show warranty" for details.<BR>This GDB was configured as "i386-suse-linux"...<BR>(gdb) l&nbsp; &nbsp;&nbsp;&nbsp;&lt;-------------------- l命令相当于list，从第一行开始例出原码。<BR>1&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;#include &lt;stdio.h&gt;;<BR>2<BR>3&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;int func(int n)<BR>4&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>5&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; int sum=0,i;<BR>6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; for(i=0; i&lt;n; i++)<BR>7&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; {<BR>8&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;sum+=i;<BR>9&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; }<BR>10&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return sum;<BR>(gdb)&nbsp; &nbsp;&nbsp; &nbsp; &lt;-------------------- 直接回车表示，重复上一次命令<BR>11&nbsp; &nbsp;&nbsp; &nbsp; }<BR>12<BR>13<BR>14&nbsp; &nbsp;&nbsp; &nbsp; main()<BR>15&nbsp; &nbsp;&nbsp; &nbsp; {<BR>16&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;int i;<BR>17&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;long result = 0;<BR>18&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for(i=1; i&lt;=100; i++)<BR>19&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>20&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;result += i;&nbsp; &nbsp; <BR>(gdb) break 16&nbsp; &nbsp; &lt;-------------------- 设置断点，在源程序第16行处。<BR>Breakpoint 1 at 0x8048496: file tst.c, line 16.<BR>(gdb) break func&nbsp;&nbsp;&lt;-------------------- 设置断点，在函数func()入口处。<BR>Breakpoint 2 at 0x8048456: file tst.c, line 5.<BR>(gdb) info break&nbsp;&nbsp;&lt;-------------------- 查看断点信息。<BR>Num Type&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Disp Enb Address&nbsp; &nbsp; What<BR>1&nbsp; &nbsp;breakpoint&nbsp; &nbsp;&nbsp;&nbsp;keep y&nbsp; &nbsp;0x08048496 in main at tst.c:16<BR>2&nbsp; &nbsp;breakpoint&nbsp; &nbsp;&nbsp;&nbsp;keep y&nbsp; &nbsp;0x08048456 in func at tst.c:5<BR>(gdb) r&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;--------------------- 运行程序，run命令简写<BR>Starting program: /home/hchen/test/tst<BR><BR>Breakpoint 1, main () at tst.c:17&nbsp; &nbsp; &lt;---------- 在断点处停住。<BR>17&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;long result = 0;<BR>(gdb) n&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;--------------------- 单条语句执行，next命令简写。<BR>18&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for(i=1; i&lt;=100; i++)<BR>(gdb) n<BR>20&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;result += i;<BR>(gdb) n<BR>18&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for(i=1; i&lt;=100; i++)<BR>(gdb) n<BR>20&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;result += i;<BR>(gdb) c&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &lt;--------------------- 继续运行程序，continue命令简写。<BR>Continuing.<BR>result[1-100] = 5050&nbsp; &nbsp;&nbsp; &nbsp; &lt;----------程序输出。<BR><BR>Breakpoint 2, func (n=250) at tst.c:5<BR>5&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; int sum=0,i;<BR>(gdb) n<BR>6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; for(i=1; i&lt;=n; i++)<BR>(gdb) p i&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;--------------------- 打印变量i的值，print命令简写。<BR>$1 = 134513808<BR>(gdb) n<BR>8&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;sum+=i;<BR>(gdb) n<BR>6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; for(i=1; i&lt;=n; i++)<BR>(gdb) p sum<BR>$2 = 1<BR>(gdb) n<BR>8&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;sum+=i;<BR>(gdb) p i<BR>$3 = 2<BR>(gdb) n<BR>6&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; for(i=1; i&lt;=n; i++)<BR>(gdb) p sum<BR>$4 = 3<BR>(gdb) bt&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;--------------------- 查看函数堆栈。<BR>#0&nbsp;&nbsp;func (n=250) at tst.c:5<BR>#1&nbsp;&nbsp;0x080484e4 in main () at tst.c:24<BR>#2&nbsp;&nbsp;0x400409ed in __libc_start_main () from /lib/libc.so.6<BR>(gdb) finish&nbsp; &nbsp; &lt;--------------------- 退出函数。<BR>Run till exit from #0&nbsp;&nbsp;func (n=250) at tst.c:5<BR>0x080484e4 in main () at tst.c:24<BR>24&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;printf("result[1-250] = %d \n", func(250) );<BR>Value returned is $6 = 31375<BR>(gdb) c&nbsp; &nbsp;&nbsp;&nbsp;&lt;--------------------- 继续运行。<BR>Continuing.<BR>result[1-250] = 31375&nbsp; &nbsp; &lt;----------程序输出。<BR><BR>Program exited with code 027. &lt;--------程序退出，调试结束。<BR>(gdb) q&nbsp; &nbsp;&nbsp;&nbsp;&lt;--------------------- 退出gdb。<BR>hchen/test&gt;;<BR><BR>好了，有了以上的感性认识，还是让我们来系统地认识一下gdb吧。<BR>使用GDB<BR>一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序，首先在编译时，我们必须要把调试信息加到可执行文件中。使用编译器（cc/gcc/g++）的 -g 参数可以做到这一点。如：<BR><BR>&nbsp; &nbsp; &gt;; cc -g hello.c -o hello<BR>&nbsp; &nbsp; &gt;; g++ -g hello.cpp -o hello<BR><BR>如果没有-g，你将看不见程序的函数名、变量名，所代替的全是运行时的内存地址。当你用-g把调试信息加入之后，并成功编译目标代码以后，让我们来看看如何用gdb来调试他。<BR><BR>启动GDB的方法有以下几种：<BR><BR>&nbsp; &nbsp; 1、gdb &lt;program&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp; program也就是你的执行文件，一般在当然目录下。<BR><BR>&nbsp; &nbsp; 2、gdb &lt;program&gt;; core<BR>&nbsp; &nbsp;&nbsp; &nbsp; 用gdb同时调试一个运行程序和core文件，core是程序非法执行后core dump后产生的文件。<BR><BR>&nbsp; &nbsp; 3、gdb &lt;program&gt;; &lt;PID&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp; 如果你的程序是一个服务程序，那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去，并调试他。program应该在PATH环境变量中搜索得到。<BR><BR><BR><BR>GDB启动时，可以加上一些GDB的启动开关，详细的开关可以用gdb -help查看。我在下面只例举一些比较常用的参数：<BR><BR>&nbsp; &nbsp; -symbols &lt;file&gt;; <BR>&nbsp; &nbsp; -s &lt;file&gt;; <BR>&nbsp; &nbsp; 从指定文件中读取符号表。<BR><BR>&nbsp; &nbsp; -se file <BR>&nbsp; &nbsp; 从指定文件中读取符号表信息，并把他用在可执行文件中。<BR><BR>&nbsp; &nbsp; -core &lt;file&gt;;<BR>&nbsp; &nbsp; -c &lt;file&gt;; <BR>&nbsp; &nbsp; 调试时core dump的core文件。<BR><BR>&nbsp; &nbsp; -directory &lt;directory&gt;;<BR>&nbsp; &nbsp; -d &lt;directory&gt;;<BR>&nbsp; &nbsp; 加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。<BR>启动gdb后，就你被带入gdb的调试环境中，就可以使用gdb的命令开始调试程序了，gdb的命令可以使用help命令来查看，如下所示：<BR><BR>&nbsp; &nbsp; /home/hchen&gt;; gdb<BR>&nbsp; &nbsp; GNU gdb 5.1.1<BR>&nbsp; &nbsp; Copyright 2002 Free Software Foundation, Inc.<BR>&nbsp; &nbsp; GDB is free software, covered by the GNU General Public License, and you are<BR>&nbsp; &nbsp; welcome to change it and/or distribute copies of it under certain conditions.<BR>&nbsp; &nbsp; Type "show copying" to see the conditions.<BR>&nbsp; &nbsp; There is absolutely no warranty for GDB.&nbsp;&nbsp;Type "show warranty" for details.<BR>&nbsp; &nbsp; This GDB was configured as "i386-suse-linux".<BR>&nbsp; &nbsp; (gdb) help<BR>&nbsp; &nbsp; List of classes of commands:<BR><BR>&nbsp; &nbsp; aliases -- Aliases of other commands<BR>&nbsp; &nbsp; breakpoints -- Making program stop at certain points<BR>&nbsp; &nbsp; data -- Examining data<BR>&nbsp; &nbsp; files -- Specifying and examining files<BR>&nbsp; &nbsp; internals -- Maintenance commands<BR>&nbsp; &nbsp; obscure -- Obscure features<BR>&nbsp; &nbsp; running -- Running the program<BR>&nbsp; &nbsp; stack -- Examining the stack<BR>&nbsp; &nbsp; status -- Status inquiries<BR>&nbsp; &nbsp; support -- Support facilities<BR>&nbsp; &nbsp; tracepoints -- Tracing of program execution without stopping the program<BR>&nbsp; &nbsp; user-defined -- User-defined commands<BR><BR>&nbsp; &nbsp; Type "help" followed by a class name for a list of commands in that class.<BR>&nbsp; &nbsp; Type "help" followed by command name for full documentation.<BR>&nbsp; &nbsp; Command name abbreviations are allowed if unambiguous.<BR>&nbsp; &nbsp; (gdb)<BR><BR>gdb的命令很多，gdb把之分成许多个种类。help命令只是例出gdb的命令种类，如果要看种类中的命令，可以使用help &lt;class&gt;; 命令，如：help breakpoints，查看设置断点的所有命令。也可以直接help &lt;command&gt;;来查看命令的帮助。<BR><BR>gdb中，输入命令时，可以不用打全命令，只用打命令的前几个字符就可以了，当然，命令的前几个字符应该要标志着一个唯一的命令，在Linux下，你可以敲击两次TAB键来补齐命令的全称，如果有重复的，那么gdb会把其例出来。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 示例一：在进入函数func时，设置一个断点。可以敲入break func，或是直接就是b func<BR>&nbsp; &nbsp; (gdb) b func<BR>&nbsp; &nbsp; Breakpoint 1 at 0x8048458: file hello.c, line 10.<BR><BR>&nbsp; &nbsp; 示例二：敲入b按两次TAB键，你会看到所有b打头的命令：<BR>&nbsp; &nbsp; (gdb) b<BR>&nbsp; &nbsp; backtrace&nbsp;&nbsp;break&nbsp; &nbsp;&nbsp; &nbsp;bt<BR>&nbsp; &nbsp; (gdb)<BR><BR>&nbsp; &nbsp; 示例三：只记得函数的前缀，可以这样：<BR>&nbsp; &nbsp; (gdb) b make_ &lt;按TAB键&gt;;<BR>&nbsp; &nbsp; （再按下一次TAB键，你会看到:）<BR>&nbsp; &nbsp; make_a_section_from_file&nbsp; &nbsp;&nbsp;&nbsp;make_environ<BR>&nbsp; &nbsp; make_abs_section&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; make_function_type<BR>&nbsp; &nbsp; make_blockvector&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; make_pointer_type<BR>&nbsp; &nbsp; make_cleanup&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;make_reference_type<BR>&nbsp; &nbsp; make_command&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;make_symbol_completion_list<BR>&nbsp; &nbsp; (gdb) b make_<BR>&nbsp; &nbsp; GDB把所有make开头的函数全部例出来给你查看。<BR><BR>&nbsp; &nbsp; 示例四：调试C++的程序时，有可以函数名一样。如：<BR>&nbsp; &nbsp; (gdb) b 'bubble( M-? <BR>&nbsp; &nbsp; bubble(double,double)&nbsp; &nbsp; bubble(int,int)<BR>&nbsp; &nbsp; (gdb) b 'bubble(<BR>&nbsp; &nbsp; 你可以查看到C++中的所有的重载函数及参数。（注：M-?和“按两次TAB键”是一个意思）<BR><BR>要退出gdb时，只用发quit或命令简称q就行了。 <BR>GDB中运行UNIX的shell程序<BR>在gdb环境中，你可以执行UNIX的shell的命令，使用gdb的shell命令来完成：<BR><BR>&nbsp; &nbsp; shell &lt;command string&gt;;<BR>&nbsp; &nbsp; 调用UNIX的shell来执行&lt;command string&gt;;，环境变量SHELL中定义的UNIX的shell将会被用来执行&lt;command string&gt;;，如果SHELL没有定义，那就使用UNIX的标准shell：/bin/sh。（在Windows中使用Command.com或cmd.exe）<BR><BR>还有一个gdb命令是make：<BR>&nbsp; &nbsp; make &lt;make-args&gt;; <BR>&nbsp; &nbsp; 可以在gdb中执行make命令来重新build自己的程序。这个命令等价于“shell make &lt;make-args&gt;;”。 <BR>在GDB中运行程序<BR>当以gdb &lt;program&gt;;方式启动gdb后，gdb会在PATH路径和当前目录中搜索&lt;program&gt;;的源文件。如要确认gdb是否读到源文件，可使用l或list命令，看看gdb是否能列出源代码。<BR><BR>在gdb中，运行程序使用r或是run命令。程序的运行，你有可能需要设置下面四方面的事。<BR><BR>1、程序运行参数。<BR>&nbsp; &nbsp; set args 可指定运行时参数。（如：set args 10 20 30 40 50）<BR>&nbsp; &nbsp; show args 命令可以查看设置好的运行参数。<BR><BR>2、运行环境。<BR>&nbsp; &nbsp; path &lt;dir&gt;; 可设定程序的运行路径。<BR>&nbsp; &nbsp; show paths 查看程序的运行路径。<BR>&nbsp; &nbsp; set environment varname [=value] 设置环境变量。如：set env USER=hchen<BR>&nbsp; &nbsp; show environment [varname] 查看环境变量。<BR><BR>3、工作目录。<BR>&nbsp; &nbsp; cd &lt;dir&gt;; 相当于shell的cd命令。<BR>&nbsp; &nbsp; pwd 显示当前的所在目录。<BR><BR>4、程序的输入输出。<BR>&nbsp; &nbsp; info terminal 显示你程序用到的终端的模式。<BR>&nbsp; &nbsp; 使用重定向控制程序输出。如：run &gt;; outfile<BR>&nbsp; &nbsp; tty命令可以指写输入输出的终端设备。如：tty /dev/ttyb<BR><BR><BR>调试已运行的程序 两种方法：<BR>1、在UNIX下用ps查看正在运行的程序的PID（进程ID），然后用gdb &lt;program&gt;; PID格式挂接正在运行的程序。<BR>2、先用gdb &lt;program&gt;;关联上源代码，并进行gdb，在gdb中用attach命令来挂接进程的PID。并用detach来取消挂接的进程。<BR>暂停 / 恢复程序运行<BR>调试程序中，暂停程序运行是必须的，GDB可以方便地暂停程序的运行。你可以设置程序的在哪行停住，在什么条件下停住，在收到什么信号时停往等等。以便于你查看运行时的变量，以及运行时的流程。<BR><BR>当进程被gdb停住时，你可以使用info program 来查看程序的是否在运行，进程号，被暂停的原因。<BR><BR>在gdb中，我们可以有以下几种暂停方式：断点（BreakPoint）、观察点（WatchPoint）、捕捉点（CatchPoint）、信号（Signals）、线程停止（Thread Stops）。如果要恢复程序运行，可以使用c或是continue命令。<BR>一、设置断点（BreakPoint）<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 我们用break命令来设置断点。正面有几点设置断点的方法：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; break &lt;function&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函数名。<BR><BR>&nbsp; &nbsp; break &lt;linenum&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在指定行号停住。<BR><BR>&nbsp; &nbsp; break +offset <BR>&nbsp; &nbsp; break -offset <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在当前行号的前面或后面的offset行停住。offiset为自然数。<BR><BR>&nbsp; &nbsp; break filename:linenum <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在源文件filename的linenum行处停住。<BR><BR>&nbsp; &nbsp; break filename:function <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在源文件filename的function函数的入口处停住。<BR><BR>&nbsp; &nbsp; break *address<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在程序运行的内存地址处停住。<BR><BR>&nbsp; &nbsp; break <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;break命令没有参数时，表示在下一条指令处停住。<BR><BR>&nbsp; &nbsp; break ... if &lt;condition&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;...可以是上述的参数，condition表示条件，在条件成立时停住。比如在循环境体中，可以设置break if i=100，表示当i为100时停住程序。<BR><BR>&nbsp; &nbsp; 查看断点时，可使用info命令，如下所示：（注：n表示断点号）<BR>&nbsp; &nbsp; info breakpoints [n] <BR>&nbsp; &nbsp; info break [n] <BR>二、设置观察点（WatchPoint）<BR>&nbsp; &nbsp; 观察点一般来观察某个表达式（变量也是一种表达式）的值是否有变化了，如果有变化，马上停住程序。我们有下面的几种方法来设置观察点：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; watch &lt;expr&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;为表达式（变量）expr设置一个观察点。一量表达式值有变化时，马上停住程序。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; rwatch &lt;expr&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当表达式（变量）expr被读时，停住程序。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; awatch &lt;expr&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当表达式（变量）的值被读或被写时，停住程序。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; info watchpoints<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;列出当前所设置了的所有观察点。<BR>三、设置捕捉点（CatchPoint）<BR>&nbsp; &nbsp; 你可设置捕捉点来补捉程序运行时的一些事件。如：载入共享库（动态链接库）或是C++的异常。设置捕捉点的格式为：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; catch &lt;event&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当event发生时，停住程序。event可以是下面的内容：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;1、throw 一个C++抛出的异常。（throw为关键字）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;2、catch 一个C++捕捉到的异常。（catch为关键字）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;3、exec 调用系统调用exec时。（exec为关键字，目前此功能只在HP-UX下有用）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;4、fork 调用系统调用fork时。（fork为关键字，目前此功能只在HP-UX下有用）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;5、vfork 调用系统调用vfork时。（vfork为关键字，目前此功能只在HP-UX下有用）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;6、load 或 load &lt;libname&gt;; 载入共享库（动态链接库）时。（load为关键字，目前此功能只在HP-UX下有用）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;7、unload 或 unload &lt;libname&gt;; 卸载共享库（动态链接库）时。（unload为关键字，目前此功能只在HP-UX下有用）<BR><BR>&nbsp; &nbsp; tcatch &lt;event&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;只设置一次捕捉点，当程序停住以后，应点被自动删除。<BR>四、维护停止点<BR>上面说了如何设置程序的停止点，GDB中的停止点也就是上述的三类。在GDB中，如果你觉得已定义好的停止点没有用了，你可以使用delete、clear、disable、enable这几个命令来进行维护。<BR><BR>&nbsp; &nbsp; clear<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;清除所有的已定义的停止点。<BR><BR>&nbsp; &nbsp; clear &lt;function&gt;;<BR>&nbsp; &nbsp; clear &lt;filename:function&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;清除所有设置在函数上的停止点。<BR><BR>&nbsp; &nbsp; clear &lt;linenum&gt;;<BR>&nbsp; &nbsp; clear &lt;filename:linenum&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;清除所有设置在指定行上的停止点。<BR><BR>&nbsp; &nbsp; delete [breakpoints] [range...]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;删除指定的断点，breakpoints为断点号。如果不指定断点号，则表示删除所有的断点。range 表示断点号的范围（如：3-7）。其简写命令为d。<BR><BR>比删除更好的一种方法是disable停止点，disable了的停止点，GDB不会删除，当你还需要时，enable即可，就好像回收站一样。<BR><BR>&nbsp; &nbsp; disable [breakpoints] [range...]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;disable所指定的停止点，breakpoints为停止点号。如果什么都不指定，表示disable所有的停止点。简写命令是dis.<BR><BR>&nbsp; &nbsp; enable [breakpoints] [range...]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;enable所指定的停止点，breakpoints为停止点号。<BR><BR>&nbsp; &nbsp; enable [breakpoints] once range...<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;enable所指定的停止点一次，当程序停止后，该停止点马上被GDB自动disable。<BR>&nbsp; &nbsp; enable [breakpoints] delete range...<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;enable所指定的停止点一次，当程序停止后，该停止点马上被GDB自动删除。<BR>五、停止条件维护<BR>前面在说到设置断点时，我们提到过可以设置一个条件，当条件成立时，程序自动停止，这是一个非常强大的功能，这里，我想专门说说这个条件的相关维护命令。一般来说，为断点设置一个条件，我们使用if关键词，后面跟其断点条件。并且，条件设置好后，我们可以用condition命令来修改断点的条件。（只有break和watch命令支持if，catch目前暂不支持if）<BR><BR>&nbsp; &nbsp; condition &lt;bnum&gt;; &lt;expression&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;修改断点号为bnum的停止条件为expression。<BR><BR>&nbsp; &nbsp; condition &lt;bnum&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;清除断点号为bnum的停止条件。<BR><BR><BR>还有一个比较特殊的维护命令ignore，你可以指定程序运行时，忽略停止条件几次。<BR><BR>&nbsp; &nbsp; ignore &lt;bnum&gt;; &lt;count&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;表示忽略断点号为bnum的停止条件count次。<BR>六、为停止点设定运行命令<BR>我们可以使用GDB提供的command命令来设置停止点的运行命令。也就是说，当运行的程序在被停止住时，我们可以让其自动运行一些别的命令，这很有利行自动化调试。对基于GDB的自动化调试是一个强大的支持。<BR><BR>&nbsp; &nbsp; commands [bnum]<BR>&nbsp; &nbsp; ... command-list ...<BR>&nbsp; &nbsp; end<BR><BR>&nbsp; &nbsp; 为断点号bnum指写一个命令列表。当程序被该断点停住时，gdb会依次运行命令列表中的命令。<BR><BR>&nbsp; &nbsp; 例如：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;break foo if x&gt;;0<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;commands<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;printf "x is %d\n",x<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;continue<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;end <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;断点设置在函数foo中，断点条件是x&gt;;0，如果程序被断住后，也就是，一旦x的值在foo函数中大于0，GDB会自动打印出x的值，并继续运行程序。<BR><BR>如果你要清除断点上的命令序列，那么只要简单的执行一下commands命令，并直接在打个end就行了。<BR>七、断点菜单<BR>在C++中，可能会重复出现同一个名字的函数若干次（函数重载），在这种情况下，break &lt;function&gt;;不能告诉GDB要停在哪个函数的入口。当然，你可以使用break &lt;function(type)&gt;;也就是把函数的参数类型告诉GDB，以指定一个函数。否则的话，GDB会给你列出一个断点菜单供你选择你所需要的断点。你只要输入你菜单列表中的编号就可以了。如：<BR><BR>&nbsp; &nbsp; (gdb) b String::after<BR>&nbsp; &nbsp; [0] cancel<BR>&nbsp; &nbsp; [1] all<BR>&nbsp; &nbsp; [2] file:String.cc; line number:867<BR>&nbsp; &nbsp; [3] file:String.cc; line number:860<BR>&nbsp; &nbsp; [4] file:String.cc; line number:875<BR>&nbsp; &nbsp; [5] file:String.cc; line number:853<BR>&nbsp; &nbsp; [6] file:String.cc; line number:846<BR>&nbsp; &nbsp; [7] file:String.cc; line number:735<BR>&nbsp; &nbsp; &gt;; 2 4 6<BR>&nbsp; &nbsp; Breakpoint 1 at 0xb26c: file String.cc, line 867.<BR>&nbsp; &nbsp; Breakpoint 2 at 0xb344: file String.cc, line 875.<BR>&nbsp; &nbsp; Breakpoint 3 at 0xafcc: file String.cc, line 846.<BR>&nbsp; &nbsp; Multiple breakpoints were set.<BR>&nbsp; &nbsp; Use the "delete" command to delete unwanted<BR>&nbsp; &nbsp;&nbsp;&nbsp;breakpoints.<BR>&nbsp; &nbsp; (gdb)<BR><BR>可见，GDB列出了所有after的重载函数，你可以选一下列表编号就行了。0表示放弃设置断点，1表示所有函数都设置断点。<BR>八、恢复程序运行和单步调试<BR>当程序被停住了，你可以用continue命令恢复程序的运行直到程序结束，或下一个断点到来。也可以使用step或next命令单步跟踪程序。<BR><BR>&nbsp; &nbsp; continue [ignore-count]<BR>&nbsp; &nbsp; c [ignore-count]<BR>&nbsp; &nbsp; fg [ignore-count]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;恢复程序运行，直到程序结束，或是下一个断点到来。ignore-count表示忽略其后的断点次数。continue，c，fg三个命令都是一样的意思。<BR><BR><BR>&nbsp; &nbsp; step &lt;count&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;单步跟踪，如果有函数调用，他会进入该函数。进入函数的前提是，此函数被编译有debug信息。很像VC等工具中的step in。后面可以加count也可以不加，不加表示一条条地执行，加表示执行后面的count条指令，然后再停住。<BR><BR>&nbsp; &nbsp; next &lt;count&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;同样单步跟踪，如果有函数调用，他不会进入该函数。很像VC等工具中的step over。后面可以加count也可以不加，不加表示一条条地执行，加表示执行后面的count条指令，然后再停住。<BR><BR>&nbsp; &nbsp; set step-mode<BR>&nbsp; &nbsp; set step-mode on<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打开step-mode模式，于是，在进行单步跟踪时，程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码。<BR><BR>&nbsp; &nbsp; set step-mod off<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;关闭step-mode模式。<BR><BR>&nbsp; &nbsp; finish<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;运行程序，直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。<BR><BR>&nbsp; &nbsp; until 或 u<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当你厌倦了在一个循环体内单步跟踪时，这个命令可以运行程序直到退出循环体。<BR><BR>&nbsp; &nbsp; stepi 或 si<BR>&nbsp; &nbsp; nexti 或 ni<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;单步跟踪一条机器指令！一条程序代码有可能由数条机器指令完成，stepi和nexti可以单步执行机器指令。与之一样有相同功能的命令是“display/i $pc” ，当运行完这个命令后，单步跟踪会在打出程序代码的同时打出机器指令（也就是汇编代码）<BR>九、信号（Signals）<BR>信号是一种软中断，是一种处理异步事件的方法。一般来说，操作系统都支持许多信号。尤其是UNIX，比较重要应用程序一般都会处理信号。UNIX定义了许多信号，比如SIGINT表示中断字符信号，也就是Ctrl+C的信号，SIGBUS表示硬件故障的信号；SIGCHLD表示子进程状态改变信号；SIGKILL表示终止程序运行的信号，等等。信号量编程是UNIX下非常重要的一种技术。<BR><BR>GDB有能力在你调试程序的时候处理任何一种信号，你可以告诉GDB需要处理哪一种信号。你可以要求GDB收到你所指定的信号时，马上停住正在运行的程序，以供你进行调试。你可以用GDB的handle命令来完成这一功能。<BR><BR>&nbsp; &nbsp; handle &lt;signal&gt;; &lt;keywords...&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在GDB中定义一个信号处理。信号&lt;signal&gt;;可以以SIG开头或不以SIG开头，可以用定义一个要处理信号的范围（如：SIGIO-SIGKILL，表示处理从SIGIO信号到SIGKILL的信号，其中包括SIGIO，SIGIOT，SIGKILL三个信号），也可以使用关键字all来标明要处理所有的信号。一旦被调试的程序接收到信号，运行程序马上会被GDB停住，以供调试。其&lt;keywords&gt;;可以是以下几种关键字的一个或多个。<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;nostop<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;当被调试的程序收到信号时，GDB不会停住程序的运行，但会打出消息告诉你收到这种信号。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;stop<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;当被调试的程序收到信号时，GDB会停住你的程序。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;print<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;当被调试的程序收到信号时，GDB会显示出一条信息。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;noprint<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;当被调试的程序收到信号时，GDB不会告诉你收到信号的信息。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;pass<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;noignore<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;当被调试的程序收到信号时，GDB不处理信号。这表示，GDB会把这个信号交给被调试程序会处理。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;nopass<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ignore<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;当被调试的程序收到信号时，GDB不会让被调试程序来处理这个信号。<BR><BR>&nbsp; &nbsp; info signals<BR>&nbsp; &nbsp; info handle<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看有哪些信号在被GDB检测中。<BR>十、线程（Thread Stops）<BR>如果你程序是多线程的话，你可以定义你的断点是否在所有的线程上，或是在某个特定的线程。GDB很容易帮你完成这一工作。<BR><BR>&nbsp; &nbsp; break &lt;linespec&gt;; thread &lt;threadno&gt;;<BR>&nbsp; &nbsp; break &lt;linespec&gt;; thread &lt;threadno&gt;; if ...<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID，注意，这个ID是GDB分配的，你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定thread &lt;threadno&gt;;则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) break frik.c:13 thread 28 if bartab &gt;; lim<BR><BR>&nbsp; &nbsp; 当你的程序被GDB停住时，所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时，所有的线程也会被恢复运行。那怕是主进程在被单步调试时。<BR>查看栈信息<BR>当程序被停住了，你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数，函数的地址，函数参数，函数内的局部变量都会被压入“栈”（Stack）中。你可以用GDB命令来查看当前的栈中的信息。<BR><BR>下面是一些查看函数调用栈信息的GDB命令：<BR><BR>&nbsp; &nbsp; backtrace <BR>&nbsp; &nbsp; bt <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打印当前的函数调用栈的所有信息。如：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) bt<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;#0&nbsp;&nbsp;func (n=250) at tst.c:6<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;#1&nbsp;&nbsp;0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;#2&nbsp;&nbsp;0x400409ed in __libc_start_main () from /lib/libc.so.6<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;从上可以看出函数的调用栈信息：__libc_start_main --&gt;; main() --&gt;; func()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; backtrace &lt;n&gt;;<BR>&nbsp; &nbsp; bt &lt;n&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;n是一个正整数，表示只打印栈顶上n层的栈信息。<BR><BR>&nbsp; &nbsp; backtrace &lt;-n&gt;; <BR>&nbsp; &nbsp; bt &lt;-n&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;-n表一个负整数，表示只打印栈底下n层的栈信息。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>如果你要查看某一层的信息，你需要在切换当前的栈，一般来说，程序停止时，最顶层的栈就是当前栈，如果你要查看栈下面层的详细信息，首先要做的是切换当前栈。<BR><BR>&nbsp; &nbsp; frame &lt;n&gt;; <BR>&nbsp; &nbsp; f &lt;n&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;n是一个从0开始的整数，是栈中的层编号。比如：frame 0，表示栈顶，frame 1，表示栈的第二层。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; up &lt;n&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;表示向栈的上面移动n层，可以不打n，表示向上移动一层。 <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; down &lt;n&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;表示向栈的下面移动n层，可以不打n，表示向下移动一层。 <BR><BR>&nbsp; &nbsp; 上面的命令，都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;select-frame &lt;n&gt;; 对应于 frame 命令。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;up-silently &lt;n&gt;; 对应于 up 命令。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;down-silently &lt;n&gt;; 对应于 down 命令。<BR><BR>&nbsp; &nbsp; <BR>查看当前栈层的信息，你可以用以下GDB命令：<BR><BR>&nbsp; &nbsp; frame 或 f <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;会打印出这些信息：栈的层编号，当前的函数名，函数参数值，函数所在文件及行号，函数执行到的语句。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; info frame <BR>&nbsp; &nbsp; info f <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;这个命令会打印出更为详细的当前栈层的信息，只不过，大多数都是运行时的内内地址。比如：函数地址，调用函数的地址，被调用函数的地址，目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。如：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(gdb) info f<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Stack level 0, frame at 0xbffff5d4:<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; eip = 0x804845d in func (tst.c:6); saved eip 0x8048524<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; called by frame at 0xbffff60c<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; source language c.<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Arglist at 0xbffff5d4, args: n=250<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Locals at 0xbffff5d4, Previous frame's sp is 0x0<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Saved registers:<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ebp at 0xbffff5d4, eip at 0xbffff5d8<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp;&nbsp;info args<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打印出当前函数的参数名及其值。<BR>&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp;&nbsp;info locals<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打印出当前函数中所有局部变量及其值。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp;&nbsp;info catch<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打印出当前的函数中的异常处理信息。<BR>查看源程序<BR>一、显示源代码<BR>&nbsp; &nbsp; GDB 可以打印出所调试程序的源代码，当然，在程序编译时一定要加上-g的参数，把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后，GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。还是来看一看查看源代码的GDB命令吧。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; list &lt;linenum&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示程序第linenum行的周围的源程序。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; list &lt;function&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示函数名为function的函数的源程序。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; list <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示当前行后面的源程序。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; list - <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示当前行前面的源程序。<BR><BR>一般是打印当前行的上5行和下5行，如果显示函数是是上2行下8行，默认是10行，当然，你也可以定制显示的范围，使用下面命令可以设置一次显示源程序的行数。<BR><BR>&nbsp; &nbsp; set listsize &lt;count&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;设置一次显示源代码的行数。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; show listsize<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看当前listsize的设置。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR><BR>list命令还有下面的用法：<BR><BR>&nbsp; &nbsp; list &lt;first&gt;;, &lt;last&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示从first行到last行之间的源代码。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; list , &lt;last&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示从当前行到last行之间的源代码。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; list +<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;往后显示源代码。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR><BR>一般来说在list后面可以跟以下这们的参数：<BR><BR>&nbsp; &nbsp; &lt;linenum&gt;;&nbsp; &nbsp;行号。<BR>&nbsp; &nbsp; &lt;+offset&gt;;&nbsp; &nbsp;当前行号的正偏移量。<BR>&nbsp; &nbsp; &lt;-offset&gt;;&nbsp; &nbsp;当前行号的负偏移量。<BR>&nbsp; &nbsp; &lt;filename:linenum&gt;;&nbsp;&nbsp;哪个文件的哪一行。<BR>&nbsp; &nbsp; &lt;function&gt;;&nbsp;&nbsp;函数名。<BR>&nbsp; &nbsp; &lt;filename:function&gt;; 哪个文件中的哪个函数。<BR>&nbsp; &nbsp; &lt;*address&gt;;&nbsp;&nbsp;程序运行时的语句在内存中的地址。<BR>二、搜索源代码<BR>不仅如此，GDB还提供了源代码搜索的命令：<BR><BR>&nbsp; &nbsp; forward-search &lt;regexp&gt;; <BR>&nbsp; &nbsp; search &lt;regexp&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;向前面搜索。<BR><BR>&nbsp; &nbsp; reverse-search &lt;regexp&gt;; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;全部搜索。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>其中，&lt;regexp&gt;;就是正则表达式，也主一个字符串的匹配模式，关于正则表达式，我就不在这里讲了，还请各位查看相关资料。<BR>三、指定源文件的路径<BR>某些时候，用-g编译过后的执行程序中只是包括了源文件的名字，没有路径名。GDB提供了可以让你指定源文件的路径的命令，以便GDB进行搜索。<BR><BR>&nbsp; &nbsp; directory &lt;dirname ... &gt;;<BR>&nbsp; &nbsp; dir &lt;dirname ... &gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;加一个源文件路径到当前路径的前面。如果你要指定多个路径，UNIX下你可以使用“:”，Windows下你可以使用“;”。<BR>&nbsp; &nbsp; directory <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;清除所有的自定义的源文件搜索路径信息。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; show directories <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;显示定义了的源文件搜索路径。<BR><BR>四、源代码的内存<BR>你可以使用info line命令来查看源代码在内存中的地址。info line后面可以跟“行号”，“函数名”，“文件名:行号”，“文件名:函数名”，这个命令会打印出所指定的源码在运行时的内存地址，如：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) info line tst.c:func<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Line 5 of "tst.c" starts at address 0x8048456 &lt;func+6&gt;; and ends at 0x804845d &lt;func+13&gt;;.<BR><BR>还有一个命令（disassemble）你可以查看源程序的当前执行时的机器码，这个命令会把目前内存中的指令dump出来。如下面的示例表示查看函数func的汇编代码。<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) disassemble func<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Dump of assembler code for function func:<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048450 &lt;func&gt;;:&nbsp; &nbsp;&nbsp; &nbsp; push&nbsp; &nbsp;%ebp<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048451 &lt;func+1&gt;;:&nbsp; &nbsp;&nbsp;&nbsp;mov&nbsp; &nbsp; %esp,%ebp<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048453 &lt;func+3&gt;;:&nbsp; &nbsp;&nbsp;&nbsp;sub&nbsp; &nbsp; $0x18,%esp<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048456 &lt;func+6&gt;;:&nbsp; &nbsp;&nbsp;&nbsp;movl&nbsp; &nbsp;$0x0,0xfffffffc(%ebp)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804845d &lt;func+13&gt;;:&nbsp; &nbsp; movl&nbsp; &nbsp;$0x1,0xfffffff8(%ebp)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048464 &lt;func+20&gt;;:&nbsp; &nbsp; mov&nbsp; &nbsp; 0xfffffff8(%ebp),%eax<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048467 &lt;func+23&gt;;:&nbsp; &nbsp; cmp&nbsp; &nbsp; 0x8(%ebp),%eax<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804846a &lt;func+26&gt;;:&nbsp; &nbsp; jle&nbsp; &nbsp; 0x8048470 &lt;func+32&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804846c &lt;func+28&gt;;:&nbsp; &nbsp; jmp&nbsp; &nbsp; 0x8048480 &lt;func+48&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804846e &lt;func+30&gt;;:&nbsp; &nbsp; mov&nbsp; &nbsp; %esi,%esi<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048470 &lt;func+32&gt;;:&nbsp; &nbsp; mov&nbsp; &nbsp; 0xfffffff8(%ebp),%eax<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048473 &lt;func+35&gt;;:&nbsp; &nbsp; add&nbsp; &nbsp; %eax,0xfffffffc(%ebp)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048476 &lt;func+38&gt;;:&nbsp; &nbsp; incl&nbsp; &nbsp;0xfffffff8(%ebp)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048479 &lt;func+41&gt;;:&nbsp; &nbsp; jmp&nbsp; &nbsp; 0x8048464 &lt;func+20&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804847b &lt;func+43&gt;;:&nbsp; &nbsp; nop<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804847c &lt;func+44&gt;;:&nbsp; &nbsp; lea&nbsp; &nbsp; 0x0(%esi,1),%esi<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048480 &lt;func+48&gt;;:&nbsp; &nbsp; mov&nbsp; &nbsp; 0xfffffffc(%ebp),%edx<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048483 &lt;func+51&gt;;:&nbsp; &nbsp; mov&nbsp; &nbsp; %edx,%eax<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048485 &lt;func+53&gt;;:&nbsp; &nbsp; jmp&nbsp; &nbsp; 0x8048487 &lt;func+55&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048487 &lt;func+55&gt;;:&nbsp; &nbsp; mov&nbsp; &nbsp; %ebp,%esp<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x8048489 &lt;func+57&gt;;:&nbsp; &nbsp; pop&nbsp; &nbsp; %ebp<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;0x804848a &lt;func+58&gt;;:&nbsp; &nbsp; ret<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;End of assembler dump.<BR><BR><BR>查看运行时数据<BR>&nbsp; &nbsp; 在你调试程序时，当程序被停住时，你可以使用print命令（简写命令为p），或是同义命令inspect来查看当前程序的运行数据。print命令的格式是：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; print &lt;expr&gt;;<BR>&nbsp; &nbsp; print /&lt;f&gt;; &lt;expr&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;expr&gt;;是表达式，是你所调试的程序的语言的表达式（GDB可以调试多种编程语言），&lt;f&gt;;是输出的格式，比如，如果要把表达式按16进制的格式输出，那么就是/x。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>一、表达式<BR>&nbsp; &nbsp; print和许多GDB的命令一样，可以接受一个表达式，GDB会根据当前的程序运行的数据来计算这个表达式，既然是表达式，那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 表达式的语法应该是当前所调试的语言的语法，由于C/C++是一种大众型的语言，所以，本文中的例子都是关于C/C++的。（而关于用GDB调试其它语言的章节，我将在后面介绍）<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 在表达式中，有几种GDB所支持的操作符，它们可以用在任何一种语言中。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; @<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;是一个和数组有关的操作符，在后面会有更详细的说明。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; ::<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;指定一个在文件或是一个函数中的变量。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; {&lt;type&gt;;} &lt;addr&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;表示一个指向内存地址&lt;addr&gt;;的类型为type的一个对象。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>二、程序变量<BR>&nbsp; &nbsp; 在GDB中，你可以随时查看以下三种变量的值：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;1、全局变量（所有文件可见的）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;2、静态全局变量（当前文件可见的）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;3、局部变量（当前Scope可见的）<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; 如果你的局部变量和全局变量发生冲突（也就是重名），一般情况下是局部变量会隐藏全局变量，也就是说，如果一个全局变量和一个函数中的局部变量同名时，如果当前停止点在函数中，用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量的值时，你可以使用“::”操作符：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;file::variable<BR>&nbsp; &nbsp; function::variable<BR>&nbsp; &nbsp; 可以通过这种形式指定你所想查看的变量，是哪个文件中的或是哪个函数中的。例如，查看文件f2.c中的全局变量x的值：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; gdb) p 'f2.c'::x<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 当然，“::”操作符会和C++中的发生冲突，GDB能自动识别“::” 是否C++的操作符，所以你不必担心在调试C++程序时会出现异常。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 另外，需要注意的是，如果你的程序编译时开启了优化选项，那么在用GDB调试被优化过的程序时，可能会发生某些变量不能访问，或是取值错误码的情况。这个是很正常的，因为优化程序会删改你的程序，整理你程序的语句顺序，剔除一些无意义的变量等，所以在GDB调试这种程序时，运行时的指令和你所编写指令就有不一样，也就会出现你所想象不到的结果。对付这种情况时，需要在编译程序时关闭编译优化。一般来说，几乎所有的编译器都支持编译优化的开关，例如，GNU的C/C++编译器GCC，你可以使用“-gstabs”选项来解决这个问题。关于编译器的参数，还请查看编译器的使用说明文档。<BR>三、数组<BR>&nbsp; &nbsp; 有时候，你需要查看一段连续的内存空间的值。比如数组的一段，或是动态分配的数据的大小。你可以使用GDB的“@”操作符，“@”的左边是第一个内存的地址的值，“@”的右边则你你想查看内存的长度。例如，你的程序中有这样的语句：<BR>&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;int *array = (int *) malloc (len * sizeof (int));<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; 于是，在GDB调试过程中，你可以以如下命令显示出这个动态数组的取值：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;p *array@len<BR><BR>&nbsp; &nbsp; @的左边是数组的首地址的值，也就是变量array所指向的内容，右边则是数据的长度，其保存在变量len中，其输出结果，大约是下面这个样子的：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p *array@len<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}<BR><BR>&nbsp; &nbsp; 如果是静态数组的话，可以直接用print数组名，就可以显示数组中所有数据的内容了。<BR><BR>&nbsp; &nbsp; <BR>四、输出格式<BR>&nbsp; &nbsp; 一般来说，GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如，你想输出一个整数的十六进制，或是二进制来查看这个整型变量的中的位的情况。要做到这样，你可以使用GDB的数据显示格式：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; x&nbsp;&nbsp;按十六进制格式显示变量。<BR>&nbsp; &nbsp; d&nbsp;&nbsp;按十进制格式显示变量。<BR>&nbsp; &nbsp; u&nbsp;&nbsp;按十六进制格式显示无符号整型。<BR>&nbsp; &nbsp; o&nbsp;&nbsp;按八进制格式显示变量。<BR>&nbsp; &nbsp; t&nbsp;&nbsp;按二进制格式显示变量。 <BR>&nbsp; &nbsp; a&nbsp;&nbsp;按十六进制格式显示变量。<BR>&nbsp; &nbsp; c&nbsp;&nbsp;按字符格式显示变量。<BR>&nbsp; &nbsp; f&nbsp;&nbsp;按浮点数格式显示变量。<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p i<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$21 = 101&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p/a i<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$22 = 0x65<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p/c i<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$23 = 101 'e'<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p/f i<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$24 = 1.41531145e-43<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p/x i<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$25 = 0x65<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p/t i<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$26 = 1100101<BR>五、查看内存<BR>&nbsp; &nbsp; 你可以使用examine命令（简写是x）来查看内存地址中的值。x命令的语法如下所示：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; x/&lt;n/f/u&gt;; &lt;addr&gt;; <BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; n、f、u是可选的参数。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; n 是一个正整数，表示显示内存的长度，也就是说从当前地址向后显示几个地址的内容。<BR>&nbsp; &nbsp; f 表示显示的格式，参见上面。如果地址所指的是字符串，那么格式可以是s，如果地十是指令地址，那么格式可以是i。<BR>&nbsp; &nbsp; u 表示从当前地址往后请求的字节数，如果不指定的话，GDB默认是4个bytes。u参数可以用下面的字符来代替，b表示单字节，h表示双字节，w表示四字节，g表示八字节。当我们指定了字节长度后，GDB会从指内存定的内存地址开始，读写指定字节，并把其当作一个值取出来。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; &lt;addr&gt;;表示一个内存地址。<BR><BR>&nbsp; &nbsp; n/f/u三个参数可以一起使用。例如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 命令：x/3uh 0x54320 表示，从内存地址0x54320读取内容，h表示以双字节为一个单位，3表示三个单位，u表示按十六进制显示。<BR>六、自动显示<BR>&nbsp; &nbsp; 你可以设置一些自动显示的变量，当程序停住时，或是在你单步跟踪时，这些变量会自动显示。相关的GDB命令是display。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; display &lt;expr&gt;; <BR>&nbsp; &nbsp; display/&lt;fmt&gt;; &lt;expr&gt;; <BR>&nbsp; &nbsp; display/&lt;fmt&gt;; &lt;addr&gt;;<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; expr是一个表达式，fmt表示显示的格式，addr表示内存地址，当你用display设定好了一个或多个表达式后，只要你的程序被停下来，GDB会自动显示你所设置的这些表达式的值。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 格式i和s同样被display支持，一个非常有用的命令是：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;display/i $pc<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; $pc是GDB的环境变量，表示着指令的地址，/i则表示输出格式为机器指令码，也就是汇编。于是当程序停下后，就会出现源代码和机器指令码相对应的情形，这是一个很有意思的功能。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 下面是一些和display相关的GDB命令：<BR>&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; undisplay &lt;dnums...&gt;;<BR>&nbsp; &nbsp; delete display &lt;dnums...&gt;;<BR>&nbsp; &nbsp; 删除自动显示，dnums意为所设置好了的自动显式的编号。如果要同时删除几个，编号可以用空格分隔，如果要删除一个范围内的编号，可以用减号表示（如：2-5）<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; disable display &lt;dnums...&gt;;<BR>&nbsp; &nbsp; enable display &lt;dnums...&gt;;<BR>&nbsp; &nbsp; disable和enalbe不删除自动显示的设置，而只是让其失效和恢复。<BR>&nbsp; &nbsp; info display<BR>&nbsp; &nbsp; 查看display设置的自动显示的信息。GDB会打出一张表格，向你报告当然调试中设置了多少个自动显示设置，其中包括，设置的编号，表达式，是否enable。<BR>七、设置显示选项<BR>&nbsp; &nbsp; GDB中关于显示的选项比较多，这里我只例举大多数常用的选项。<BR><BR>&nbsp; &nbsp; set print address <BR>&nbsp; &nbsp; set print address on <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打开地址输出，当程序显示函数信息时，GDB会显出函数的参数地址。系统默认为打开的，如：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) f<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;#0&nbsp;&nbsp;set_quotes (lq=0x34c78 "&lt;&lt;", rq=0x34c88 "&gt;;&gt;;")<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;at input.c:530<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;530&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (lquote != def_lquote)<BR><BR>&nbsp; &nbsp; set print address off <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;关闭函数的参数地址显示，如：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) set print addr off<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) f<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;#0&nbsp;&nbsp;set_quotes (lq="&lt;&lt;", rq="&gt;;&gt;;") at input.c:530<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;530&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (lquote != def_lquote)<BR><BR>&nbsp; &nbsp; show print address <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看当前地址显示选项是否打开。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print array <BR>&nbsp; &nbsp; set print array on <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;打开数组显示，打开后当数组显示时，每个元素占一行，如果不打开的话，每个元素则以逗号分隔。这个选项默认是关闭的。与之相关的两个命令如下，我就不再多说了。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print array off <BR>&nbsp; &nbsp; show print array <BR><BR>&nbsp; &nbsp; set print elements &lt;number-of-elements&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;这个选项主要是设置数组的，如果你的数组太大了，那么就可以指定一个&lt;number-of-elements&gt;;来指定数据显示的最大长度，当到达这个长度时，GDB就不再往下显示了。如果设置为0，则表示不限制。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; show print elements <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看print elements的选项信息。<BR>&nbsp; &nbsp; set print null-stop &lt;on/off&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;如果打开了这个选项，那么当显示字符串时，遇到结束符则停止显示。这个选项默认为off。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print pretty on <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;如果打开printf pretty这个选项，那么当GDB显示结构体时会比较漂亮。如：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;$1 = {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;next = 0x0,<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;flags = {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; sweet = 1,<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; sour = 1<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;},<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;meat = 0x54 "Pork"<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR><BR>&nbsp; &nbsp; set print pretty off<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;关闭printf pretty这个选项，GDB显示结构体时会如下显示：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;$1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<BR>&nbsp; &nbsp; show print pretty <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看GDB是如何显示结构体的。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; set print sevenbit-strings &lt;on/off&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;设置字符显示，是否按“\nnn”的格式显示，如果打开，则字符串或字符数据按\nnn显示，如“\065”。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; show print sevenbit-strings<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看字符显示开关是否打开。 <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print union &lt;on/off&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;设置显示结构体时，是否显式其内的联合体数据。例如有以下数据结构：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;typedef enum {Tree, Bug} Species;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;typedef enum {Big_tree, Acorn, Seedling} Tree_forms;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;typedef enum {Caterpillar, Cocoon, Butterfly}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Bug_forms;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;struct thing {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Species it;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; union {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Tree_forms tree;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Bug_forms bug;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; } form;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;};<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;struct thing foo = {Tree, {Acorn}};<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当打开这个开关时，执行 p foo 命令后，会如下显示：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;$1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当关闭这个开关时，执行 p foo 命令后，会如下显示：<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;$1 = {it = Tree, form = {...}}<BR><BR>&nbsp; &nbsp; show print union<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看联合体数据的显示方式<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print object &lt;on/off&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;在C++中，如果一个对象指针指向其派生类，如果打开这个选项，GDB会自动按照虚方法调用的规则显示输出，如果关闭这个选项的话，GDB就不管虚函数表了。这个选项默认是off。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; show print object<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看对象选项的设置。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print static-members &lt;on/off&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;这个选项表示，当显示一个C++对象中的内容是，是否显示其中的静态数据成员。默认是on。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; show print static-members<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看静态数据成员选项设置。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; set print vtbl &lt;on/off&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;当此选项打开时，GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; show print vtbl<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看虚函数显示格式的选项。<BR>八、历史记录<BR>&nbsp; &nbsp; 当你用GDB的print查看程序运行时的数据时，你每一个print都会被GDB记录下来。GDB会以$1, $2, $3 .....这样的方式为你每一个print命令编上号。于是，你可以使用这个编号访问以前的表达式，如$1。这个功能所带来的好处是，如果你先前输入了一个比较长的表达式，如果你还想查看这个表达式的值，你可以使用历史记录来访问，省去了重复输入。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; <BR>九、GDB环境变量<BR>&nbsp; &nbsp; 你可以在GDB的调试环境中定义自己的变量，用来保存一些调试程序中的运行数据。要定义一个GDB的变量很简单只需。使用GDB的set命令。GDB的环境变量和UNIX一样，也是以$起头。如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; set $foo = *object_ptr<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 使用环境变量时，GDB会在你第一次使用时创建这个变量，而在以后的使用中，则直接对其賦值。环境变量没有类型，你可以给环境变量定义任一的类型。包括结构体和数组。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; show convenience <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;该命令查看当前所设置的所有的环境变量。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; 这是一个比较强大的功能，环境变量和程序变量的交互使用，将使得程序调试更为灵活便捷。例如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;set $i = 0<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;print bar[$i++]-&gt;;contents<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 于是，当你就不必，print bar[0]-&gt;;contents, print bar[1]-&gt;;contents地输入命令了。输入这样的命令后，只用敲回车，重复执行上一条语句，环境变量会自动累加，从而完成逐个输出的功能。<BR>十、查看寄存器<BR>&nbsp; &nbsp; 要查看寄存器的值，很简单，可以使用如下命令：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; info registers <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看寄存器的情况。（除了浮点寄存器）<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; info all-registers<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看所有寄存器的情况。（包括浮点寄存器）<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; info registers &lt;regname ...&gt;;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;查看所指定的寄存器的情况。<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; 寄存器中放置了程序运行时的数据，比如程序当前运行的指令地址（ip），程序的当前堆栈地址（sp）等等。你同样可以使用print命令来访问寄存器的情况，只需要在寄存器名字前加一个$符号就可以了。如：p $eip。<BR>&nbsp; &nbsp; <BR><BR><BR>改变程序的执行<BR>&nbsp; &nbsp; 一旦使用GDB挂上被调试程序，当程序运行起来后，你可以根据自己的调试思路来动态地在GDB中更改当前被调试程序的运行线路或是其变量的值，这个强大的功能能够让你更好的调试你的程序，比如，你可以在程序的一次运行中走遍程序的所有分支。<BR>一、修改变量值<BR>&nbsp; &nbsp; 修改被调试程序运行时的变量值，在GDB中很容易实现，使用GDB的print命令即可完成。如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) print x=4<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; x=4这个表达式是C/C++的语法，意为把变量x的值修改为4，如果你当前调试的语言是Pascal，那么你可以使用Pascal的语法：x:=4。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; 在某些时候，很有可能你的变量和GDB中的参数冲突，如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) whatis width<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;type = double<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) p width<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;$4 = 13<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) set width=47<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Invalid syntax in expression.<BR><BR>&nbsp; &nbsp; 因为，set width是GDB的命令，所以，出现了“Invalid syntax in expression”的设置错误，此时，你可以使用set var命令来告诉GDB，width不是你GDB的参数，而是程序的变量名，如：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(gdb) set var width=47<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<BR>&nbsp; &nbsp; 另外，还可能有些情况，GDB并不报告这种错误，所以保险起见，在你改变程序变量取值时，最好都使用set var格式的GDB命令。<BR>二、跳转执行<BR>&nbsp; &nbsp; 一般来说，被调试程序会按照程序代码的运行顺序依次执行。GDB提供了乱序执行的功能，也就是说，GDB可以修改程序的执行顺序，可以让程序执行随意跳跃。这个功能可以由GDB的jump命令来完：<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; jump &lt;linespec&gt;;<BR>&nbsp; &nbsp; 指定下一条语句的运行点。&lt;linespce&gt;;可以是文件的行号，可以是file:line格式，可以是+num这种偏移量格式。表式着下一条运行语句从哪里开始。<BR>&nbsp; &nbsp; <BR>&nbsp; &nbsp; jump &lt;address&gt;;<BR>&nbsp; &nbsp; 这里的&lt;address&gt;;是]]></description>
</item><item>
<title><![CDATA[lsof 快速起步]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=FoxWolf&amp;id=35463</link>
<author>FoxWolf</author>
<pubDate>2008/5/19 11:01:56</pubDate>
<description><![CDATA[<STRONG>lsof 快速起步</STRONG>
<DIV><STRONG>lsof 工具介绍</STRONG><BR>作者： etony<BR>来自： Linuxsir.org<BR>提要： lsof（lsof的全称是list open files），此工具可以用来查看正在运行中的进程打开了哪些文件、目录和套接字；是系统监测工具之一。在服务器管理中，我们还是常用到这个工具的。如果有需要的弟兄，建议还是看看。最少也知道lsof是做什么用的。<BR></DIV>
<DIV><STRONG>关于本文:</STRONG></DIV>
<DIV>本文是etony兄所写，lsof工具可以说是系统监测工具之一。在我看来这个工具还是比较重要的，虽然本文很短，但入门足够。此文权当抛砖引玉吧； 我们倒是希望看到一个更权威的lsof工具说明书。当然最好是有实例的，而不是简单的翻译MAN； —— 北南南北 at 2006/08/17 </DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp;</DIV>
<DIV>Abstract:<BR>通过大量的示例介绍使用lsof的方法. <BR>&nbsp;</DIV>
<DIV><STRONG>Contents <BR>查看对某个文件的使用情况 <BR>查看对文件系统的使用 <BR>查找打开,但是不能连接的文件 </STRONG></DIV>
<DIV><STRONG>无法卸载 <BR>查看监听socket <BR>查看某个网络连接 <BR>识别 Netstat 连接 <BR>查找针对某个命令打开的文件 <BR>查看某个用户的操作 <BR>更多信息 <BR>Bibliography</STRONG> </DIV>
<DIV>&nbsp;</DIV>
<DIV><BR><STRONG>查看对某个文件的使用情况</STRONG> <BR>查看哪些进程对某个文件进行了调用: </DIV>
<DIV>$ lsof /etc/passwd 1 </DIV>
<DIV><BR><STRONG>查看对文件系统的使用</STRONG> <BR>/tmp目录被垃圾文件塞满了, 但是, 用ls 又看不到太大文件, 谁干的? </DIV>
<DIV>$ lsof /tmp </DIV>
<DIV><BR><STRONG>查找打开,但是不能连接的文件</STRONG> <BR>一个进程打开一个文件, 然后将其设为 unlinked 状态, 则此文件资源仍能被进程使用, 但是其访问路径已经被删除了. 因此, 使用ls不能将其列出. 只有当进程结束时, 才能释放文件占用的资源 </DIV>
<DIV>查找unlinked 文件, 选项 +L, 作用: 列出打开文件的连接数 </DIV>
<DIV>$lsof +L </DIV>
<DIV>指定连接数的上限 $lsof +L1 </DIV>
<DIV>同时指定文件系统, 则需要使用 -a(AND) 选项 </DIV>
<DIV>$ lsof -a +L1 /home </DIV>
<DIV><BR><STRONG>无法卸载 <BR></STRONG>查看谁令mount的分区无法卸载 </DIV>
<DIV>$ lsof &lt;file_system_name&gt; </DIV>
<DIV><BR><STRONG>查看监听socket</STRONG> <BR>查看网络服务 <BR>$ lsof -i </DIV>
<DIV><BR><STRONG>查看某个网络连接 <BR></STRONG>$ lsof <A href="mailto:-i@aaa.bbb.ccc">-i@aaa.bbb.ccc</A> </DIV>
<DIV>$ lsof <A href="mailto:-iTCP@aaa.bbb.ccc:ftp-data">-iTCP@aaa.bbb.ccc:ftp-data</A> *指定协议* </DIV>
<DIV>$ lsof -i4 *指定IP版本* </DIV>
<DIV>$ lsof -i6 </DIV>
<DIV><BR><STRONG>识别 Netstat 连接</STRONG> <BR>例如: netstat -p -t -n 的输出为: <BR>Proto Recv-Q Send-Q Local Address Foreign Address State </DIV>
<DIV>tcp 0 0 218.56.203.246:52634 202.109.72.72:7000 ESTABLISHED </DIV>
<DIV><BR>则可以: <A href="mailto:tony@tony:~$">tony@tony:~$</A> lsof <A href="mailto:-iTCP@202.109.72.72:7000">-iTCP@202.109.72.72:7000</A> </DIV>
<DIV>COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME </DIV>
<DIV>xchat 4505 tony 12u IPv4 5775 TCP 218.56.203.246:52634-&gt;202.109.72.72:afs3-fileserver (ESTABLISHED) </DIV>
<DIV><BR><STRONG>查找针对某个命令打开的文件</STRONG> <BR>使用命令的PID </DIV>
<DIV>$ lsof -p &lt;PID&gt; </DIV>
<DIV>使用命令名称 </DIV>
<DIV>$ lsof -c &lt;first_characters_of_command_name_that_interest_you&gt; </DIV>
<DIV>$ lsof -c sendmail </DIV>
<DIV>查看谁在使用设备文件 </DIV>
<DIV>$ lsof /dev/hda6 </DIV>
<DIV><STRONG>查看某个用户的操作</STRONG> <BR>$ id -u tony </DIV>
<DIV>1000 </DIV>
<DIV><BR>$ lsof -u1000 or $ lsof -utony </DIV>
<DIV><BR>$ lsof -u^tony * 则是取反的意思* </DIV>
<DIV><BR><STRONG>更多信息</STRONG> <BR>更多信息参阅 lsof 的联机手册losf(1). </DIV>
<DIV><STRONG>Bibliography </STRONG></DIV>
<DIV><STRONG></STRONG>&nbsp;</DIV>
<DIV><STRONG>附：</STRONG>Lsof命令详解 </DIV>
<DIV>&nbsp;</DIV>
<DIV><SPAN class=a14c id=zoom>&nbsp;
<P style="TEXT-INDENT: 2em">一般root用户才能执行lsof命令，普通用户可以看见/usr/sbin/lsof命令，但是普通用户执行会显示“permission denied” 
<P style="TEXT-INDENT: 2em">我总结一下lsof指令的用法： 
<P style="TEXT-INDENT: 2em">lsof abc.txt 显示开启文件abc.txt的进程 
<P style="TEXT-INDENT: 2em">lsof -i :22 知道22端口现在运行什么程序 
<P style="TEXT-INDENT: 2em">lsof -c abc 显示abc进程现在打开的文件 
<P style="TEXT-INDENT: 2em">lsof -g gid 显示归属gid的进程情况 
<P style="TEXT-INDENT: 2em">lsof +d /usr/local/ 显示目录下被进程开启的文件 
<P style="TEXT-INDENT: 2em">lsof +D /usr/local/ 同上，但是会搜索目录下的目录，时间较长 
<P style="TEXT-INDENT: 2em">lsof -d 4 显示使用fd为4的进程 
<P style="TEXT-INDENT: 2em">lsof -i 用以显示符合条件的进程情况 
<P style="TEXT-INDENT: 2em">语法: lsof -i[46] [protocol][@hostname|hostaddr][:service|port] 
<P style="TEXT-INDENT: 2em">46 --&gt; IPv4 or IPv6 
<P style="TEXT-INDENT: 2em">protocol --&gt; TCP or UDP 
<P style="TEXT-INDENT: 2em">hostname --&gt; Internet host name 
<P style="TEXT-INDENT: 2em">hostaddr --&gt; IPv4位置 
<P style="TEXT-INDENT: 2em">service --&gt; /etc/service中的 service name (可以不只一个) 
<P style="TEXT-INDENT: 2em">port --&gt; 端口号 (可以不只一个) 
<P style="TEXT-INDENT: 2em">例子: TCP:25 - TCP and port 25 
<P style="TEXT-INDENT: 2em">@1.2.3.4 - Internet IPv4 host address 1.2.3.4 
<P style="TEXT-INDENT: 2em">tcp@ohaha.ks.edu.tw:ftp - TCP protocol hosthaha.ks.edu.tw service name:ftp 
<P style="TEXT-INDENT: 2em">lsof -n 不将IP转换为hostname，缺省是不加上-n参数 
<P style="TEXT-INDENT: 2em">例子: lsof -i tcp@ohaha.ks.edu.tw:ftp -n 
<P style="TEXT-INDENT: 2em">lsof -p 12 看进程号为12的进程打开了哪些文件 
<P style="TEXT-INDENT: 2em">lsof +|-r [t] 控制lsof不断重复执行，缺省是15s刷新 
<P style="TEXT-INDENT: 2em">-r，lsof会永远不断的执行，直到收到中断信号 
<P style="TEXT-INDENT: 2em">+r，lsof会一直执行，直到没有档案被显示 
<P style="TEXT-INDENT: 2em">例子：不断查看目前ftp连接的情况：lsof -i tcp@ohaha.ks.edu.tw:ftp -r 
<P style="TEXT-INDENT: 2em">lsof -s 列出打开文件的大小，如果没有大小，则留下空白 
<P style="TEXT-INDENT: 2em">lsof -u username 以UID，列出打开的文件 
<P style="TEXT-INDENT: 2em">
<P style="TEXT-INDENT: 2em">一般root用户才能执行lsof命令，普通用户可以看见/usr/sbin/lsof命令，但是普通用户执行会显示“permission denied” 
<P style="TEXT-INDENT: 2em">我总结一下lsof指令的用法： 
<P style="TEXT-INDENT: 2em">lsof abc.txt 显示开启文件abc.txt的进程 
<P style="TEXT-INDENT: 2em">lsof -i :22 知道22端口现在运行什么程序 
<P style="TEXT-INDENT: 2em">lsof -c abc 显示abc进程现在打开的文件 
<P style="TEXT-INDENT: 2em">lsof -g gid 显示归属gid的进程情况 
<P style="TEXT-INDENT: 2em">lsof +d /usr/local/ 显示目录下被进程开启的文件 
<P style="TEXT-INDENT: 2em">lsof +D /usr/local/ 同上，但是会搜索目录下的目录，时间较长 
<P style="TEXT-INDENT: 2em">lsof -d 4 显示使用fd为4的进程 
<P style="TEXT-INDENT: 2em">lsof -i 用以显示符合条件的进程情况 
<P style="TEXT-INDENT: 2em">语法: lsof -i[46] [protocol][@hostname|hostaddr][:service|port] 
<P style="TEXT-INDENT: 2em">46 --&gt; IPv4 or IPv6 
<P style="TEXT-INDENT: 2em">protocol --&gt; TCP or UDP 
<P style="TEXT-INDENT: 2em">hostname --&gt; Internet host name 
<P style="TEXT-INDENT: 2em">hostaddr --&gt; IPv4位置 
<P style="TEXT-INDENT: 2em">service --&gt; /etc/service中的 service name (可以不只一个) 
<P style="TEXT-INDENT: 2em">port --&gt; 端口号 (可以不只一个) 
<P style="TEXT-INDENT: 2em">例子: TCP:25 - TCP and port 25 
<P style="TEXT-INDENT: 2em">@1.2.3.4 - Internet IPv4 host address 1.2.3.4 
<P style="TEXT-INDENT: 2em">tcp@ohaha.ks.edu.tw:ftp - TCP protocol hosthaha.ks.edu.tw service name:ftp 
<P style="TEXT-INDENT: 2em">lsof -n 不将IP转换为hostname，缺省是不加上-n参数 
<P style="TEXT-INDENT: 2em">例子: lsof -i tcp@ohaha.ks.edu.tw:ftp -n 
<P style="TEXT-INDENT: 2em">lsof -p 12 看进程号为12的进程打开了哪些文件 
<P style="TEXT-INDENT: 2em">lsof +|-r [t] 控制lsof不断重复执行，缺省是15s刷新 
<P style="TEXT-INDENT: 2em">-r，lsof会永远不断的执行，直到收到中断信号 
<P style="TEXT-INDENT: 2em">+r，lsof会一直执行，直到没有档案被显示 
<P style="TEXT-INDENT: 2em">
<P style="TEXT-INDENT: 2em">例子：不断查看目前ftp连接的情况：lsof -i tcp@ohaha.ks.edu.tw:ftp -r 
<P style="TEXT-INDENT: 2em">lsof -s 列出打开文件的大小，如果没有大小，则留下空白 
<P style="TEXT-INDENT: 2em">lsof -u username 以UID，列出打开的文件 
<P style="TEXT-INDENT: 2em">
<P style="TEXT-INDENT: 2em">
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>[root@tcx160 FILES]# lsof -i tcp:22
COMMAND   PID USER   FD   TYPE DEVICE SIZE NODE NAME
sshd     3261 root    3u  IPv6   7301       TCP *:ssh (LISTEN)
sshd    19692 root    3u  IPv6 348642       TCP 9.186.96.160:ssh-&gt;9.186.96.117:32914 (ESTABLISHED)</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CENTER></SPAN></DIV>]]></description>
</item>
</channel>
</rss>