Regroupement par :
Date
Catégorie
24/10/2008
仅以简单的日程纪念那些敬业的程序员们
纪念那些日以继夜工作在第一线上的工程师们
现在是加拿大时间早上五点,有一个说法说,如果你在早上看到一个程序员,那肯定是他一夜没睡,而不是早起。而我,看见了一群.
2008年4月 白天 加拿大 渥太华 CDMA实验室. 测试中几个电话掉线,忽略.
2008年6月 白天 美国 加州小镇 CDMA 开局测试. 风和日丽
2008年8月10日 白天 美国 加州小镇 上百人电话间隔性同时掉线.
2008年8月10日 白天 罗马尼亚 GNPS.
2008年8月10日 白天 美国 Richardson 北电研发中心,终端调试会议
2008年8月11日 凌晨 中国 广东 广东北电/Richardson/罗马尼亚 会议
2008年8月12日 凌晨 中国 北京 广东北电/Richardson/罗马尼亚/北京(86+Base) 会议
2008年8月20日 凌晨 中国 北京 广东北电/Richardson/罗马尼亚/北京(86+ARP) 会议
2008年9月 深夜 中国 北京 加州/广东北电/Richardson/罗马尼亚/北京(86+ARP) 调试
2008年9月中旬 凌晨 86:确定. / CDMA:我们已经没有耐心了! CC Kalli
2008年9月20日 凌晨 MSS:确认互联问题. / CDMA:接受!
2008年9月26日 深夜 中国 北京 麦当劳,<画皮>开始前一个半小时.紧急支持电话
2008年9月28日 白天 中国 北京 加班安排
2008年10月国庆 白天 中国 北京 加班
2008年10月中旬 白天 中国 北京 最终解决方案
2008年10月中旬 深夜 加拿大 渥太华 测试完成
2008年10月下旬 白天 美国 加州小镇 CDMA 测试. 风和日丽
13/11/2006
Yahoo面经
周日早上北航Yahoo笔试,考的东西比较多,50道选择题,40题技术的,10题OQ,还有两道编程题,还比较容易. 晚上的时候就通知周一面试,感觉Yahoo的效率还是挺高的^_^
周一下午独自去雅虎中国总部,位置不错,坐地铁道大望路,A口出来,往北300米温特来中心18楼.
直接到公司门口,填了一张简历表,找了一张访客的标签(cs找了一张最大的,fq找了一张最小的,我找的一张不大不小^_^)就和HR JJ进去了.直接被领到一个30左右的面试管面前,就在他们的工作格子里面试的,气氛还行,面试管应该是坐技术的,一边和我聊,一边还在上msn,时不时有美女的头像在闪.
做技术的交流还是比较容易的,上来就是写个链表转置的题目,我用了两个辅助空间,貌似一个是不够的.
接着就顺着我的简历开始发问,C(嵌入式,Voliate,算法+数据,指针(越界,const)) C++(面向对象,static,虚函数,菱形继承,编译器行为),Java(接口与多重继承) 设计模式(单例,简单工厂,抽象工厂),代码重构(好处,思想,对比),数据库(oracle ,Sqlserver mysql 对比,内容对象组织,sql语句(取最大值记录,取靠前的记录)),网络(字节序,int写入buf,select,fd_set),操作系统(Win Linux对比,Linux文件查找操作,grep,find),开发环境(喜欢的IDE,why),中间还拿出几道题关于函数返回指针的,让我指错,有要求写了一个函数归并排序两个递增数列,最少的循环找到相同的数字.(都是简历惹得祸,面试官都是看着简历问得,写的多问得多,错的也多,有几个哥们也就问了几个问题就over了) balabalabala了一个多小时,终于面试完了,除了linux操作不熟悉以外,大部分都被我解决了,整体感觉良好,只是有点口干舌燥的,肚子也开始饿了.
然后取会议室休息,抽空我喝了点水,靠着18楼的窗户休息了一会儿,又大摇大摆的在18楼逛了一圈(其实是在找WC),回来和一起在等待的北邮,北航的几个男生聊了一会儿,似乎没有和我面一样职位的,无聊的时候又看了看Yahoo的杂志<阿里人>,还有阿里巴巴的宣传片(三国群英传系列的,马云的最爱吧).
似乎今天面试的人比较多,面试官都安排不过来了,大家都在等... 一会儿 fq来了,接着被叫出去了,然后又进来了,然后又被叫出去了^_^ 来了一个老外,霸王笔的,说自己很喜欢Yahoo的音乐搜索(好像是是这样的吧,cs?) 然后 cs来了,一起等待中,一起挨饿中 差不多等了45分钟后,终于轮到我了,跟着hr jj到了17层,去和经理聊.
二面的考官也是比较年轻,气氛比较轻松,小间的办公室里,提问的都是OQ了,比如我从那里来,我到哪里去,为什么选择Yahoo,有什么爱好,遇到什么挫折,什么事情最有成就感,有什么缺点,写代码最多的失误在哪里,都怎么改近,对写代码提点具体的意见,是否碰见过项目中大家意见不一样的情况,都怎么解决 这些问题也看过一些,不过自己没有好好准备好,都是临时想的,比较被动,没有办法只好硬着头皮上了,感觉考官不是很满意的样子,不管它了,毕竟咱们和考官是平等地位的^_^
接着就是让我提问了,这个环节偶比较满意,提了一个很有深度的问题^_^关于Yahoo的运营模式 技术人员的发展相关的.
balabalabala差不多40分钟,就客气的和二面考官byebye了,Yahoo面试终了,两个星期内等消息...
走出温特莱中心的大门,外边都已经天黑了,北京的夜有点冷,赶紧钻进地铁回学校了.
总结: 找工作以来比较正式的一次面试(不像德信无线等),气氛比较宽松,和Yahoo的工作气氛一样,不过Yahoo已经转型,它慢慢的淡出技术在运营中的因素,不像google和baidu用技术引导潮流.我一直都是写C/C++的,喜欢做的是底层,做过的最多也是一些C/S的,没有写过网页,没有做过B/S,没有审美观(zzy言).整体感觉yahoo和我的方向有些出入.忘记了自己为什么要投,谈完之后有点不知道去Yahoo干什么好了.
to be or not to be ,it is a queston.
20/10/2006
#include <iostream> #include <string> #include <Windows.h> #include <Excpt.h> using namespace std; //============================================================================== // 功能描述: 程序启动,先clone一份做工作进程,原来的做监控进程,当工作程序歇菜后,监控进程再 // 起一个工作进程 // //============================================================================== //============================================================================== // 类 属: // 函数名称: Moniter Process CopyRight // 参 数: void // 返回类型: void // 异常类型: // 功能描述: 显示版权信息等 // // 全局变量: // 调用模块: // 备 注: // // 创 建 人: C.H.Long // 创建日期: 2004/06/05 //============================================================================== void MoniterProcessCopyRight() { SYSTEMTIME st; TCHAR szBuild[128]; { GetLocalTime(&st); wsprintf ( szBuild, "%04d-%02d-%02d", st.wYear, st.wMonth, st.wDay ); } string strText = "***************************************************************\n"; strText += "=============> This is Moniter Process ! <=============\n"; strText += "***************************************************************\n"; cout << "Moniter oneself Sample Program" << " (Built on " << szBuild << " " << " )" << endl << " Copyright(c) AllAboutProgram Shanghai Inc. All Rights Reserved." << endl << endl << strText << endl; } //============================================================================== // 类 属: // 函数名称: Work Process CopyRight // 参 数: void // 返回类型: void // 异常类型: // 功能描述: 显示版权信息等 // // 全局变量: // 调用模块: // 备 注: // // 创 建 人: C.H.Long // 创建日期: 2004/06/05 //============================================================================== void WorkProcessCopyRight() { SYSTEMTIME st; TCHAR szBuild[128]; { GetLocalTime(&st); wsprintf ( szBuild, "%04d-%02d-%02d", st.wYear, st.wMonth, st.wDay ); } string strText = "***************************************************************\n"; strText += "===============> This is Work Process ! <==============\n"; strText += "***************************************************************\n"; cout << "Moniter oneself Sample Program" << " (Built on " << szBuild << " " << " )" << endl << " Copyright(c) AllAboutProgram Shanghai Inc. All Rights Reserved." << endl << endl << strText << endl; } //============================================================================== // 类 属: // 函数名称: Moniter // 参 数: [in] : strCommandLine , 命令行参数 // [out]: PROCESS_INFORMATION , 进程信息 // 返回类型: int // 异常类型: // 功能描述: 监视程序自己的运行状态,如果停止,自动重启 // // 全局变量: // 调用模块: // 备 注: // // 创 建 人: C.H.Long // 创建日期: 2004/06/05 //============================================================================== BOOL Moniter(const string strCommandLine, PROCESS_INFORMATION& pi) { STARTUPINFO si = {sizeof(STARTUPINFO)}; GetStartupInfo(&si); BOOL bRes = CreateProcess( NULL, (LPSTR)&strCommandLine[0], NULL, NULL, TRUE, CREATE_NEW_CONSOLE,//0, 如果不想打开新的Console窗口,将此参数改为0 NULL, NULL, &si, &pi); DWORD dwRes = WaitForSingleObject( pi.hProcess,INFINITE ); return bRes; } //============================================================================== // 类 属: // 函数名称: Moniter // 参 数: [in] : strCommandLine , 命令行参数 // 返回类型: int // 异常类型: // 功能描述: Sample function // // 全局变量: // 调用模块: // 备 注: // // 创 建 人: C.H.Long // 创建日期: 2004/06/05 //============================================================================== void ShowSample() { cout << "I'm Work Process, Hello AllAboutProgram!" << endl; Sleep(1000); } //============================================================================== // 类 属: // 函数名称: Moniter // 参 数: 无 // 返回类型: int // 异常类型: // 功能描述: main : 程序非参数入口 // // 全局变量: // 调用模块: // 备 注: 这里可以作为程序工作进程的入口点 // // 创 建 人: C.H.Long // 创建日期: 2004/06/05 //============================================================================== int _tmain_1 () { // ... Add your code .......... ShowSample(); Sleep(2000); return 0; } int main ( int argc, char* argv[] ) { DWORD dwExitCode = 0; if(argc <= 1) // 监控进程 { MoniterProcessCopyRight(); for(;;) { string strCommondLine = argv[0]; strCommondLine += " "; strCommondLine += "work.txt"; // 无实际用处的命令行参数,只为建立新进程时能跳到工作进程的接口 cout << "=============== < Work Process will Startup > =============" << endl; PROCESS_INFORMATION process_information; BOOL bRes = Moniter(strCommondLine,process_information); CloseHandle(process_information.hThread); GetExitCodeProcess(process_information.hProcess,&dwExitCode); CloseHandle(process_information.hProcess); cout << "=============== < Work Process have Stutdown > =============" << endl; Sleep(1000); } } else // Wrok 进程 { WorkProcessCopyRight(); _tmain_1 (); } return 0; }
18/10/2006
1.2 int t7() { const char *p="12345"; const char ** q=&p; *q="abcde"; const char *s=++p; p="XYZWVU"; printf("%c\r\n",*++s); }思路: const char *p ="12345" 定义的是 p所指内容不可改变, p可变 const char ** q=&p; *q="abcde"; 改变p所指的内容为 abcde ,p改变 const char *s=++p; 拆解为 ++p;const char *s=p; s指向 "b"的地址 p="XYZWVU"; p改变,但p原来所指的不发生变化 printf("%c\r\n",*++s); 打印"b"后的字符 'c'
该题考察的是const的用法,对于
const int a; int const a; const int *a; int * const a; int const * a const; /******/ 前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。即使不用关键字 const,也还是能很容易写出功能正确的程序,那么为什么还要如此看重关键字const呢?因为: 1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
编程题 1二叉平衡树搜索节点 思路:这是最简单的数据结构操作
递归操作 Node * search(Node * root ,int value) { if(! root ) return NULL; else if if(value == root->value ) return root; else if(value < root->value ) return search(root->left,value); else return search(root->right,value); } 非递归搜索 Node * search(Node * root ,int value) { Node *p=root; while(1){ if(! p ) return NULL; else if(value == p->value ) return p; else if(value < p->value ) p=p->left; else p=p->right; } } 2 给定函数T的表达式 T(0)=T(1)=1 T(2)=2 T(n)=T(n-1)+T(n-2)+T(n-3) ,求T(N)最优算法 思路: 按道理应该是对T进行数学计算,计算出T(n)与n之间的关系表达式,不过考场的时间不多,加上我又是特别懒的那种因此决定还是根据迭代式进行计算,这样子显得我的编程能力不错(显然数学能力差),回来听闻SMTH上有NB人士用数学方法计算了,看来我是去不了google了 对付这种表达式最快的算法应该是递归 了 int t4(int n) { switch (n) { case 0: ; case 1: return 1; break; case 2: return 2; break; default: return t4(n-1)+t4(n-2)+t4(n-3); break; } } 考虑没有关系表达式,又要最优,因此只能放弃写的最快递归写法,考虑递推 的使用 另外考虑到空间效率,因此把需要的数组压缩到最间的3个数 int t3(int n) { int i=0; int t[3]; t[0]=1; t[1]=1; t[2]=2; for(i=3;i<=n;i++) { t [ i % 3 ] = t[0] +t[1] +t[2] ; } return t [ (i-1) % 3 ]; } int main() { #define M 1000 double start,finish; int nRes,i; start=(double)clock(); for(i=0;i<1;i++) nRes=t4(30) ; finish=(double)clock(); printf("%d,time=%lf\r\n",nRes,(finish-start)/M); start=(double)clock(); for(i=0;i<1;i++) nRes=t3(30) ; finish=(double)clock(); printf("%d,time=%lf\r\n",nRes,(finish-start)/M); printf("\r\n"); system("PAUSE"); return 0; } 运行程序就能发现递推能比递归快上很多 运行结果: 53798080,time=0.921000 53798080,time=0.000000 请按任意键继续. . .
3 给定无向图以及顶点 V1 V2 求V1 V2间是否存在长度为K的通路 思路: 模糊记得定理 设A有向图G=的邻接矩阵,V={v1,v2,…,vn},则Ak=()的第i行第j列元素等于从vi到vj长为k的通路数,等于经过vi长为k的
回路数. 因此问题转换为对无向图的邻接矩阵求K方 做这种计算,我向来不考虑空间损耗的,因此直接使用N×N的邻接矩阵 MAX 为数组最大开辟长度 m为数组实际大小 n 为K长度 #define MAX 100 int t6(int m,int n) { int i, j, k,z; double r; int A[MAX][MAX],B[MAX][MAX],C[MAX][MAX]; //置初值 for (i=0; i<m; i++) for (j=0; j<m; j++) { A[i][j] = 1; B[i][j] = 1; C[i][j] = 0; } int ii,jj, kk, im; int minj, mink; for(z=0;z<n-1;z++) { for(i=0;i<m;i++) for(j=0;j<m;j++) { for(k=0;k<m;k++) C[i][j]+=A[i][j]*B[i][k]; } memcpy(B,C,m*m*sizeof(int)); memset(C,C,m*m*sizeof(int)); } } 空间复杂度主要在 int A[MAX][MAX],B[MAX][MAX],C[MAX][MAX]; 因此 T(n)=3*n*n=n^2 时间复杂度主要表现在 for(z=0;z<n-1;z++) { for(i=0;i<m;i++) for(j=0;j<m;j++) for(k=0;k<m;k++) { C[i][j]+=A[i][k]*B[k][j]; } memcpy(B,C,m*m*sizeof(int)); memset(C,C,m*m*sizeof(int)); } T(n)=(n-1)* m * m * m =m^3
17/10/2006
递归函数的复杂度分析主要是通过递归方程和递归树进行分析的。首先,将一个递归函数的复杂度表示成递归方程的形式,然后通过数学计算或递归树分析,得到递归函数的近似复杂度。这里主要介绍两种递归函数的复杂度分析方法。(这里主要指时间复杂度)
1 . Divide-and-Conquer( 分而治之 )
这种类型的递归函数的复杂度可以用递归方程表示为
T(n)=bT(n/c)+f(n) ------------------------(1)
显而易见,这种方法是将一个问题分拆为几个问题大小相同的子问题。 f(n) 叫非递归代价,主要是指分拆问题和组合结果的代价
2 . Chip and Conquer
这种类型的递归函数的复杂度可以用递归方程表示为
T(n)=T(n-c)+f(n) ------------------------(2)
或更一般地,
T(n)=bT(n-c)+f(n) ------------------------(3)
显然,这种方法是将问题大小逐步减小,一直减小到可以解决的问题大小(也就是 base-case ) .
下面介绍分析复杂度的一个重要工具 ----- 递归树
递归树的结点有两个域,如下图:
T(size) 指问题大小为 size 时,函数的复杂度。 nonrec.cost 指问题大小为 size 时的非递归代价。
根结点的每个子结点都代表了这个问题分拆的一个子问题的复杂度。就这样递归地分解问题。一直到达叶子结点,也就是 base-case. 在前面的讨论中,我们没有涉及 base-case ,在使用递归树分析复杂度时,我们假设 base-case 的复杂度为 1 。
举一个例子就可以很明白的说明如何构造递归树。
Example1 : 由递归方程 T(n)=2T(n/2)+n 构造递归树
首先,构造根接点
它的子结点是
……, 以此类推。所以,最后的递归树为:
递归树规则 :
根结点的复杂度 = 所有非叶结点的非递归复杂度 + 叶子结点的复杂度。
所以,在上面的例子中,每层的非递归复杂度为 n, 而 base-case 出现在大约 lgn 层 (n/2^d =1;d = lgn) 。由于 base-case 的复杂度为 1 ,所以 T(n ) ≈nlgn ,即
递归树是分析和计算递归方程的一个重要工具。它可以直观地表示出递归函数的复杂度,并使人易于理解。
三. Divide-and-Conquer 问题的解决方案
在上面的例子中,我们用递归树规则得出了递归函数大概的时间复杂度。但递归树只能粗略地解决一小部分问题。这里要介绍 Divide-and-Conquer 类问题更一般的解决方案 —Master 定理。
不过为了更好的理解和使用 Master 定理,我们先要进一步地分析一下递归树,并介绍一个重要的参数:关键幂
回忆一下递归方程( 1 )的递归树。
每增加一层,函数的问题大小就减少 c 倍。假设 base-case 出现在第 D 层,则 n/c^D=1; D= ㏒ c(n). 使用换底公式, D=lg(n)/lg(c).
而同时每增加一层,这层的结点个数就增加 b 倍。所以第 D 层的结点个数为 L=b^D(L 表示 D 层的结点个数 ) 。两边取对数,得 lg(L)=Dlg(b); 将 D=lg(n)/lg(c) 带入上式,得 lg(L)=(lg(b)/lg(c))lg(n). 这时 lg(n) 前面的系数就被叫做关键幂。一般用 E 来表示。
Chip-and-Conquer 问题的解决方案
我们先考虑 Chip-and_Conquer 问题的一般情况,递归方程( 3 )
T(n)=bT(n-c)+f(n)
同样,我们使用递归树进行分析,递归树和 Divide-and-Conquer 类问题的递归树基本一样,这里就不再重复。只是对递归树进行一下分析。
显然, base-case 出现在 n/c 层,而且第 n/c 层的每个结点代价为 1 (假设)
首先,计算一下各层所有结点的非递归代价 , 第 i 层的所有结点的非递归代价是 (b^i)*f(n-ic).
所以,根结点的复杂度(也就是递归函数的复杂度)为
T(n) = ∑ 0 …n/c (b^i)*f(n-ic). 令 h=(n/c)-i ,得
T(n)=b^(n/c) ∑ 0 …n/c f(ch)/b^h ∈⊙(b^(n/c));
所以,这种递归函数的复杂度是随指数增长的,显然,指数增长的算法不具有任何意义。这种问题的递归方法是不实用的。
不过,考虑到它一种特别情况:就是递归方程( 2 )
T(n)=T(n-c)+f(n)
即 b=1 的情况。
这时,复杂度
T(n)= ∑ 0 …n/c f(n-ic) = ∑ 0 …n/c f(ch) ≈ (1/c) ∫ 0 …n f(x)dx( 定积分的定义)
此时 , 若 f(n) ∈⊙ (n^a), 则 T(n) ∈⊙ (n^(a+1));
若 f(n) ∈⊙ (logn), 则 T(n) ∈⊙ (nlogn); 这样的复杂度是可以接受的。
4 、 master 定理
10/10/2006
作者简介:梁永健先生目前担任思科系统(中国)网络技术有限公司首席技术官兼系统工程师总监一职,全面负责思科中国公司的产品和技术战略制定,并将和业务发展部门一起进行技术评估和实施,并为合作伙伴提供策略性的技术支持。
网络技术发展与Internet发展息息相关,而由此引发的网络技术发展新趋势也将与Ineternet发展关系密切。
我在网络行业从业时间比较长,而从一个业内人角度看Internet发展历程,它应该分为三个阶段:
80年代末期到90年代中期是互联网发展的第一个阶段,这一阶段的特点是,以企业为导向,实现内部网络建设,实现数据互联,强调网络技术的发展。
90年代末期是第二阶段,以外网建设为主,强调网络构架、以太网,如网通在ADSL的方面的发展。
Internet发展的第三个阶段,也是我们正在经历的阶段,时间大致是在2004到2009年之间。这一阶段与前两个阶段不同的是,技术不再成为导向。取而代之的是用户体验与新功能新技术的应用和开拓为特点。技术的发展不再单单为了内网或者外网的建设,而是如何利用好网络,如何使用户更容易享受到网络服务,以及网络服务的开拓与推广。
基于这种变化,就思科观察,未来网络将呈现出八大发展新趋势。
趋势一:智能终端与应用服务导向
智能终端的出现使未来的网络、应用和服务趋于融合,技术应用成为导向,即以技术应用为主导。 以前不论是话音网、数据网、广电网,一切都由网络运营商来主导,网络运营商提供什么服务,每个用户就享受什么服务。而智能终端的出现,将数据业务、语音业务以及视频业务等多种不同的服务叠加在一起,致使应用推动成为未来网络发展的首要趋势。
随着智能终端的发展,应用的融合推动服务的融合,服务的融合又推动网络的融合。以前,语音、视频、数据等业务分别在语音网、视频网、数据网上实现,每一张网都是专网;而现在数据网上融合了以上三种业务,而其它两个网络也融合其它网络的业务。业务不断叠加、融合到同一网络之中,使网络的融合成为不可抵挡的趋势,最终的结果是不同的网络定会融合在一起。原有的话音网日益缩小,新的网络将不断扩张,并具备更多的功能以适应服务融合的要求,一张共享的网有多种不同的服务平面。以前谈到三网合一是从经济的角度去看,今天再谈三网合一则是推动新服务的角度看来,融合的趋势必然会日益明显。一切与网络相关的技术都将受到应用为导向的影响。目前正处在过渡时期。 趋势二:计算机系统与网络重组
随着计算机技术发展,软、硬件技术的发展,计算机跟网络的分工将会重新定位。随着技术的发展,网络设备能力是越来越来强,以前由计算机来完成的工作会慢慢转到网络上。而且这种变化其实多年来一直在持续。
以芯片技术发展为例:早期的集线器、路由器以及防火墙等设备均以软件形式出现,随技术非发展,一些具有共性特征的功能给剥离,通过专有芯片来完成相关功能,形成专有设备。从发展历程看,芯片技术只能做到物理连接,出现了集线器;后来交换功能可交由芯片完成,出现了交换机设备……
这种发展趋势预示着那些缺乏共性的、专门的应用开发都会放到计算机里做;共性的应用将移交给专有设备完成。这将使网络系统总体性能大大提高,同时导致市场细分日趋明显。
趋势三:相关“网络”技术的整合
以前网络的层次为: 第一层:传输——光对光、电信、光网; 第二层:路由/转换——数据网,路由器,转换器等第二层数据; 第三层:应用服务的集成平台; 第四层:网络应用与交换(IPC)。
以前我们一直看这些技术完全是分开的,目前不同技术开始融合。思科SDN(自防御网络)的理念让安全与网络不可分离,建网、做芯片做软件不再仅仅考虑速度问题,而要增加防火墙、放病毒等功能,即更多地考虑安全因素,整个网络层次最终整合在一起。
就目前状况而言,我们承认思科在数据方面仍然无法做到电信运营商那么好。试想今天几乎没有任何一家公司在话音业务方面,会因为考虑交换机宕机问题采购两台电话交换机,互为备份的。同样,我们不会有电话交换机放在这里后,再要加很多东西放入设备中,因为设计之初,就已经考虑较为全面了。
今天数据网的建立非常复杂,建网要有路由、交换机等网络设备,还需连线,加了很多东西。从发展趋势看网络将不仅单单追求快,而是追求把很多功能集中在一起,并像以前的网一样——是一个结构简单、安全可靠、功能全面的网络。
可见,数据整合、技术整合带来的并不是技术本身的发展,而是让网络更简单,带给用户很多好处。思科在做产品是也有很理想的方向,即不单单是追求“快”,而且把相关的技术整合在一起。 趋势四:计算机系统结构的改变
在第二个趋势当中,计算机和网络系统之间的分工发生变化,很多计算机的功能放到网络之中。
计算机本身由CPU、内存、外设构成,通过中间的总线连接。随着网络的发展,网络的频宽越来越宽的时候,我们网络可以具备同于计算机那样的总线,CPU跟外设,CPU跟Memory,中间的总线通过网络来实现。我们以前所熟悉的计算机结构将会发生巨大变化,CPU、MB分化,外设的分化。新的概念产生——虚拟系统总线,网络可以支持传统的计算机设备。
虚拟总线带来最大的好处是虚拟化,使资源共享更加理想。不久的将来,计算机本身也发生改变。现在我们已经看到有一些新的技术往这个方向走。
趋势五:数据中心构造与发展
以前的数据中心网络建设由于受到网络带宽的限制,计算机不得不离用户很近。网络带宽的扩增则使数据中心更加集中化,不需要在每个地方放很多人维护整个计算机网络跟应用系统,大大降低了用户的网络建设成本。在美国在国外已经非常非常明显,大部分大企业IT的人基本上都集中在总部,比如容灾中心,而外面的机构基本上不需要投入太多的人力维护。
以思科自身举例,思科全球基本上没有太多IT人员做维护工作。这将是数据中心发展的一个明显趋势。中国是思科一个比较特别的例子,需要投入一些维护人员,在其它大部分国家思科的数据中心基本上一个维护人员都不需要。
这种发展趋势对用户而言是操作的简化,人工成本的下降。数据中心的集中化不仅是数据的集中,计算机中心建设变成大计算机中心,而是同时网络也在集中化。过去电信运营商需要在很多程序里存放数据,当中央的设备是越来越可靠,越来越性能高的时候,网络将趋于扁平化。
我们看到数据中心的建立,大数据中心,网络扁平化,是随着网络的频宽本身能力端口的加强,我们明显见得到,在这里数据中心建立我们可以分三个阶段:
第一阶段:数据中心整合 国内的几个大银行、海关都已经完成了整合工作。其它国内的用户虽然尚未着手实施,但是整合已成为共识。 第二阶段:集中与虚拟化 国外大部分的企业已经进入这一阶段,通过集中进一步实现虚拟化,最终达到共享。很多新技术、很多新产品应势而生。
第三阶段:自动调配 从整个计算机网络发展的方向性来讲,虚拟化使网络跟计算机自动调配去适应需求成为可能。今天国内、国外都向着这一阶段发展,相对应的技术逐渐成熟。思科也不例外,现在总体产品规划和标准的制订都朝着这个方向走,相信会有更多的新技术和标准出台。
随着这三个阶段的演变发展,今后的网络趋于简化,成为低端网,用户不用再关心他的网到底怎么建,资源应该放在哪里,一切都已虚拟化。未来用户端设备可以用任何技术连到网络,连接时,网络设备跟终端设备会自动知道你的应用是什么,你的设备能享受什么服务,你无需了解后台的东西是什么样的,所有物品会用一个标签去代表,实现自动会匹配(match),这是我们说的最终希望做到的一点,这是我介绍的三个阶段里面的第三个阶段。
这样的结构同样适用于运营商来。运营商跟企业网的唯一区别在于他可能今天连接的接入网多了几种,但是有些网会联到一张SP网上去,后台是同一个服务中心,面向不单是企业内的用户。数据中心,用户端,其实都一样,企业跟电信从理论上来讲,又跑回同一个概念之中,前面所说的3个阶段,开始的时候是企业导向,后来是公网导向,现在又回到原来的导向,日益趋同。唯一不同的地方就是与企业网的流量不一样,关心的安全不一样,其实整个概念是一样的。
趋势六:传统应用IP化
传统技术、传统产业与IP日趋融合。举一个明显的例子——北京各个路口的交通监控器。这些监控器是模拟技术,一个简单的摄像头也可以接入IP网络(从某种角度讲,IP本身就是一个很好的交换机)。随着网络芯片技术的发展,越来越从核心应用边缘化,体积趋小,价格便宜,更多传统应用将日益IP化。我们从简单的计算机互联到后来的VoIP,到现在的统一通信,到存储,过去我们所谓的SAN,今天所谓的IPTV,以后的IP视频监控,所有东西都向这IP化的方向发展。
正是基于此种思路,“第四张网”的概念随之出炉:现每栋楼房都至少具备3张网——煤气、水、电。而以后会出现第四张网——信息网,也将成为一个楼宇的基础设施。这第四张网——即-公用事业网(Utility Network)的出现,将会使更多我们早已熟悉的习惯的传统的服务日益IT化。
趋势七:网络设备“电信”化
当技术发展到进入第四张网——公用事业网(Utility Network)之后,网络设备将会电信化。电信化的意思是,网络设备将会像电信语音业务中那样,人们完全不会为交换机宕机故障而担忧,设备间可以迅速切换。以后的网络设备进入第四张网之后,在技术的设计上,对性能叠加予以更多的考虑,具有很强的可靠性。模块化的设计,以便于功能的添加以及设备的快速更换。当需要添加新的功能或是出现故障的时候不必整台设备进行更换,只需对不同的功能模块进行加减替换就可以了。这也将成为未来的一个重要发展趋势。
趋势八:IT投资分配发生变化
今后投资分配将会发生较大变化,投资将随着技术的发展而发生整合。在整合之前,用户的投资无限增大,需要不断地进行网络建设投资。而整合之后,一些IT投资随着服务器之间的重新定位、技术本身的整合发生改变。光投资、计算机投资、数据设备投资以及话音网与数据网投资,在融合之后要比以前各自分开的时候要更经济。整合之后的投入会比以往更加经济。
09/10/2006
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型
(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、
结构、联合等等)的数据单元。在结构中,编译器为结构的每个成员按其自然对
界(alignment)条件分配空间;各个成员按照它们被声明的顺序在内存中顺序
存储,第一个成员的地址和整个结构的地址相同。在缺省情况下,
C编译器为每一个变量或是数据单元按其自然对界条件分配空间
例如,下面的结构各成员空间分配情况:
struct test {
char x1;
short x2;
float x3;
char x4;
};
结构的第一个成员x1,其偏移地址为0,占据了第1个字节。
第二个成员x2为short类型,其起始地址必须2字节对界,因此,
编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和
第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。
在test结构中,成员x3要求4字节对界,
是该结构所有成员中要求的最大对界单元,
因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。
整个结构所占据空间为12字节。
结构中的位段
所谓位段是以位为单位定义长度的结构体类型中的成员。
编译器对结构中位段的分配遵从下面几点原则:
? 对于长度为0的位段,其下一个位段从下一个存储单元开始存放:
如:
struct T {
unsigned char a : 1;
unsigned char b : 2;
unsigned : 0;
unsigned c : 3;
};
结构T的成员a和b在一个存储单元中,c则在另一个存储单元中。
· 一个位段必须存储在同一存储单元中,不能跨两个单元:
如:
struct T {
unsigned char a : 4;
unsigned char b : 6;
};
结构T的成员a在一个存储单元中,b则在另一个存储单元中。
更改C编译器的缺省分配策略
一般地,可以通过下面的两种方法改变缺省的对界条件:
· 使用伪指令#pragma pack ([n])
· 在编译时使用命令行参数
#pragma pack ([n])伪指令允许你选择编译器为数据分配空间所采取的对界策略.
05/10/2006
凌晨一点半,网卡的灯还在一闪一闪的,远方的你们,还好吗?
国庆快乐,该睡觉了!
for xf 昨天生日快乐!
交大的网络
刚刚写完IP/MAC切换的程序,顺便就回忆了一下交大的网络.
交大网络历程
2000年我刚来的时候,交大的宿舍还没有网络,能上网的地方只有实验室和教学区机房.
大一上学期的时候要想上网只能去机房,而要进机房必须要排队,而且是提前排队,计科的机房是不收费的 所以排队的人特别多,要想占个好位置,必须要早点去排队,机房两点开门,一般都是一点去排队.和上课占座一样,我们也是团伙作案的,每次派一个人去站队,其他人吃饭,一周五天,大家轮着来. 当时qq还不是很流行吧,网上聊天的不多,游戏也玩的少,我们去机房上网大多时间是看电影,那时候rm格式刚刚出来,一张cd盘能装下好多部,好些人拿盘溜进宿舍兜售.能在那么小的一张盘上装如此多的电影的确是一件很神奇的事情,以致于wx一直都不相信,认为我们受骗了^_^其他时间我们会玩玩远程控制等一下简单的所谓黑客的手段,比如我就欺负过yzg
值得一提的是当年交大的红果园在全国是很有名的,可惜官方介入了红果园的发展,导致了红果园的言论自由被限制,结果红果园慢慢的走向了无名
大一下学期,学院允许学生装电脑,我才能在宿舍使用.当时19楼的网络已经有个大概的模型,很多宿舍已经自发连接在一起的,用的是最低级的HUB,速度不快,不过算是一个小规模的局域网,晨光就是这个时候出现的吧,来自计科98级的师兄们的努力.往后一点时间,20楼在alulu等人的努力下也和12楼19楼联网成功,交大的宿舍网基本出现.在大家的倡议下我们集资买了百兆交换机用于楼道间数据交换,局域网开始稳定,服务也得到改善了,由于网络中心的支持,当时我们可以在宿舍无限制的上网.
这个时候,由于网线布满了楼道,学校觉得很不美观,决定投资500W建立学生宿舍网,因此拆除了我们原始搭建的网络,重新布局,一个学期的时间布局完成百兆校内局域网,应该是大二下学期期末了吧,大三了.(也是那个时候,xxxss来到我们寝室,这个交大传奇式的大百科全书,在我们寝室里接受了两年多的魔鬼式锻炼,终于在我们毕业的时候脱离苦海,当然这是后话了)
随着学校校内宿舍网的建立,校内的访问速度和访问质量都在提高,毕竟官方的500W投资不比我们集体捐款的那几个money,在宿舍网官方管理后几个月,学校开始对宿舍网收取上网费用,20元/月,10G国内流量.此后一直如此
宿舍网在一点一点的建立,教学区实验室的网络却没有得到什么改善,除了机房这种收费的地方比较规范以外,其他几栋大楼的实验室IP规划都特别乱,特别是九号教学楼,各层的交换机,网线布局,vlan规划根本没有条理可言,怎一个乱字了得
比宿舍网收费稍晚几个月,教学区实验室也开始了收费行动,和宿舍网不大一样的是教学区实验室的IPMAC难以控制,各个实验室都是自己申请IP,然后自己规划内部网络,无法做到像宿舍网一样IP/MAC匹配到每个物理端口,于是计算中心开发了IP网关计费系统,只有在计算中心注册的IP/MAC对才能访问网络资源,这样学校的网络访问就控制到个人了.
在整个网络规划成长的过程中,我一直都处于旁观者的地位,我一直都没有自己的上网帐号.舍网开始建立的时候,我把机器搬去了实验室,实验室开始收费的时候我是毕业生,享受毕业生帐号,上研后用了一学期实验室帐号,然后去实习,没有机会申请^_^
交大的网络很锻炼人
想最开始的时候,只要有台机器,有根网线,在九教往墙上一插就能上网,的确很惬意,后来开始MAC限制,在后来开始IP/MAC绑定,控制的手段在发展,对付的手段也在发展,为了上网,我们必须充分利用我们的聪明才智,没有MAC,就监听网络,也就促成了我的第一个网络软件,IP/MAC监听器的产生,它会自动监听网络,探测IP和MAC的变化,并且记录,记得当时很短的时间我就扫描到了整个网络的IP/MAC,能自动比较收集不在线可以使用的IP/MAC,避免经常出现的MAC冲突导致双方都不能上网的麻烦
我记得最开始写这个软件的时候,天天抱着一本TCP/IP详解在看,晚上回寝室会和xxxss讨论ip mac冲突能产生的问题和解决方式,还有就是突破网络防火墙的一些办法,然后一点一点的开始学 vc,研究候捷的深入浅出MFC,记得就是那个时候的某一个时刻我突然开始对c++的面向对象来了感觉,就行一个瞬间突破了自己瓶颈的武林高手一般.现在看来当时的程序好简陋^_^
最近几天,拿着笔记本在宿舍和实验室两头跑,天天改IP/MAC是一件很麻烦的事情,上网找了一下,要么是能改IP的,要么是能改MAC的,没有专门为交大制作的IP/MAC同时切换的软件,只好自己写了一个IP MAC一起切换的工具
19/06/2006
以下事情发生在2006年6月14日至2006年6月16日:
0614 公司对于实习生一直没有一个明确的说法,特别针对N+1的补偿问题,gaoxin对此表示担忧 下班临走前,我建议他给王总发信确认一下
0615 9:00 早上上班收到gaoxin转发的信件,王总责bai回答gaoxin的问题,bai回的是根据实习协议公司将不给于实习生 任何补偿。当即我将bai的回信转发到我所认识的实习生,几分钟后,这封信在公司公告栏上贴出,大家反映激烈 随即yangyan将实习生协议复印输入,并且开始询问学法律的同学我们实习生的协议
0615 10:00 根据收集到的实习生信件地址估计在harbournetworks的实习生将达到50人的规模,大家决定在食堂聚会商议 此事,主要讨论了我们的应对措施,大会决定联名上书,实习生集体签名希望公司能够给予一个明确的说法
(youlei的信件)
1.公司有很多实习员工,在公司工作和正式员工没有差别,一样卖力,有的比正式员工工作时间都长,对公司的发展做出了很大贡献。
2.合同上没有明确说公司被收购时的补偿,但是直接不给补偿应该是不合法的,合同不能不符合法律。
3.事实上的劳动关系就应该遵循劳动法。 农民工尚且如此。
4.招了实习生就要负责,请公司给一个正式的,明确的,公开的说法,以邮件或者公告的形式通知所有的实习员工,不要让 我们自己蒙在鼓里,到时候直接走人还不知道怎么回事。 5.保留寻求法律援助的权利。
0615 15:00 主要在港湾实习生MSN群内讨论对策,选出实习生代表lidanli,yulei,lijia,jinxingliang,guoling 签名 人数达47人,下午公司领导在开会,没有合适的时间接触,lidanli询问changzhimin,听说公司在开会顺便会讨论我们实 习生的问题
0616 9:00 没有收到公司关于实习生问题的任何回复,决定联系员工代表,希望zhang能帮忙向公司转达我们实习生的意 见
0616 10:20 lidanli通知大家在D3002开会讨论,同时与chang联系,希望能与公司领导对话 0606 11:00 员工代表zhang与我们在E1016开会讨论,阐述了公司和员工两方面的意见,并帮忙联系王总
0606 14:00 zhangjianjun、lidanli、guoling一起去见了王总,一起讨论了一下关于公司给我们实习生的政策,总结为 下面几条:
1.对于不满意华为安排的同志给予和普通员工一样的待遇,即实习生也有补偿金;
2.对于不满意华为安排的同志补偿金的给予细则同普通员工一样;
3.对于满意华为安排的同志,具体签约细则和补偿细则由华为公司给出具体的决定,我们现在还没有决定,作为港湾公司 也只能给予部分的建议。
至此,事情暂时告一段落,感谢在此事件中支持帮助我们的各位兄弟姐妹,感谢深圳的兄弟姐妹的支持。
听说,作网络的港湾网络被华为收购了,听说而已,合同还没有正式签,只是发布了备忘录。但我却还在港湾上班,颇大的一个公司,上班的同事也有上千人,偶尔还有中央的大员来视察,在中国的网络设备市场一个中华骄傲、一个后起之辈。这些年,据说都风光到外国去了,就是“国际化路线”,现在IT厂商里最流行的。 听说华为是很厉害的,“打得思科、北电网络落花流水”。可惜我没有亲身经历过,描述的有些夸张了,我以为。 然而一切国内的网络设备供应商里,我知道得最早的却是这华为公司。早在大学即将毕业时,为工作忙的焦头烂额,我的师哥师姐们就常常对我说,要是没有地方去的话就去华为吧!有个叫做任正非的一手创建了华为这个公司,发展的空前绝后的迅速。早几年疯狂招人,基数都是成百上千的,往往一条线的人就被华为连锅端了,工资还出奇的高。哪像现在的这些公司挑挑拣拣,还不付四金。 之后,任正非遭遇了一次背叛,他最喜欢的一个叫做李一男的手下背叛了他,拉走了一批精英创办了港湾公司,专门和华为对着干。任正非很痛心,于是痛下杀手,据说当年在华为公司的每个区域办事处里都设置了一个部门叫做“打港办”,专门负责和港湾抢单子,有的单子港湾已经拿下来了,“打港办”的同志会找到招标的公司,跟他说这个项目华为不收钱了,免费给你做。 我师哥讲得还要有趣的多,大约是出于一本任正非的自传《华为的冬天》里的,但我没看过这本书,所以不知道“任正非”“李一男”究竟是否这样写。 但“打港办”,这个名字让我想到了“打狗棒”、“扫黄打非”之类的,我想不明白,什么样的过节,会让师徒反目成仇,而且如此兴师动众? 总而言之,在华为的强压之下,港湾公司日子过的很不舒畅,甚至于想破罐破摔,动了干脆把自己卖掉的念头。之后就传出了几乎确切要卖给西门子的传言,风波似乎平息了……这就是港湾公司和华为公司的故事。此后的事情还很多,如“港湾公司努力转型”之类,但我现在都忘记了。 那时候我唯一的希望,就是华为公司继续疯狂,把我们这条线也一锅端了,我的工资也能到10k。后来我又听说华为公司死人了,心里有一点不舒服,听说华为的每个人桌子下面都有个毯子,中午还能躺在地上睡午觉,当然晚上也睡觉用,毕竟现在出租车夜间出行都涨价了,公司并不鼓励员工打的回家吧,毕竟这样还是能给国民省点油的。后来我仔细看了《华为的冬天》这本书,书里说华为纪律很严,十点半之前不准下班,如果连续两个月评分等级低于B(最高A,连续两月A可以加薪;连续两月C,减薪;如果是D,就开除)或者被开除或者被降薪,其实也很痛苦,然而我心里仍然不舒服,仍然希望华为再疯狂一次,毕竟我的工位下也有一个睡垫。 不过我还是同情李一男的,我觉得他是个有志青年,任正非,既是师生关系又都是中国人,干吗要置人于死地呢?也把自己给弄得这么紧张。 现在华为居然把港湾给买下了,则普天下的媒体,其欣喜为何为? 这是有事实可证的。试到国内各大媒体,找专题报道去,凡有新闻频道、通信频道,除了几个脑髓里有点贵恙、消息比较闭塞的之外,可有谁不替华为击掌叫好,不怪李一男咎由自取。 作为华为公司曾经最年轻的副总裁,任正非对你有多大的知遇之恩,你应该只管作好自己的份内,研发和管理,这难道不能实现你的人生价值吗?难道不能报效祖国吗?何必要玩火,横来招是搬非,大约是怀着嫉妒罢,――那简直是一定的。 听说政府领导也怪港湾公司多事,浪费多余的生产力,在“打港办”兢兢业业、孜孜不倦的努力下,港湾公司的用户也渐渐远去,港湾公司几乎到了众叛亲离的境态。港湾公司转型来转型去、卖来卖去,到最后还是不偏不倚撞倒了华为的枪口上。 年少的时候,对政府的做派总是有些微词,大约是“逆反心理”作怪,独于这一件却很满意,因为华为不再“疯狂招人”一案,在我看来的确应该由港湾公司负责,他实在办得很不错的。只是没有途径打听,这个事情是否确切属实,因为不在《华为的冬天》中找到,却是民间传说罢。 收购尘埃落定的时节,大家开始冷静的观看这一起并购案。由于港湾网络的优势在于企业网领域,华为本身已经拥有了主攻数据通信市场的华为3COM公司,从业务构成上分析,华为并没有收购港湾的必要性。但由于港湾网络的创始人李一男曾是华为董事长任正非最为看重的、也是华为最年轻的副总裁,他独立出走并创办港湾后,华为成立了“打港办”对港湾进行打压,因此,此次收购不排除有感情因素。 对于港湾员工,除了华为因为业务整合需要裁掉一部分外,愿意到华为工作的可以到华为继续工作,不愿意的则按离职处理,员工的补偿待定,原来员工持有的期权也可能无法兑现。 曾经的港湾公司说自己将来会是对员工最慷慨的上市公司,但如今港湾的员工只能大批的去华为公司上班了,与曾经的“打港办”(这个部门现在自然不需要了)成为同事,之前的诺言基本上如风而过。任正非的华为仍在做“中华骄傲”,员工们日复一日,年复一年的加班,但听到“过劳死”之后,我却已经彻底不想去华为了。现在只有李一男,一个人退到了暗处,静静的在观望着我们,同时也在反省着自己。莫非他离开华为公司的时候,竞没有感受到,他的老师是如此看重他吗? 活该。
25/05/2006
OpenVPN Protocol
OpenVPN Protocol, taken from ssl.h in OpenVPN source code.
TCP/UDP Packet: This represents the top-level encapsulation.
TCP/UDP packet format:
Packet length (16 bits, unsigned) -- TCP only, always sent as plaintext. Since TCP is a stream protocol, the packet length words define the packetization of the stream.
Packet opcode/key_id (8 bits) -- TLS only, not used in pre-shared secret mode. packet message type, a P_* constant (high 5 bits) key_id (low 3 bits, see key_id in struct tls_session below for comment). The key_id refers to an already negotiated TLS session. OpenVPN seamlessly renegotiates the TLS session by using a new key_id for the new session. Overlap (controlled by user definable parameters) between old and new TLS sessions is allowed, providing a seamless transition during tunnel operation.
Payload (n bytes), which may be a P_CONTROL, P_ACK, or P_DATA message.
消息类型:
P_CONTROL_HARD_RESET_CLIENT_V1 -- Key method 1,清楚先前会话 从客户端获取初始KEY
P_CONTROL_HARD_RESET_SERVER_V1 -- Key method 2,清楚先前会话 从服务器端获取初始KEY
P_CONTROL_SOFT_RESET_V1 -- New key, with a graceful transition from old to new key in the sense that a transition window exists where both the old or new key_id can be used. OpenVPN uses two different forms of key_id. The first form is 64 bits and is used for all P_CONTROL messages. P_DATA messages on the other hand use a shortened key_id of 3 bits for efficiency reasons since the vast majority of OpenVPN packets in an active tunnel will be P_DATA messages. The 64 bit form is referred to as a session_id, while the 3 bit form is referred to as a key_id.
P_CONTROL_V1 -- Control channel packet (usually TLS ciphertext).
P_ACK_V1 -- Acknowledgement for P_CONTROL packets received.
P_DATA_V1 -- Data channel packet containing actual tunnel data ciphertext.
P_CONTROL_HARD_RESET_CLIENT_V2 -- Key method 2, initial key from client, forget previous state.
P_CONTROL_HARD_RESET_SERVER_V2 -- Key method 2, initial key from server, forget previous state.
P_CONTROL* and P_ACK Payload: The P_CONTROL message type indicates a TLS ciphertext packet which has been encapsulated inside of a reliability layer. The reliability layer is implemented as a straightforward ACK and retransmit model.
P_CONTROL message format:
local session_id (random 64 bit value to identify TLS session). HMAC signature of entire encapsulation header for integrity check if --tls-auth is specified (usually 16 or 20 bytes). packet-id for replay protection (4 or 8 bytes, includes sequence number and optional time_t timestamp). P_ACK packet_id array length (1 byte). P_ACK packet-id array (if length > 0). P_ACK remote session_id (if length > 0). message packet-id (4 bytes). TLS payload ciphertext (n bytes) (only for P_CONTROL).
Once the TLS session has been initialized and authenticated, the TLS channel is used to exchange random key material for bidirectional cipher and HMAC keys which will be used to secure actual tunnel packets. OpenVPN currently implements two key methods. Key method 1 directly derives keys using random bits obtained from the RAND_bytes OpenSSL function. Key method 2 mixes random key material from both sides of the connection using the TLS PRF mixing function. Key method 2 is the preferred method and is the default for OpenVPN 2.0.
TLS plaintext content:
TLS plaintext packet (if key_method == 1):
Cipher key length in bytes (1 byte). Cipher key (n bytes). HMAC key length in bytes (1 byte). HMAC key (n bytes). Options string (n bytes, null terminated, client/server options string should match).
TLS plaintext packet (if key_method == 2):
Literal 0 (4 bytes). key_method type (1 byte). key_source structure (pre_master only defined for client -> server). options_string_length, including null (2 bytes). Options string (n bytes, null terminated, client/server options string must match). [The username/password data below is optional, record can end at this point.] username_string_length, including null (2 bytes). Username string (n bytes, null terminated). password_string_length, including null (2 bytes). Password string (n bytes, null terminated).
The P_DATA payload represents encrypted, encapsulated tunnel packets which tend to be either IP packets or Ethernet frames. This is essentially the "payload" of the VPN.
P_DATA message content: HMAC of ciphertext IV + ciphertext (if not disabled by --auth none). Ciphertext IV (size is cipher-dependent, if not disabled by --no-iv). Tunnel packet ciphertext.
P_DATA plaintext packet_id (4 or 8 bytes, if not disabled by --no-replay). In SSL/TLS mode, 4 bytes are used because the implementation can force a TLS renegotation before 2^32 packets are sent. In pre-shared key mode, 8 bytes are used (sequence number and time_t value) to allow long-term key usage without packet_id collisions. User plaintext (n bytes).
Notes: (1) ACK messages can be encoded in either the dedicated P_ACK record or they can be prepended to a P_CONTROL message. (2) P_DATA and P_CONTROL/P_ACK use independent packet-id sequences because P_DATA is an unreliable channel while P_CONTROL/P_ACK is a reliable channel. Each use their own independent HMAC keys. (3) Note that when --tls-auth is used, all message types are protected with an HMAC signature, even the initial packets of the TLS handshake. This makes it easy for OpenVPN to throw away bogus packets quickly, without wasting resources on attempting a TLS handshake which will ultimately fail.
18/05/2006
OPENVPN+OPENSSL数据流程分析
严格来说,OpenVPN调用了Openssl函数库,Openssl对于OpenVPN而言仅仅是一套函数,我在这里所说的应该是OpenVPN的数据流程才对,但实际上OpenVPN不像其他简单的VPN程序将SSL协议过程完全交由Openssl控制,OpenVPN介入了对SSL报文的分析处理,将Openssl的状态机引入到自身的状态机中。
OpenSSL的BIO概念,抽象了输入输出通道,基本的使用方式是将程序置于OpenSSL之上,对于Openssl而言,BIO的输入便是网络,通过 readsocket(#define readsocket(s,b,n) recv((s),(b),(n),0))进行网络读取,底层的数据流动完全由OpenSSL控制,对于应用层程序而言,就像在原有的TCP、IP协议栈的网络层和应用层之间添加了一个安全层,数据的收发加密解密对于上层应用而言是透明的,这也是符合网络分层概念的。此时数据的走向是 Internet=>Openssl=>Program=>Openssl=> Internet。
同样,由于BIO的抽象,它并不固定输入输出目的地,因此为了能够完全控制数据流,OpenVPN将自身定义为openssl的BIO的输入端,从而完全掌握了报文,此时数据的走向是Internet=> OpenVPN9(1) =>Openssl=>OpenVPN(2)=>Openssl=> OpenVPN(1) => Internet。 其中在OpenVPN(1)阶段,OpenVPN的主要工作在于对报文做预处理;而在在OpenVPN(2)阶段,OpenVPN的主要工作在于实现功能。
由于OpenSSL中存在大量的指针函数操作,给程序流程的分析带来了麻烦,为了能够进行堆栈调试,我们在OpenVP和OpenSSL的源代码中引入了一个宏定义 #ifdef _DEBUG_DATA_FLOW_ #define DEBUG_STACKWALK(fun_name)\ do{\ if(0)\ printf("%s\r\n",fun_name);\ }while(0); #else #define DEBUG_STACKWALK(fun_name) ; #endif 并将该定义添加到各个函数中,以便可以在各个操作系统上都能够打印函数执行过程。 环境 代码版本:OpenVPN-2.0.5以及OpenSSL 0.97i 操作系统版本Windows XP SP2 OpenVPN配置 客户端 clientdev tapproto tcp-clientremote 10.23.15.28 17909resolv-retry infinitenobindmute-replay-warningsca ca.crtcert CLIENT.crt #这里改成每个客户端相应的证书key CLIENT.key #这里改成每个客户端相应的证书keepalive 20 180comp-noadaptverb 1status openvpn-status.loglog openvpn.log 服务器端 port 17909proto tcp-serverdev tapserver 192.168.10.0 255.255.255.0keepalive 20 180ca ca.crtcert SERVER.crtkey SERVER.keydh dh1024.pempush "redirect-gateway def1"push "dhcp-option DNS 192.168.10.1"push "keepalive 5 180"user openvpngroup openvpnmode servertls-serverstatus openvpn-status.loglog openvpn.logclient-to-clientcomp-noadaptverb 1
编译 WINDOWS:选择DEV CPP的GCC编译器,首先用DEV CPP建立工程,将所有的源代码添加到工程中,添加工程的include目录,需要添加的有LZO库的include目录,OpenSSL的Include目录,之后添加工程所需的LIB库,包括 -L"C:/openvpn-2.0.5/lzo-1.08/lzo.lib" -L"C:/openvpn-2.0.5/ openssl-0.97i /out32dll/ ssleay32.lib" -L"C:/openvpn-2.0.5/openssl-0.97i/ out32dll/ libeay32.lib" -lcrypt32 -lws2_32 -lgdi32 -liphlpapi -lwinmm -lgmon,可以使用绝对路径取代这些简写,比如 -llzo 等价于-L"C:/openvpn-2.0.5/lzo-1.08/lzo.lib"。添加完这些即可编译运行。 分析 在Linux下和Windows下对OpenVPN进行线程观察,发现OpenVPN的确是单进程单线程在运行,而在这个过程中并不发生用户登陆的阻塞。 OpenVPN进行时间调度interval_earliest_wakeup,调用相关函数io_wait-> io_wait_dowork->socket_set-> stream_buf_read_setup->link_socket_connection_oriented-> link_socket_proto_connection_oriented-> stream_buf_read_setup_dowork-> stream_buf_set_next socket_recv_queue-> stream_buf_get_next-> socket_event_handle->socket_read_residual 由于TCP是基于流的读取,为了区别每个报文,OpenVPN为每个报文添加了一个长度字段, 因此OpenVPN形成的TCP报文流是如下样式的: 长度:2+数据+长度:2+数据+长度:2+数据+长度:2+数据……. 当读取到一个完整的报文(长度:2+数据)时,查看代码可以发现OpenVPN通过link_socket_read_tcp读取网络数据,并缓存于sock->stream_buf中。 取得的数据经过tls_multi_process确定客户端实例,转交tls_process,进入OpenVPN的状态机,如果处于握手协商阶段,OpenVPN首先将报文拆解开,读取操作码,同时读取SSL数据,通过key_state_write_plaintext 调用bio_write 写入到openssl的BIO通道,在写入过程中,因为BIO_Write调用到了ssl3_read_bytes(OpenVPN仅仅支持TLS1.0,TLS1.0的握手状态与SSL3是一致的),在该函数的执行过程中handshake的函数指针被赋值为ssl3_connect。此时openssl状态机运行,处理相应的状态,并将结果bio_write回OpenVPN,OpenVPN获取到openssl的返回,继续tls_process的过程 如果是转发的数据报文,则不再需要openssl状态机的维护,OpenVPN直接调用openssl的加密解密函数,处理报文然后发送到对应的socket上即可。
关于BIO-抽象的IO接口 其实包含了很多种接口,用通用的函数接口,主要控制在BIO_METHOD中的不通实现函数控制, 我初步估计了一下,大概有14种,包括6种filter型和8种source/sink型。 BIO是在底层覆盖了许多类型I/O接口细节的一种应用接口,如果你在程序中使用BIO,那么就可以和SSL连接、非加密的网络连接以及文件IO进行透明的连接。 有两种不通的BIO接口,一种是source/sink型,一种是fileter型的。 顾名思义,source/sink类型的BIO是数据源或数据目标(我不知道sink该怎么翻译,据水木liaojzh说,一般是destination(目标、宿)的同义词,大家自己理解吧,呵呵),例如,sokect BIO和文件BIO。 而filter BIO就是把数据从一个BIO转换到另外一个BIO或应用接口,在转换过程中,这些数据可以不修改(如信息摘要BIO),也可以进行转换。例如在加密BIO中,如果写操作,数据就会被加密,如果是读操作,数据就会被解密。 BIO可以连接在一起成为一个BIO链(单个的BIO就是一个环节的BIO链的特例),如下是BIO的结构定义,可以看到它有上下环节的: struct bio_st { BIO_METHOD *method; /* bio, mode, argp, argi, argl, ret */ long (*callback)(struct bio_st *,int,const char *,int, long,long); char *cb_arg; /* first argument for the callback */ int init; int shutdown; int flags; /* extra storage */ int retry_reason; int num; void *ptr; struct bio_st *next_bio; /* used by filter BIOs */BIO下联 struct bio_st *prev_bio; /* used by filter BIOs */BIO上联 int references; unsigned long num_read; unsigned long num_write; CRYPTO_EX_DATA ex_data; }; 一个BIO链通常包括一个source BIO和一个或多个filter BIO,数据从第一个BIO读出或写入,然后经过一系列BIO变化到输出(通常是一个source/sink BIO)。 BIO的结构定义和相关项解析如下: (包含在bio.h文件中,其主文件为bio_lib.c) typedef struct bio_st BIO; struct bio_st { BIO_METHOD *method;//BIO方法结构,是决定BIO类型和行为的重要参数,各种BIO的不同之处主要也正在于此项。 /* bio, mode, argp, argi, argl, ret */ long (*callback)(struct bio_st *,int,const char *,int, long,long);//BIO回调函数 char *cb_arg; /* first argument for the callback *//回调函数的第一个参量 int init;//初始化标志,初始化了为1,否则为0 int shutdown;//BIO开关标志,如果为1,则处于关闭状态,如果为0,则处于打开的状态。 int flags; /* extra storage */ int retry_reason; int num; void *ptr; struct bio_st *next_bio; /* used by filter BIOs */BIO下联 struct bio_st *prev_bio; /* used by filter BIOs */BIO上联 int references; unsigned long num_read;//读出的数据长度 unsigned long num_write;//写入的数据长度 CRYPTO_EX_DATA ex_data; }; 在BIO的所用成员中,method可以说是最关键的一个成员,它决定了BIO的类型,可以看到,在声明一个新的BIO结构时,总是使用下面的声明: BIO* BIO_new(BIO_METHOD *type); 在源代码可以看出,BIO_new函数除了给一些初始变量赋值外,主要就是把type中的各个变量赋值给BIO结构中的method成员。 一般来说,上述type参数是以一个类型生成函数的形式提供的,如生成一个mem型的BIO结构,就使用下面的语句: BIO *mem = BIO_new(BIO_s_mem()); 这样的函数有以下一些: 【source/sink型】 BIO_s_accept():是一个封装了类似TCP/IP socket Accept规则的接口,并且使TCP/IP操作对于BIO接口是透明的。 BIO_s_bio():封装了一个BIO对,数据从其中一个BIO写入,从另外一个BIO读出 BIO_s_connect():是一个封装了类似TCP/IP socket Connect规则的接口,并且使TCP/IP操作对于BIO接口是透明的 BIO_s_fd():是一个封装了文件描述符的BIO接口,提供类似文件读写操作的功能 BIO_s_file():封装了标准的文件接口的BIO,包括标志的输入输出设备如stdin等 BIO_s_mem():封装了内存操作的BIO接口,包括了对内存的读写操作 BIO_s_null():返回空的sink型BIO接口,写入这种接口的所有数据读被丢弃,读的时候总是返回EOF BIO_s_socket():封装了socket接口的BIO类型 【filter型】 BIO_f_base64():封装了base64编码方法的BIO,写的时候进行编码,读的时候解码 BIO_f_buffer():封装了缓冲区操作的BIO,写入该接口的数据一般是准备传入下一个BIO接口的,从该接口读出的数据一般也是从另一个BIO传过来的。 BIO_f_cipher():封装了加解密方法的BIO,写的时候加密,读的时候解密 BIO_f_md():封装了信息摘要方法的BIO,通过该接口读写的数据都是已经经过摘要的。 BIO_f_null():一个不作任何事情的BIO,对它的操作都简单传到下一个BIO去了,相当于不存在。 BIO_f_ssl():封装了openssl 的SSL协议的BIO类型,也就是为SSL协议增加了一些BIO操作方法。 上述各种类型的函数正是构成BIO强大功能的基本单元,所以,要了解BIO的各种结构和功能,也就应该了解这些函数类型相关的操作函数。 所有这些源文件,都基本上包含于/crypto/bio/目录下的同名.c文件(大部分是同名的)中。 在BIO_METHOD里面,定义了一组行为函数,上述不通类型的BIO_METHOD行为函数的定义是不同的,其结构如下(以非16位系统为例): typedef struct bio_method_st { int type; const char *name; int (*bwrite)(BIO *, const char *, int); int (*bread)(BIO *, char *, int); int (*bputs)(BIO *, const char *); int (*bgets)(BIO *, char *, int); long (*ctrl)(BIO *, int, long, void *); int (*create)(BIO *); int (*destroy)(BIO *); long (*callback_ctrl)(BIO *, int, bio_info_cb *); } BIO_METHOD; 在BIO的成员中,callback也是比较重要的,它能够用于程序调试用或者自定义改变BIO的行为。详细会在以后相关的部分介绍。 BIO的很多操作,都是BIO_ctrl系列函数根据不通参数组成的宏定义来完成的。所以要了解BIO的行为,了解BIO_ctrl系列函数以及其各个参数的意义也是很重要的。 【BIO目录文件的简要说明】 bio.h:主定义的头文件,包括了很多通用的宏的定义。 bio_lib.c主要的BIO操作定义文件,是比较上层的函数了。 bss_*系列:是soruce/sink型BIO具体的操作实现文件 bf_*系列:是filter型BIO具体的操作实现文件 bio_err.c:是错误信息处理文件 bio_cb.c:是callback函数的相关文件 b_print.c:是信息输出的处理函数文件 b_socket.c:是Socket连接的一些相关信息处理文件 b_dump.c:是对内存内容的存储操作处理 在BIO的基本操作系列函数中,他们用来BIO分配和释放操作,包括: BIO_new, BIO_set, BIO_free, BIO_vfree, BIO_free_all 他们的声明在openssl/bio.h文件中,其声明形式如下: BIO * BIO_new(BIO_METHOD *type); int BIO_set(BIO *a,BIO_METHOD *type); int BIO_free(BIO *a); void BIO_vfree(BIO *a); void BIO_free_all(BIO *a); 下面分别对这些函数进行解释. 【BIO_new】 这个函数创建并返回一个相应的新的BIO,并根据给定的BIO_METHOD类型调用下述的BIO_set()函数给BIO结构的method成员赋值,如果创建或给method赋值失败,则返回NULL。创建一个Memory类型的BIO例子如下: BIO* mem=BIO_new(BIO_s_mem()); 有些类型的BIO使用BIO_new()函数之后就可以直接使用了,如memory类型的BIO;而有些BIO创建之后还需要一些初始化工作,如文件BIO,一般来说,也提供了这样的一些函数来创建和初始化这种类型的BIO。 这是什么意思呢,举个简单的例子大家就明白了: 比如创建一个文件BIO,使用下面的代码: BIO* in=NULL; in=BIO_new(BIO_s_file()); BIO_read_filename(in,"rsa512.pem"); 这样,BIO in才能使用,而如果是创建一个memory类型的BIO,则只需要如下一句代码: BIO* mem=BIO_new(BIO_s_mem()); 然后就可以对该BIO mem进行操作了。 另外,需要补充的是(这个大家从前面两篇文章可能已经认识到了),对于source/sink类型的BIO,其类型创建函数一般为BIO_s_*的形式,对于filter型的函数,其类型创建函数一般为BIO_f_*的形式。 【BIO_set】 该函数功能比较简单,就是对一个已经存在的BIO设置新的BIO_METHOD类型。其实就是简单的对BIO的各个成员进行初始化,并将参数type赋值给该BIO。其实,BIO_new函数在使用OPENSSL_malloc给BIO分配了内存之后,就简单调用了BIO_set函数进行初始化工作。所以一般来说,除非你要重新设置你已经存在的BIO,否则是不需要直接调用这个函数的。成功操作返回1,否则返回0。 【BIO_free】 该函数释放单个BIO的内存和资源,成功操作返回1,失败返回0。BIO的操作不仅仅是释放BIO结构所占用的资源,也会释放其下层的I/O资源,比如关闭释放相关的文件符等,这对不同类型的BIO是不一样的,详细的请参看各种类型BIO本身的说明文件和源文件。需要注意的是,BIO_free只释放当前的一个BIO,如果用来释放一个BIO链,就可能会导致内存泄漏,这种情况应该使用下述的BIO_free_all函数。 【BIO_vfree】 该函数功能与BIO_free完全相同,只是没有返回值。事实上,它简单调用了BIO_free函数,但不返回该函数的返回值,所以它的函数实现代码只有一个语句。 【BIO_free_all】 该函数释放这个BIO链,并且即使在这个过程中,如果释放其中一个BIO出错,释放过程也不会停止,会继续释放下面的BIO,这保证了尽量避免内存泄漏的出现。如果你非要调用这个函数释放单个的BIO,那么效果跟BIO_free是一样的。事实上,该函数只是简单的遍历整个BIO链,并调用BIO_free释放各个环节的BIO。
20/04/2006
编译内核解决问题
解决之道
在Redhatas3.0上精简并编译系统内核
2.6内核编译
解决之道
因为实际的硬件配置和环境可能不一样,所以不一定能解决问题,但大致的解决思路应该是一样的。 首先当时建cluster时,主节点(Master Node)和从节点(Client Nodes)的硬件配置是一样的。虽然我看oscar4.1的文档说其打包的systemimager版本只支持单一映象文件,所以尽量要求client nodes的配置一样(并没要求主节点),但主节点跟从节点一样配置也可以省很多事(这样不用修改用oscar生成的client node的image文件中关于硬件配置方面的东西,比如硬盘类型,如何分区和用什么文件系统)。 另外一点非常重要的是看client nodes用pxe网络启动时出错信息前面的一段(看看加注释的部分) Start_syslogdget_boel_binaries_tarball rsync -av 192.168.1.1::boot/i386/standard/boel_binaries.tar.gz /tmp/boel_binaries receiving file list... done boel_binaries.tar.gz wrote 116 bytes read 3370446 bytes 674116400 bytes/sec total size is 3369935 speedup is 1.00 tmpfs_watcher get_scripts_directory rsync -a 192.168.1.1::scripts/ /scripts/ autodetect_hardware_and_load_modules Detecting hardware: spurious 8259A interrupt: IRQ7 ide-scsi Loading sd_mod... using /lib/modules/2.4.25-boel/kernel/drivers/scsi/scsi_mod.o SCSI subsystem driver Revision:1.00 Using /lib/modules/2.4.25-boel/kernel/drivers/scsi/sd_mod.o Loading ide_disk... Using /lib/modules/2.4.25-boel/kernel/drivers/ide/ide_disk.o Loading ide_scsi... Assuming ide_scsi is compiled into the kernel, not needed, or already loaded. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 上面这一段会报告硬件检测和模块加载的输出信息,一般只要驱动硬盘的模块没有加载,会导致后面的问题 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ get_hostname_by_hosts_file. Hosts file exists... searching for the machine's hostname in /scripts/hosts by IP: 192.168.1.2 This hosts name is: oscarnode2 run_pre_install_scripts >;>;>;99all.harmless_example_script I live in /var/lib/systemimager/scripts/pre-install choose_autoinstall_script Using autoinstall script:/scripts/oscarnode2.sh write_variables run_autoinstall_script >;>;>;/scripts/oscarnode2.sh get_arch DISKORDER=sd,cciss,ida,rd,hd enumerate_disks DISKS=0 NO DISK DEVICE FILES WERE FOUND. THIS USUALLY MEANS THE KERNEL DID NOT RECOGNIZE ANY OF THE ATTACHED DISKS The kernel boot messages,which preceded this, may indicate why. Reverting to disk configuration specified by image master script. Partitioning /dev/sda... Old partition table for /dev/sda: Error: could not stat device /dev/sda -No such file or directory parted -s -- /dev/sda mklabel msdos || shellout Error: could not stat device /dev/sda -No such file or directory Killing off running processes. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 停在这里时会再现BusyBox built-in shell的提示符,这时可以用#lsmod 命令看看哪些模块加载了,用#modprobe 看看加载必须的设备驱动需要什么模块文件名。 此外,用PXE网络安装Client nodes时,最开始是用systemimager自带的BOEL-2.4.25内核来引导。因此如果这个内核不带有必须的驱动模块就会出现问题。而且这个内核是分为两部分的,一部分为放在/tftpboot/initrd.img中,另一部分为/usr/share/systemimager/boot/i386/standard/boel_binaries.tar.gz文件(这个文件才包含所有的模块,由/tftpboot/initrd.img中的脚本文件./etc/init.d/functions负责解压、硬件检测和模块加载)。如果没有出现找不到的硬件,则后面的client nodes的安装工作仅仅是从Master node拷贝文件及配置了,安装一个client nodes也就两三分钟的时间。
了解了上面的过程就可以根据情况找解决方法了。遇到的问题是client nodes的adaptec 39320b所需要的驱动模块aic79xx.o版本比BOEL-2.4.25带的要高。我当时试了直接将Master node上的aic79xx.o放到boel_binaries.tar.gz文件中,但还是有问题。因为master node上安装的RHEL AS3u3内核版本为2.4.21-20,那个scsi的驱动应该是REDHAT在这个版本下编译的,可能与BOEL- 2.4.25不匹配。后来我从kernel.org下载了2.4.25的内核,将adaptec 39320B的驱动程序源码放到里面,然后新编译了一次内核(到make modules_install就可以了),将编译好的驱动模块放到boel_binaries.tar.gz文件中的想应目录下。最后我解压开/tftpboot/initrd.img,修改了其中的functions脚本的如下部分 # # Detect what we can. # Note: Modules that are needed during the initial boot stages of BOEL should # be copied to the "skel/my_modules" directory prior to creating your # custom initrd.gz . -BEF- # autodetect_hardware_and_load_modules() { echo echo autodetect_hardware_and_load_modules echo -n "Detecting hardware: " # MODULES=`discover --module bridge ethernet ide scsi usb` # echo $MODULES # Prepend with other modules that we will probably need. # MODULES="sd_mod ide-disk $MODULES" MODULES="sd_mod scsi_mod aic79xx" for MODULE in $MODULES do echo echo "Loading $MODULE..." # modprobe $MODULE 2>/dev/null || echo "Assuming $MODULE is compiled into the kernel, not needed, or already #loaded." insmod scsi_mod.o insmod sd_mod.o insmod scsi_mod.o insmod aic79xx.o done } # 修改这个脚本的原因是因为它在用discover检测硬件时总不能正确的检测出client nodes的硬件,所以脚本也不知道该加载什么模块。我只是简单的将检测部份注释掉,用insmod命令强制加载这几个需要的模块。(这一步可能也有其它的解决方法,当时成功之后就忙着解决后面遇到的问题了,也没有进一步探究了:-)
[TOP]
在Redhatas3.0上精简并编译系统内核
我们在安装LUNUX的过程中实际上有大量的,我们并不需要的模块被放在LINUX的系统内核中,并且每次在系统启动时这些没有的内核模块(在特定的需求下)会加载到系统内核中,这样以来不仅有大量的内存被占用,更为严重的是系统内核的运行效率会大打折扣。 下面是我精简和编译系统内核的实践过程。
1.编译环境说明: 在这里特别说明的是在编译系统内核之前一定要装全系统的编译环境。一般情况下在安装LINUX时选上KernelDevelopment和DevelopmentTools就可以了。如果当时没有选装上请再单独安装,否则编译系统内核的过程会出错而不能进行。
另外我们编译内核时一定要在本地登陆,不能通过远程的控制台登陆到系统上进行操作。
2.去掉多余的内核模块:
#su- #cd/usr/src/linux-2.4 #makemrproper如果是新装的系统可省略这个 #makemenuconfig
如果你不需要并口设备支持(如传统的打印机),注销这个选项:
Parallelportsupport--->;
如果你使用的IDE硬盘(会影响USB设备的使用),注销这个选项:
SCSIsupport--->;
如果你没有IEEE1394(火线)设备,注销这个选项:
IEEE1394(FireWire)support--->;
如果你的网络中没有下列需求,注销这个选项的下列子选项:
Networkdevicesupport--->;
FDDIdriversupport光纤网络 Ethernet(1000Mbit)1000M网络 ATMdriversATM网络 PCMCIAnetworkdevicesupport笔记本计算机支持 WirelessLAN(non-hamradio)无线网络 TokenRingdevices令牌环网络
如果你不想用LINUX架设业余电台,注销这个选项:
AmateurRadiosupport--->;
如果你不想使用红外线设备,注销这个选项:
IrDA(infrared)support--->;
如果你没有ISDN线路,注销这个选项:
ISDNsubsystem--->;
如果你没有可以支持I20(PIC的超集)的设备,注销这个选项:
I2Odevicesupport--->;
如果你只想让LINUX安静的工作,注销这个选项:
Sound--->;
如果你没有USB接口设备并厌恶USB设备,注销这个选项:
USBsupport--->;
如果你没有IBM的蓝牙设备(市场这种产品非常少),注销这个选项:
Bluetoothsupport--->;
如果你有其他的内核需求,查找相关的内核选项:
3.开始内核编译:
#makedep大约需要5分钟 #makeclean大约需要5秒 #makebzImage大约需要10分钟 #makemodules大约需要30分钟 #makemodules_install大约需要1分钟 #reboot
4.精简编译的成果:
我的系统在完成了上述编译后系统内存占用下降了35M,CUP的占用明显下降了, 我的系统服务有: CROND、IMAP、HTTPD、IPTABLES、IRQBALANCE、MYSQLD、NETFS、NETWORK、 POSTFIX、PROFTPD、SGI_FAM、SMB、SSHD、SYSLOG、XFS、XINETD
REBOOT计算机TOP查看: RAM使用:45M CUP使用:0
[TOP]
编译linux 2.6内核
内核升级最好升级到2.6.8以后,之前的kernel好象有offset漏洞.偶就是按这篇文档升级成功的,不过rpm与声卡好象还有点问题! 一 编译前准备 1)下载一份内核源代码,我下的是linux-2.6.7.tar.bz2,你可在如下地址下载它或者是更新的版本. http://kernel.org/pub/linux/kernel/v2.6/ 2) 下载最新版本的module-init-tools( "module-init-tools-3.0.tar.gz" and "modutils-2.4.21-23.src.rpm") http://www.kernel.org/pub/linux/kernel/people/rusty/modules/module-init-tools-3.0.tar.gz http://www.kernel.org/pub/linux/kernel/people/rusty/modules/modutils-2.4.21-23.src.rpm 3)安装module-init-tools. 它会替代depmod [/sbin/depmod]和其他工具. tar -zxvf module-init-tools-3.0.tar.gz cd module-init-tools-3.0 ./configure --prefix=/sbin make make install ./generate-modprobe.conf /etc/modprobe.conf 4)安装modutils-2.4.21-23.src.rpm. 你可能会看到"user rusty and group rusty not existing"的警告. 没关系,你只需强制安装就是了.如果你不对Redhat 9和Redhat 8做这几步, 你将会在"make modules_install"这一步时出现问题. rpm -i modutils-2.4.21-23.src.rpm rpmbuild -bb /usr/src/redhat/SPECS/modutils.spec rpm -Fi /usr/src/redhat/RPMS/i386/modutils-2.4.21-23.i386.rpm 5)解压缩内核源代码.把下载的源代码包放到目录/usr/src下,然后 cd /usr/src tar xvfj linux-2.6.7.tar.bz2 cd linux-2.6.7 二 编译配置 在这一部分涉及几个重要模块的配置请,特别注意.一般用"make menuconfig"命令来配置内核. 输入以上命令后出现一个菜单界面,用户可以对需要的模块.下面着重讲几个重要的配置 1)文件系统 请务必要选中ext3文件系统, File systems---> [*] Ext3 journalling file system support [*] Ext3 Security Labels [*] JBD (ext3) debugging support 以上三项一定要选上,而且要内建(即标*). 这个非常重要,在配置完后一定要检查一下.config文件有没有"CONFIG_EXT3_FS=y"这一项. 如果不是"CONFIG_EXT3_FS=y"而是"CONFIG_EXT3_FS=m",你在运行内核时就会遇上以下错误: pivotroot: pivot_root(/sysroot,/sysroot/initrd) failed 2)网卡驱动 请务必把自己网卡对应的驱动编译进内核,比较普遍的网卡是realtek 8139,以下就是这种网卡的配置,以供参考 Device Drivers---> Networking support---> Ethernet (10 or 100Mbit) ---> <*> RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support (EXPERIMENTAL) <*> RealTek RTL-8139 PCI Fast Ethernet Adapter support 3)声卡驱动 也要选择自己声卡对应的驱动编译进内核,比较普遍的声卡是i810_audio,以下就是这种声卡的配置,以供参考 Device Drivers ---> Sound ---> <*> Sound card support Advanced Linux Sound Architecture ---> <*> Advanced Linux Sound Architecture <*> Sequencer support < > Sequencer dummy client <*> OSS Mixer API <*> OSS PCM (digital audio) API[*] OSS Sequencer API <*> RTC Timer support PCI devices ---> <*> Intel i8x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111 Open Sound System ---> < > Open Sound System (DEPRECATED) 以上三项配置关系到新内核能否正常运行,请备加注意.其他的配置如果不是很了解,大可以按默认的选择. 三 编译过程 按如下命令编译,大概需要一个多小时,大可以好好放松一下 make bzImage make modules make modules_install make install 运行新内核之前,请检查一下/boot/grub/grub.conf的内容,下面的配置可作参考 # grub.conf generated by anaconda # # Note that you do not have to rerun grub after making changes to this file # NOTICE: You have a /boot partition. This means that # all kernel and initrd paths are relative to /boot/, eg. # root (hd0,0) # kernel /vmlinuz-version ro root=/dev/hdc3 # initrd /initrd-version.img #boot=/dev/hdc default=1 timeout=10 splashimage=(hd0,0)/grub/splash.xpm.gz title Red Hat Linux (2.6.7) root (hd0,0) kernel /vmlinuz-2.6.7 ro root=LABEL=/ initrd /initrd-2.6.7.img title Red Hat Linux (2.4.20- root (hd0,0) kernel /vmlinuz-2.4.20-8 ro root=LABEL=/ initrd /initrd-2.4.20-8.img
四 运行内核的常见问题 1)RPM问题
进入编译好的内核后,与RPM相关的命令有些不能使用,并出现下列错误:
rpmdb: unable to join the environment error: db4 error(11) from dbenv->open: Resource temporarily unavailable error: cannot open Packages index using db3 - Resource temporarily unavailable (11) error: cannot open Packages database in /var/lib/rpm no packages
解决方法是执行“export LD_ASSUME_KERNEL =2.2.25”命令,也可以将其写入/etc/bashrc。 2)Sound问题
声音部分的模块名也改变了。我的笔记本原来的声卡驱动是i810_audio,现在已改为snd-intel8x0。因此需要把下面的内容添加到/etc/modprobe.conf中:
alias char-major-14 soundcore alias sound snd-intel8x0 alias sound-slot-0 snd-intel8x0 alias snd-card-0 snd-intel8x0 alias sound-service-0-0 snd-mixer-oss alias sound-service-0-1 snd-seq-oss alias sound-service-0-3 snd-pcm-oss alias sound-service-0-8 snd-seq-oss alias sound-service-0-12 snd-pcm-oss install snd-intel8x0 /sbin/modprobe --ignore-install sound-slot-0 && { /bin/aumix-minimal -f /etc/.aumixrc -L >/dev/null 2>&1; /bin/true; } remove snd-intel8x0 { /bin/aumix-minimal -f /etc/.aumixrc -S >/dev/null 2>&1; /bin/true; }; /sbin/modprobe -r --ignore-remove sound-slot-0
然后执行“modprobe sound”加载声音模块,并使用下列命令检验声卡驱动:
#cat /proc/asound/cards
显示结果如下:
0 [SI7012]: ICH - SiS SI7012 SiS SI7012 at 0xdc00, irq 11
3)VMware问题
解决方法是:
◆ 将/usr/bin/vmware-config.pl中所有的“/proc/ksyms”替换为“/proc/kallsyms”。使用“sed”命令可以达到这个目的。
◆ 重新运行该脚本,使用内核头文件编译新的内核模块。在编译过程中如发生错误,应该进入/usr/lib/vmware/modules/source,使用下面的命令将vmnet.tar解包:
#tar xvf vmnet.tar
◆ 进入vmnet-only目录修改bridge.c文件。将“atomic_add(skb->truesize, &sk->wmem_alloc);”修改为“atomic_add(skb->truesize, &sk->sk_wmem_alloc);”,并用类似的方式将“protinfo”改为“sk_protinfo”。
◆ 再次把vmnet-only目录用下面的命令重新打包为vmmon.tar:
#tar cvf vmmon.tar vmnet-only。
如果按照上面的操作依旧失败,另一解决方法是到http://ftp.cvut.cz/vmware/下载vmware-any-any-updateXX.tar.gz,将其解压到任何目录下,执行其中的runme.pl。
4)USB问题
新的2.6.0内核中使用的USB模块大多数已经改名,因此需要修改/etc/rc.sysinit中对USB子系统初始化的代码。将该文件中所有的“keybdev”改为“usbkbd”、“mousedev”改为“usbmouse”、“/proc/bus/usb”改为“/sys/bus/usb”,并在/etc/init.d/halt中进行同样的修改。此外,还要在/etc/rc.sysinit中找到“needusbstorage”,做如下修改:
needusbstorage= if [ $usb = "1" ]; then needusbstorage=`LC_ALL=C grep -e "^I.*Cls=08" /sys/bus/usb/devices 2>/dev/null` action $"Initializing USB 1.1 host controller: " modprobe ohci-hcd 2> /dev/null action $"Initializing USB HID interface: " modprobe hid 2> /dev/null action $"Initializing USB keyboard: " modprobe usbkbd 2> /dev/null action $"Initializing USB mouse: " modprobe usbmouse 2> /dev/null fi
如果USB总线是2.0的,还需将“ohci-hcd”改为“ehci-hcd”。
5)Sysfs问题
解决方法是:
◆ 建立目录/sys:#mkdir /sys
◆ 在/etc/rc.d/rc.sysinit文件中找到“mount -f /proc”,在其下一行加入“mount -f /sys”。
◆ 同样在/etc/rc.d/rc.sysinit文件中找到“action $"Mounting proc filesystem: " mount -n -t proc /proc /proc”,在其下一行加入“action $"Mounting sysfs filesystem: " mount -n -t sysfs /sys /sys”。
◆ 在/etc/fstab文件中加入“none /sys sysfs defaults 0 0”。
◆ 在/etc/init.d/halt的halt_get_remaining函数中找到“awk ' ~ /^\/$|^\/proc|^\/dev/”,改为“awk ' ~ /^\/$|^\/proc|^\/sys|^\/dev/”。
6)Hotplug(热插拔)问题
内核对热插拔功能的支持与KMOD内核线程有关。
解决方法是将/etc/rc.sysinit中所有的/proc/ksyms替换为/proc/kallsyms。执行如下命令:
#mv /etc/rc.d/rc.sysinit /etc/rc.d/rc.sysinit.bak #sed -e 's/\/proc\/ksyms/\/proc\/kallsyms/g' /etc/rc.d/rc.sysinit.bak > /etc/rc.d/rc.sysinit
7)Glibc问题
用户可以升级Glibc标准库的软件包来解决该问题。因为有些发行版,例如Red Hat 9.0上默认安装的Glibc可能是被Red Hat内核小组修改过的。软件包的下载地址是:
ftp://ftp.rpmfind.net/linux/redhat/updates/9/en/os/i386/glibc-2.3.2-27.9.i386.rpm。
可以连同以下几个软件包一起升级:
ftp://ftp.rpmfind.net/linux/redhat/updates/9/en/os/i386/glibc-common-2.3.2-27.9.i386.rpm
ftp://ftp.rpmfind.net/linux/redhat/updates/9/en/os/i386/glibc-devel-2.3.2-27.9.i386.rpm
ftp://ftp.rpmfind.net/linux/redhat/updates/9/en/os/i386/glibc-utils-2.3.2-27.9.i386.rpm
如果使用“rpm -Uvh glibc*”失败,请用命令“rpm -e”先删除旧的Glibc,然后用命令“rpm -Uvh --force glibc*”强制安装。
其它问题
大家也许还会遇到其它问题,但是无论遇上什么问题都可以依照下列步骤尝试解决:
1.内核组件尽可能编译为模块。执行如下命令可以快速重建内核:
#make all modules_install install
2.软件失败的大多数情况是由于模块名已被更改,而/etc/rc.d/rc.sysinit和/etc/rcX.d/*下的脚本却没有修改这些值而导致的。因此,依次修改相关条目可以改进,但是这也需要相当多的背景知识。如果觉得麻烦,可以把所有加载模块的命令集中在/etc/rc.d/rc.local中。例如:
modprobe eth0 modprobe isofs modprobe loop modprobe vfat
同时修改/etc/modoribe.conf文件。具体可参见“man modoribe.conf”获得更多的帮助信息。
3.如果想知道某模块变更后的名字,可以首先在“make menuconfig”时找到该选项,选择Help找到它的配置名称(CONFIG_*),然后到源代码相关目录下的makefile中寻找CONFIG_*。一般可以找到obj-$(CONFIG_*)一项,其值就是该模块的名字。
附:本文参考文章:http://tech.ccidnet.com/pub/article/c309_a103129_p1.html
[TOP]
04/04/2006
拆解和定制initrd.img
为什么要拆解和定制initrd.img? 因为学校给新机器了,旧的驱动跑不起来,不能浪费新机器阿!
1. 什么是initrd.img,它有什么用?
initrd.img是Linux启动过程中很重要的一个文件,如果你编译内核时将一部分功能编译为可加载模块。如果系统的一些设备的驱动编译为可加载模,那么启动时如果没有指定INITRD=/path_to_initrd.img,那么系统启动或者会失败,或者启动后会有设备无法使用(像网卡或者其它设备)。
比如我的Dell Precision 470 计算机的Adaptec HostRaid 39320B SCSI控制卡驱动就编为可加载模块,如果没指定initrd.img或者指定的initrd.img中并没有包含正确的驱动模块(有些硬件很多系列的驱动都是一个名称,像Adaptec 的一系列Ultra320卡用的驱动模块名称都是aic79xx.o,但当前很多2.4版本内核模块中并不支持较新的39320B驱动),则系统启动时会挂起,并报告"kernel panic: VFS: Unable to mount root fs on 08:06"的错误。
2. 拆解initrd.img
很庆幸initrd.img可以进行拆解,或许这正是设计者高明所在。initrd.img不像通常的以.img为扩展名的ramdisk cramfs文件。它是经过用gzip -9进行压缩过的ramdisk文件。所以,如果直接用#mount initrd.img /temppath -o loop不能mount上,会报告你指定一个文件类型。 所以我拆解它的过程要先将其进行解压缩,然后再mount。下面是我的操作过程,可能有些命令用加些参数的方式更简捷,当我知道后会进行更新。
#cd 假设已经到你的initrd.img文件所在目录(最好先将其备份一个)
#mv initrd.img initrd.gz <--在我的RHEL AS3 U3 /bsh下,不做这一步的话,用gunzip解压时会报告扩展名不对
#gunzip initrd.gz /tmp/initrd <--解压后会生成一个不带扩展名的initrd文件
#mkdir /mnt/tmp
#mv /tmp/initrd /tmp/initrd.img <--将解压出的initrd文件加个.img的扩展名,在我的RHEL AS3 U3 下不做这一步,mount时会出错
#mount /tmp/initrd.img /mnt/tmp -o loop <--mount成功后,/mnt/tmp目录中将能看到initrd.img中的所有文件及目录
#cp -a /mnt/tmp/* /tmp/initrd.new <--拷贝一份方便编辑
#umount /mnt/tmp
#cd /tmp/initrd.new
切换到/tmp/initrd.new目录后,你可以按需要进行编辑。比如更新一些设备驱动模块,或者对其中的一些启动过程中会执行的shell script进行修改。
所有想要的修改完成后,进行打包生成新的initrd.img文件。方法如下:
#mkcramfs /tmp/initrd.new /tmp/newinitrd
#gzip -9 /tmp/newinitrd /tmp/newinitrd.gz
#mv /tmp/newinitrd.gz /tmp/initrd.img
上面的目录可以根据需要进行更改。如果在gnome下,有些解压缩、改名、复制可以直接用右键弹出菜单完成。
3. 定制自已的initrd.img
我修改initrd.img的起因还是归于用的那台Dell precision 470 及要用OSCAR4.1组建一个linux cluster(正在进行中,尚未完成)。OSCAR4.1包中所用的SystemImager32.2-1带的用于网络引导客户机的BOEL-kernel-2.4.25和initrd.img不支持Dell precision 470的Adaptec39320B HostRaid Control Card。此外,在上面装RedHat 9.0后进行内核重编译时,可能因为mkinitrd或者9.0内核版本的原因,生成的img文件无法使用。在此以我实际遇到的问题,主要讲讲更新initrd.img中的硬件驱动模块的方法,抛砖引玉。
模块跟kernel的版本相关,直接的用其它支持你硬件但kernel版本不同的模块进行替换通常不会成功,所以最好到kernel.org上下载所需要的kernel版本的源程序包。我解决我上面的问题的文法就是下载了Linux-2.4.25.tar.gz源程序,并解压到/usr/src目录,之后到adaptec.com网站下载到支持39320B HostRaid的适合linux-2.4版的驱动源程序,并解压替换2.4.25内核源程序中的驱动程序源代码。之后对内核进行重新编译(在本文不详述具体方法,网上有很多相关资料,或许有时间我会写一篇),用make menuconfig时最好直接选上随着原来kernel & initrd.img在一起的config文件。如果不出错,执行make modules_install后就生成了需要的模块(通常在目录/lib/modules/kernel-version)。 之后我用新的/lib/modules/kernel-version/lib/下的modules目录及其文件替换掉旧的initrd.img中的modules目录(当然先得拆解之,方法见第2部分)。
当然initrd.img可修改的部分还有很多,但在此只以自已实际做过的部分进行说明。
Question:
最近想用oscar4.1建个cluster,主节点(master node)安装好了RedHat EL AS3U3,并装好oscar4.1,并配置好dhcpd,pxe服务。利用oscar的wizard在主节点上生成了从节点(slave node)的image文件(oscar调用的systemimager生成image文件)。
之后启动了从节点,并且从节点成功的从主节点获取IP并找到配置文件。之后加载了主节点/tftpboot/目录下的kernel和initrd.img文件。但在后面进行硬件检测和运行自动安装脚本时出错。下面是从显示器上抄下来的输出信息,希望各位有过这方面经验的朋友给指点迷径。
Start_syslogdget_boel_binaries_tarball rsync -av 192.168.1.1::boot/i386/standard/boel_binaries.tar.gz /tmp/boel_binaries receiving file list... done boel_binaries.tar.gz wrote 116 bytes read 3370446 bytes 674116400 bytes/sec total size is 3369935 speedup is 1.00 tmpfs_watcher get_scripts_directory rsync -a 192.168.1.1::scripts/ /scripts/
autodetect_hardware_and_load_modules Detecting hardware: spurious 8259A interrupt: IRQ7 ide-scsi Loading sd_mod... using /lib/modules/2.4.25-boel/kernel/drivers/scsi/scsi_mod.o SCSI subsystem driver Revision:1.00 Using /lib/modules/2.4.25-boel/kernel/drivers/scsi/sd_mod.o Loading ide_disk... Using /lib/modules/2.4.25-boel/kernel/drivers/ide/ide_disk.o Loading ide_scsi... Assuming ide_scsi is compiled into the kernel, not needed, or already loaded.
get_hostname_by_hosts_file. Hosts file exists... searching for the machine's hostname in /scripts/hosts by IP:192.168.1.2 This hosts name is: oscarnode2 run_pre_install_scripts >;>;>;99all.harmless_example_script I live in /var/lib/systemimager/scripts/pre-install choose_autoinstall_script Using autoinstall script:/scripts/oscarnode2.sh write_variables run_autoinstall_script >;>;>;/scripts/oscarnode2.sh get_arch DISKORDER=sd,cciss,ida,rd,hd enumerate_disks DISKS=0 NO DISK DEVICE FILES WERE FOUND. THIS USUALLY MEANS THE KERNEL DID NOT RECOGNIZE ANY OF THE ATTACHED DISKS
The kernel boot messages,which preceded this, may indicate why. Reverting to disk configuration specified by image master script.
Partitioning /dev/sda... Old partition table for /dev/sda: Error: could not stat device /dev/sda -No such file or directory parted -s -- /dev/sda mklabel msdos || shellout Error: could not stat device /dev/sda -No such file or directory Killing off running processes.
安装时停在这了,并出现BusyBox built-in shell的提示符 我试着用lsmod 和modprobe aic79xx命令,系统输出如下信息 # lsmod Module Size Used by ide-disk 11520 0 sd_mod 9628 0 (unused) scsi_mod 49780 1 (autoclean) [sd_mod] # modprobe aic79xx Using /lib/modules/2.4.25-boel/kernel/drivers/scsi/scsi_mod.o Using /lib/modules/2.4.25-boel/kernel/drivers/scsi/aic7xxx/aic79xx.o insmod: init_module: aic79xx: No such device
我不明白为什么这个kernel不能成功找到我的scsi硬盘,既然kernel都已经包含有aic79xx.o的模块? 我用google.com搜索了好久也没有找到解决的方法。 很迷惑为什么就不能找到硬盘,要选择什么样的内核才行呢?
Any suggestions are appreciated !
Hardware and software information of master and slave nodes: 2*3.6GHz xeon (smp), 1GB ECC RAM, RedHat EL AS3U3 , Oscar 4.1 Adaptec SCSI control card(driver is aic79xx, vendor: SEAGATE ST336754LW).
01/04/2006
BLOG下载问题睡想
昨晚洗澡,早早躺在床上了,迷迷糊糊之间突然想起关于BLOG搬家的问题,要启用第二空间必定要把现有的blog项搬到新的空间存放,人工完成不是我的风格,能用程序作的我绝不自己来弄。 想着想着竟然来了精神,于是打开手机,登陆到Mobile MSN,下载blog的时候做了一个过滤,发现blog项的地址实际存储的时候应该是使用一个递增数列加上某一个起始数值并于一个固定的URL进行string连接得到整个BLOG URL,对于日志项如此,相关的评论项也应该是这样吧,如果将这些URl遍历一次,就应该获取到BLOG的所有的日志项,而且Microsoft在开发MSN Spaces的时候应该也在静态URL中作了标志项的,如此一来,只要一个程序能够批量读取URL页面即可下载到所有的日志了,但这个程序还是太笨了,因为每一次都是把页面上所有的元素都下载了,为此还需要对下载得到的文件进行字符串过滤,这可不是一件轻松的事情啊,还好HTML元素还是可以进行正则分析的。 因此,编写这个程序只需要一个下载单元,一个过滤引擎(正则表达式分析),似乎C#比较适合,不过这个程序还是比较麻烦的。 换用RSS登陆下载,发现RSS呈现的元素很少,而且大部分是都是我们要找的日志项,这样就变成了在单个HTM文件中过滤,相对而言程序可以简单很多,正则分析也可以变成简单的关键字查找。但是RSS下载存在的一个问题是RSS不能浏览所有的日志项,因此如何使用RSS遍历整个MSN Spaces空间成了最大的问题。 困了,想睡觉了。不管是整体的强制下载还是RSS遍历,都是在没有BLOG的协议,相关文档支持的情况下作出的一种设想,对于BLOG而言应该还存在一些标示性的东西,可以参考一些文档,应该有更好的解决方案的。 好了,呼呼了 P.S 小熊去写程序吧^_^
嗯,刚刚又想到一个问题,下载到的字符流的编码问题关于,zz一篇文章留着讨论
每一个软件开发人员绝对必须掌握的关于 Unicode 和字符集的最基础的知识
关< <P><FONT style="FONT-SIZE: 9pt; FILTER: glow(color=black); WIDTH: 100%; COLOR: #ffffff; LINE-HEIGHT: 150%"><B>每一个软件开发人员绝对必须掌握的关于 Unicode 和字符集的最基础的知识</B></FONT><BR> </P> 键字: Unicode, Character Set, 字符集, UTF-8, ANSI, ASCII, UTF-7 原文标题: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets(No Excuses!) 原文链接: http://www.joelonsoftware.com/printerFriendly/articles/Unicode.html 作者: Joel Spolsky 翻译、摘要: 木野狐(ChenRong2003[at]hotmail.com) 日期: 2004-11-29
ASCII 码 ------------------------------------------------------------------------------------ 7 位(00~7F)。 32 ~ 127 表示字符。32 是空格, 32 以下是控制字符(不可见)。 第8位没有被使用。全世界很多人同时对这个位的含义发展了不同的用处。比如 IBM PC 中的 OEM 字符集。 最后就 128 位以下的用处达成共识,制定了 ASCII 标准。 而 128 位以上的可能有不同的解释,这些不同的解释就叫做 code pages. 甚至有用于在同一台电脑上解释多种语言的 code page.
同时,在亚洲发生了更加疯狂的事情。亚洲语言的字符集通常数以千计, 8 位已经不足以表达,这通常用一种 很凌乱的,叫做 DBCS(双字节字符集,double byte character set) 的系统来解决。 这种系统中,有些字符占用 1 字节,有些 2 字节。这样一来,在字符串中向前解析很容易,而倒退却很麻烦。 程序员们被建议,不要使用 s++ 或 s-- 来前进和后退,而使用一些函数,比如 Windows 的 AnsiNext 和 AnsiPrev. 因为这些函数知道是怎么回事。
这些不同的假设(code page)在单个的机器上没有问题。而随着 Internet 的发展,字符串要从一个机器上移到 另一个机器上,这就产生了问题。于是, Unicode 出现了。
Unicode --------------------------------------------------------------------------------------- Unicode 是一个勇敢的成就。它把在这个星球上的每一个合理的文字系统整合成了一个单一的字符集。 很多人还存在这样的误解: Unicode 仅仅是 16 位的这么简单,每个字符占 16 位,所以一共有 65536 个可能的字符。 然而,这是错误的。不过不要紧,因为这是大部分人都会犯的一个普遍的错误。
实际上,Unicode 理解字符的方式是截然不同的,而这是我们必须了解的。 到目前为止,我们都曾经认为:一个字符对应到一些在磁盘上或内存中储存的位(bits). 如: A -> 0100 0001 而在 Unicode 中, 一个字符实际上对应一种叫做 code point 的东西。 比如 A 这个字符,是抽象的(原文:platonic,柏拉图式的,理想的)一个概念。 无论是 Times New Roman 或者 Helvetica 或者其他的什么字体中,都代表同一个字符。但是它和小写的字母 a 不同。 但是在其他的语言,比如希伯莱语(Hebrew) 或者德语(German), 阿拉伯语(Arabian) 中,同一个字母的不同的字形代表的含义是否 相同,是有争议的。经过长时间的争论,这些也终于被确定了。
每一个字母表中的每一个抽象的字母,都被赋予了一个数字,比如 U+0645. 这个叫做 code point. U+ 表示: Unicode, 数字是 16 进制的。 你可以通过 charmap 命令来查看所有这些编码。(Windows 2000/XP 中). 或者访问 Unicode 的网站(http://www.unicode.org) Unicode 中 code point 的数字的大小是没有限制的,而且也早就超过了 65535. 所以不是每个字符都能存储在两个字节中。 那么,一个字符串 "Hello", 在 Unicode 中会表示成 5 个 code points : U+0048 U+0065 U+006C U+006C U+006F 只不过是一些数字。但我们现在还没有提到如何在磁盘或者 Email 中表示这些信息,这就是我们下面要提到的编码(Encoding) 干的事情。
Encodings (编码) ------------------------------------------------------------------------- 最初的 Unicode Encoding, 使用两个字节表示一个字符。那么 "Hello" 表示为: 00 48 00 65 00 6C 00 6C 00 6F 实际上,还有一种表示方式: 48 00 65 00 6C 00 6C 00 6F 00 到底高位字节在前还是低位字节在前面,是两种不同的模式。这要看特定的 CPU 在何种模式下工作的更快。 所以这两种都有。 这就有了两种不同的 Unicode 表示方式了,为了区分,人们又采用了一种奇异的方式: 在每一个 Unicode 字符串的前面,加上 FEFF (这称为 Unicode 字节顺序标志,Unicode Byte Order Mark). 如果你交换高位和低位次序,那么会加上一个 FFFE. 这样,读这个字符串的人才知道要对每两个相邻的字节进行交换。 但在最初的时候,并不是每一个 Unicode 字符串都有这个标志的。
这看起来很不错。可程序员们开始抱怨了,“看看那些零!”。因为有些是美国人,他们使用英语。而英语中很少需要使用 U+00FF 以上的 字符, 有些人无法忍受采用双倍的存储空间来存储每个字符。 基于这些原因,很多人决定忽视 Unicode, 而同时,事情变得更糟了。
然后人们制定了 UTF-8. UTF-8 是用于保存 Unicode code points 的另一套系统。 每一个 U+ 数字,在内存中占用 8 bit. 在 UTF-8 中,任何一个 0~127 的 code point 占用一个字节。 只有 128 以及更大的才占用 2, 3, 直到 6 个字节。 具体如下图所示:
16进制的最小的数 16进制的最大的数 内存中的字节序列 ------------------------------------------------------------------------------ 00000000 0000007F 0vvvvvvv 00000080 000007FF 110vvvvv 10vvvvvv 00000800 0000FFFF 1110vvvv 10vvvvvv 10vvvvvv 00010000 001FFFFF 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv 00200000 03FFFFFF 111110vv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv 04000000 7FFFFFFF 1111110v 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv
这看起来很不错,其中的英文字符和 ASCII 中一样。所以美国人根本没意识到有什么错误。只有世界上的其他国家需要使用高位的字节。 特别的,"Hello" 这个字符串,Unicode code point 为 U+0048 U+0065 U+006C U+006C U+006F, 会被存储为 48 65 6C 6C 6F。 和 ASCII, ANSI, 以及在这个星球上的任何一个 OEM 的字符集中表示的含义都一样。 现在,如果你需要表示重音的字符,或者希腊语,你需要使用多个字节来表示一个 code point. 但美国人不会介意这些。 (UTF-8 还有一个好处就是,老的字符串处理程序使用一个为 0 的字节来表示 null-terminator, 不会截断字符串)
到目前为止已经介绍了三种 Unicode 的表示方法:
传统的双字节表示方法, 称为 UCS-2(因为有 2 个字节) 或者 UTF-16(因为有 16 个位) 而且你还要搞清楚是高位在前的,还是高位在后的 UCS-2.
还有一种就是新的 UTF-8. 如果你的程序只使用英文的话,它仍然会工作正常。
实际上还有一堆的其他办法对 Unicode 进行编码: 有 UTF-7,这种编码方式大部分和 UTF-8 相同,但保证高位一定为 0. 所以如果你必须通过某种 Email 系统传送 Unicode,这些系统认为 7 位足够了,那使用 UTF-7 会正常。 还有 UCS-4, 储存每一个 code point 为 4 个字节。它的优点是每一个字符都保存为同样长的。但很明显,缺点是浪费太多存储空间了。 所以,现在你思考问题要把每一个字符想象成抽象的一个 unicode code point. 而它们同样可以使用任何旧的方式编码。 举例来说,你可以把 Unicode 字符串 Hello (U+0048 U+0065 U+006C U+006C U+006F) 编码(encode)为 ASCII, 或者古老的 OEM 希腊语编码,或者希柏莱 ANSI 编码,等等。而有些字符串不能显示! 也就是说,假如你要表示一个在某个编码中没有对应的 Unicode code point, 通常会显示为一个 ? 或者一个白色的小方框。
英文常用的一些编码有, Windows-1252(Windows 9x 标准 for 西欧语言) 以及 ISO-8859-1, aka Latin-1(对任何西欧语言也有效) 如果用这些编码来尝试存储俄文字符,你会得到一堆的 ?
UTF 7, 8, 16 以及 32 都有一个优点,能够正确的存储任何的 code point.
最简单,也是最重要的几个概念 ==================================================================== 一个字符串不指定它使用什么编码是没有意义的。 再也不要假定, “纯”文本(plain text) 是 ASCII. 没有 “纯文本” 这个东西。
如果你有一个字符串,在内存中,在文件中,或者在 Email 消息里,你必须知道它的编码是什么。否则你无法正确的解释或者显示给用户。 所有的诸如 “我的网页不能正常显示了”,或者 ”Email 消息不能正常显示了“ 之类的愚蠢问题, 都是因为, 没有告诉你到底是使用的那种编码, UTF-8 还是 ASCII 还是 ISO 8859-1 或者 Windows 1252 ?? 那么自然无法正常的解释和显示,甚至不知道字符串该在哪里结束。
那么如何保留这样的编码标志,来表示字符串的编码? 有一些基本的办法。 比如对于 Email 来说,在表单的 header 中加上:
Content-Type:text/plain;charset="UTF-8"
对于 Web 页面来说,原来的做法是, Web 服务器随着 web 页面本身一起,发送一个类似于 Content-Type 的 http header. (不是在 HTML 里面,而是作为一个 response header 在 HTML 页之前发送)
这样做有一个问题。如果你的 Web 服务器同时有多个站点,站点由多个不同的人用不同的语言开发的程序混在一起。那么 Web 服务器将无从得知, 每一个文件是用什么编码方式写的。这样也就无法发送正确的 Content-Type header. 如果你能够在每一个 HTML 文件中记录 Content-Type 信息,那么就很方便了。可这念头似乎也很疯狂,因为你还没有知道用什么编码方式去 读取这个文件,又怎么能读出编码信息呢? 幸好,几乎每一种编码中,对 32~127 的字符都解释的相同。所以你可以在每一个 html 文件中这么写:
<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
但是要注意, 这个 meta 标签必须放在 head 中靠前面的位置才能保证不会出问题。 因为 Web 服务器读到这里的时候,就会停止解析, 然后用读到的这个编码方式重新解析页面。
那么,作为 Web 浏览器来说,如果没有在 meta 标签中或者 http headers 中发现 Content-Type, 会怎么样呢? IE 是这么做的: 先尝试去猜,根据特定的字节出现在各种语言的典型的编码中的频率。 如果编码设定不正常,用户可以通过 View|Encoding 菜单来尝试不同的编码方式。(当然,不是每个人都知道该这样做)
在 VB, COM, Windows NT/2000/XP 中,默认的字符串类型是 UCS-2(2字节)的。 在 C++ 代码中, 我们可以定义字符串为 wchar_t(wide char),同时用 wcs 系列的函数代替 str 系列的函数。 如 wcscat, wcslen, 而不是 strcat, strlen. 在 C 代码中,要创建 UCS-2 字符串的话,只要在前面加一个 "L", 如 L"Hello"
对于 Web 页面,最好统一为使用 UTF-8 编码。 这个编码已经被各种 web 浏览器支持了很多年了。
(完)
27/03/2006
公告
公告1
关于启动第二BLOG空间以及私有域名的通知
鉴于当前学校访问本人BLOG需要切换国内国际用户导致各位访问同学同事先生小姐大哥大姐小弟小妹们的麻烦,本人将启用第二BLOG空间, 同时为了节省大家的输入,已经申请了国际顶级域名 ,正在进行ICP备案,空间具体切换时间请等待我的MAIL通知,敬请关注。
公告2
关于MSN LIVE MESSAGE 的邀请
有需要的网友请留言发信均可,关于MSN LIVE MESSAGE 的相信介绍请自行查阅相关说明。
郭铃
2006.03.27
16/02/2006
可以把整个网络(一个自治系统AS)看成一个王国,这个王国可以分成几个 区(area),现在我们来看看区域内的某一个人(你所在的机器root)是怎样得到一张 世界地图(routing table)的。
首先,你得跟你周围的人(同一网段如129.102)建立基本联系。你大叫一声 "我在这!"(发HELLO报文),于是,周围的人知道你的存在,他们也会大叫,这样你知道周围大概有哪些人,你与他们之间建立了邻居(neighbor)关系,当然,他们 之间也有邻居关系。
在你们这一群人中,最有威望(Priority优先级)的人会被推荐为首领( Designated Router)首领与你之间是上下级关系(adjacency邻接),它会与你建立单线联系,而不许你与其它邻居有过多交往,他会说:"那样做的话,街上太挤了 "。
你只好通过首领来知道更多的消息了,首先,你们互通消息,他告诉你他知 道的所有地图的地名,你也会告诉他你现知道的地名,当然上也许只有你一个点。 (Database Des cription数据库描述报文)
你发现地名表中有你缺少的或比你新的东西,你会问他要一份更详细的资料 ,他发现你的地名表中有他需要的东西,他也会向你索求新资料。(Link State Request连接状态请求报文)
当然,你们毫不犹豫地将一份详细资料发送给对方。(Link State Update连 接状态升级报文)
收到地图后,互相致谢表示收到了。(Link State Ack连接状态响应报文)
现在,你已经尽你所能得到一份地图(Link State DataBase连接状态数据库 ),你去查找地图把到所有地方的路挑一条最近(shortest path最短路)的,记 为一张表格(routing table路由表),当然以后查这份表格就知道到目的地的一 条最近的路了。地图也要收好,万一表格上的某条路不通了可以通过图去找一条新的路。
其实跟你有联系的,只是周围一群人,外面的消息要通过首领来知道。因为 你的地图是跟首领的一致,我们假设你是首领,你要去画一份世界地图。
你命令所有手下向你通报消息,你可以知道你这一群人的任何一点点小动静 (event事件)。你手下还会有同时属于两群人的家伙(同一区内两网段),他会告诉你另一群人的地图,当然也会把你们这一群人的地图泄露,(不过,无所谓啦 )。这样,整个区的地图你知道了(对于不知道的那也没办法,我们尽力了)。
通过不停地交换地图,现在,整个区的人都有同样的地图了,住在区边境上 的人义不容辞地把这个区的地图(精确到每一群人)发送到别的区,把别的区信息 发送进来。国王会把这些边境的人命名为骨干(backbone area)。通过骨干人士 的不懈努力,现在,整个国家的地图你都了解得一清二楚了。
有些人"里通外国"(AS Boundary Router自治系统边界路由器) ,他们知 道一些"出国"(AS External route自治系统外部路由)的路,当然他们会把这些 秘密公之与众(import 引入),通过信息的传递,现在,你已经有一张完整的" 世界地图"了。
OSPF是这样标记最短的路的:对于某个目的地,首先,考虑是否有同一区内部到目的地的路(intra area区域内),如果有,则在其中取一条离你最近的(花 费最小),写进你的表格中,这个目的地可能是到本群体某个人也可能是到其他群 体的,对于经过其他区域的路由,你会不予考虑,跟自己人(同区域)打交道总比与外人(其他区域)打交道好;如果没有本区的路,你只好通过别的区域了(区域 间),你只要在地图上找最近的就是了;如果你发现目的地在国外,你也只能先把 它标记到你的表格上,期待什么时候王国扩张到那,你就可以把它标记到国内地图上了。 15/02/2006
#include <stdio.h> #include <stdlib.h>
struct Test { int a; char b; int c[1]; char *d; }; int main(int argc, char *argv[]) { printf("%d\r\n",(int)(&((struct Test*)0)->b)); printf("%d\r\n",(int)(((struct Test*)0)->c)); printf("%d\r\n",(int)(&((struct Test*)0)->d)); system("PAUSE"); return 0; }
对于不定长结构,统一分配最大内存空间会带来巨大的浪费,而使用 vector 等数据结构效率又会比较低,因此可以使用在结构中插入长度信息的方式,配合不同的内存分配方式
举例:对于 FIB 下发过程中的结构
typedef struct tagFSYN_FibEntry{
ULONG ulVpnId;
UCHAR ucNextHopNum;
UCHAR ucReserve;
USHORT usPrefixLen;
ULONG ulDstIpAddr;
#define FSYN_MAX_NEXT_HOP 8
FSYN_NextHop_S astNextHop[FSYN_MAX_NEXT_HOP];
} FSYN_FibEntry_S;
其中 astNextHop 数组仅仅在等价路由存在的情况下才会大于 1 ,而对于几万条路由的存储下发 统一使用这个结构太浪费空间了 因此修改为
typedef struct tagAMS_Ipuc_Fib
{
UCHAR ucCountOfNextHopIp;
UCHAR ucPadding;
USHORT usNetMaskLen;
ULONG ulDestIpAdd;
ULONG ulVpnId;
AMS_IPUC_FIBUNIT_S RibInfoUnit[1];
}AMS_IPUC_FIB_S;
其中 ucCountOfNextHopIp 定义实际使用的 astNextHop 个数,在分配空间时采用 pstFIB=(AMS_IPUC_FIB_S *)((UCHAR *)pstFIB+sizeof(AMS_IPUC_FIB_S)+
sizeof(AMS_IPUC_FIBUNIT_S)*(pstFIB->ucCountOfNextHopIp-1));的方式
在使用时就可以直接访问 RibInfoUnit[0 —— ucCountOfNextHopIp-1] 而不会发生数组越界的问题 07/02/2006
主题: . cisco show interface信息解释 分类: 讨论
Router#show interface e0/0 Ethernet0/0 is up, line protocol is down Hardware is AmdP2, address is 0009.4375.5e20 (bia 0009.4375.5e20) Internet address is 192.168.1.53/24 MTU 1500 bytes, BW 10000 Kbit, DLY 1000 usec, reliability 172/255, txload 3/255, rxload 39/255 Encapsulation ARPA, loopback not set Keepalive set (10 sec) ARP type: ARPA, ARP Timeout 04:00:00 Last input never, output 00:00:07, output hang never Last clearing of "show interface" counters never Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 0 Queueing strategy: fifo Output queue :0/40 (size/max) 5 minute input rate 0 bits/sec, 0 packets/sec 5 minute output rate 0 bits/sec, 0 packets/sec 0 packets input, 0 bytes, 0 no buffer Received 0 broadcasts, 0 runts, 0 giants, 0 throttles 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored 0 input packets with dribble condition detected 50 packets output, 3270 bytes, 0 underruns 50 output errors, 0 collisions, 2 interface resets 0 babbles, 0 late collision, 0 deferred 50 lost carrier, 0 no carrier 0 output buffer failures, 0 output buffers swapped out
(1) 接口和活动状态 在上面的显示中,内容表示硬件接口是活动的,而处理行协议的软件过程相信次接口可用。如果路由器操作员拆卸此硬件接口,第一个字段将显示信息is administratively down.如果路由器在活动间隔内收到5000个以上的错误,单词Disabled将出现在此字段中,以显示连路由器自动禁用此端口。行协议字段还显示以前提到的三个描述之一:up 、down、administratively down.如果字段项是up,则表示处理行协议和软件过程相信此接口可用,因为她正在接收keepalives的目的也是如此,其他设备可以确定某个空闲连接是否仍然活动。对于以太网接口,Keepalives的默认值是10s。我们不久将注意到,Keepalives设置可以通过为特定接口使用show interfaces命令来获得。可以用keepalive interface 命令来改变keepalives 设置。此命令的格式如下: Keepalive seconds (2) 硬件字段为你提供接口的硬件类型。在以上的例子中,硬件是CISCO扩展总线(CxBus)以太网,即接口处理器的533-Mbps数据总线。因此,硬件通知我们高速CxBus接口处理器用于支持以太网连接。同时还要注意显示字段包括接口的Mac地址。Mac是48位长的。因为Mac地址的头24位是表示生产厂家ID,所以十六进制数00-10-79是由IEEE分配给Csico的标识符。 (3) Internet地址 如果某个接口是为IP路由配置,那么将为它分配一个Internet地址。此地址后面是他的子网掩码。IP地址是205.141.192.1/24 。反斜杠(/)后面表示此地址的头24位表示网络,他等于子网掩码255.255.255.0。 (4) MTU 最大传输单元(MTU)表示运行在接口上的协议的信息字段所支持的最大字节数。因为以太网桢的信息字段的最大长度是1500字节,所以它的MTU显示为1500字节。对于几乎所有的以太网应用程序,默认的1500字节MTU应该是有效的。对于令牌环,默认的MTU值为8192字节;但是应该注意的一点是RFC1191建议的MTU值为16-Mbps令牌环选择17 914的,而为4-Mbps令牌环选择4464字节。 最小的MTU是64个字节,而最大的值是65535字节。如果IP数据报超过最大的 MTU,将对它进行分段,这将增加额外开销,因为每个最后的数据报都包含它自己的报头。虽然在高速LAN连接中,通常无需担心与分段有关的额外开销,但在低速串行接口上,这可能会是一个比较严重的问题。可以用MTU interface命令来改变默认的MTU,此命令格式如下: mtu bytes 字节数可以是从64~6553。 (5) BW 接口带宽(BW)通常指的是接口的运行速率,用每秒千字节表示。因为以太网运行速率为10Mbps,所以BW值显示为10 000Kb。 可以用Bandwidth命令设置信息带宽值,但实际上不用它来调整接口的带宽,因为对于某些类型的介质,如以太网,带宽是固定的。对于其他的介质,如串行线,通常通过调整硬件来调整其运行速率。例如通过DSU/CSU上设置不同的时钟速率来提高或降低串行接口的运行速率。因此,bandwidth命令主要目的是使当前带宽与高层协议通信。 可以通过以下命令格式设置带宽值,千位表示以千位每秒表示的带宽。 Bandwidth kilobits (6) DLY 此字段表示接口的延迟,用微秒表示。以太网的延迟(DLY)为1000s。可以使用delay interface命令为接口设置延迟值。此命令的格式如下: delay tens-of-microseconds (7) 可靠性 可靠性字段表示接口的可靠性,用255分之几表示。此字段 中所显示的值由在5分钟内的幂平均值计算。因为以太网为每个桢计算CRC,所以可靠性是基于CRC错误率,而不是位错误率。255/255表示接口在5分钟内100%可靠。 虽然没有可靠性命令,可以考虑定期使用的一个重要命令是clear conuter EXEC命令。此命令的功能是清楚或重置接口计数器。此命令的一般格式取决于正在使用的路由器。下面显示的是第二种格式用于Cisco7000系列产品: clear counter [type number] clear counter [type slot/port] type表示特定的接口类型。如果你不指定特定接口,所有接口的计数器都被清除。 (8) 负载 接口上的发送和接收负载均显示为255分之几。与可靠性字段类似,负载字段也是计算5分钟内的幂平均值。从上面可以看出,发送(Txload)负载表示为3/255,而接收(rxload)负载为39/255。因为以太网运行速率为10Mbps,所以可以通过将每分数乘以运行速率来获得接口活动的一般指示。这是因为每个以太网桢都至少有26个额外字节,而当信息字段少于45字节时,将使PAD字符添加到信息字段中。 (9) 封装 此字段表示分配给接口的封装方法。在上面的例子中,封装显示为ARPA,他的标准的以太网2.0版封装方法。其他封装方法还包括IEEE 802.3以太网的关键字iso1,以及IEEE 802.3桢的关键字snap(子网访问协议)桢变异。 (10) 回送 回送字段表示接口是否处于运行的回送模式。如果设置回送,这是当技术人员夜间将接口放入回送接口进行测试,而忘了重置回送时发生的常见问题,这会导致第二天早上会有一些有趣的电话打到控制中心。 可以使用Loopback interface设置命令将接口置于运行的回送模式。Loopback命令没有参数,应使用no Loopback命令删除或禁用回送。以下例子显示了将以太网接口设置为回送模式。 Interface ethernet0/0 Loopback 可以使用show interface loopback EXEC命令查看回送的状态。如果你的路由器有大量的接口,并且技术人员进行定期检测,那么在一大早使用次命令以避免不必要的问题是一个不错的主意。 (11) ARP类型 此字段表示分配的地址解析协议(ARP)类型。在IP环境中,ARP类型是ARPA。默认情况下,以太网接口使用ARPA关键字以指定IP接口上的ARPA封装。可以通过使用arp interface 命令将封装更改为HP PROBE或SNAP,此命令格式如下: arp {arpa/probe/snap} 请注意HP Probe被IOS用于试图解析IEEE802.3或以太网本地数据连路地址。应将ARP类型设为probe,以使得一个或多个路由器接口透明地与使用称为”虚拟地址请求和回复”的地址解析技术的HP IEEE802.3 主机通信。 (12) ARP 超时 此字段表示当非活动时,ARP项在清洗之前保留于缓存中的时间长度。ARP超时的默认值为4个小时,如上面例子所示: 可以通过使用ARP timeout命令调整 ARP缓存项在缓存中的时间长度。此命令格式如: arp timeout seconds (13) 最后的输入和输出 此字段表示最后一个分组或侦被接口成功接收或发送以来的小时、分钟和秒数。可以使用此字段中的值确定活动接口是否依然激活或者死接口何时出现故障。关于前者,在第一个show interface 命令指示接口新的最后输出(这还可以指示是否有问题发生)后10秒或1分钟,再输入第二个show interface命令。它还表示如果出现问题,并非由于无法接收分组。例如,上面的例子中,最后一个成功输入发生在2秒之前。如果我们等待几秒,并发布又一个show interface命令,就可以获得对此计数器的更新。 (14) 输出中断 输出中断字段表示自接口由于发送时间太长而进行最后一次重置以来的时间。此字段的值用小时、分钟和秒数指定,或者如果未发生中断(hang)情况,将永不显示。如果自最后一次重置以来的小时数超过24,将显示天数和小时数,直到字段益出。当发生此情形时,将在此字段中显示星号(*)。 (15) 最后一次清除 此字段表示测量累计统计信息的接口计数器最后一次被重置为0的时间。清除会影响几乎所以的统计信息,除了诸如负载和可靠性等路由统计信息之外。 最后一次清除所显示的实际值是基于32位ms计数器的使用。显示星号表示经过的时间太长无法显示,而显示0:00:00表示计数器在2的31次幂ms到2的32次幂ms之前清除。在许多路由器上最后一次清除值将以星期和月或日和小时表示。例如,在上面的例子里,show interfaces计数器最后一次清除显示为1w2d。 (16) 排队策略 此字段表示分配给接口的配对策略。默认为先入后出(First in first out ,FIFO)。如果以前为接口分配了优先级配对方式,将在此字段中列出此配对方法。 (17) 队列消息 对于输出和输入队列,显示为m/n形式的一队数字,随后是由于队列已满而丢失的分组数。这里替代了m的值表示队列中的分组数,而替代n的值表示用分组表示最大队列大小。通过检查丢失的分组数以及在一段时间内m和n之间的关系,就可以确定是否需要建议对特定接口的队列长度进行调整以减少丢失的分组。但是,还应考虑与接口相连的介质和使用级别,以确定对输出队列长度进行调试是否有益。使用率高的介质最有可能引起队列中分组的丢失:路由器在传输数据时,将遭遇困难,从而导致输出分组排队,而这反过来导致当输出队列已满,且有其他分组到达以便通过接口传输到介质时出现分组丢失。在输入方,丢失的分组和m和n的较大比值表示路由器正忙于进行其他工作,而无法适时地处理进入的分组。如果次情形持续的时间比较长,则通常表示需要一个更强大的路由器以满足工作需要。通常,此情形可通过许多路由器接口的进入方向上的大量丢失的分组而观察到。 在上面的 show interfaces中队列信息字段值显示目前任一队列中均无分组。而且,虽然输出队列已满而造成63个分组丢失,但没有分组由于输入队列而丢失。后者是一种常见情形,因为大多数路由器(除非配置过度)不应该在处理进入的数据方面有问题。 (18) 5-分钟I/O速率 下一个字段显示在前5分钟通过接口发送和接收的平均位数和平均分组数。当解释在此字段中显示的数据时,必须考虑几个因素。首先,必须考虑接口的运行模式和接口相连的网络的配置。例如,如果接口是LAN接口,则即可以运行在混乱模式,从而度曲LAN上的每一侦,也可以运行在非混乱模式,即仅读取广播榛和直接投递到接口的桢。 如果端口处于混乱模式,则读取所有的分组,并提供一种测试在网络中流动的数据的方法。如果接口不处于混乱状态,则仅对她发送和接收的流量有感觉,这可能只占网络中所有流量的一小部分。 考虑到网络配置,如果接口连接到只有一个站的LAN,如WEB服务器,那么所有的流量将流经路由器的接口。这意味着可以获得一种相对准确的测试网络活动方法,而无需考虑接口所处的模式。 BR>需要考虑的另一个因素是5分钟I/O速率表示5分钟时间常数的幂平均值之一事实。因此,任意一个5分钟I/O速率都是这段时间内每秒流量的大概值。但是4个5分钟的时间跨度所产生的平均值将在20分钟的统一流量的即时速率的2%以内。 因为分组的长度可变,所以每秒位率通常比从传输介质角度检查接口上的活动更有用。在上面的例子中,输入速率1 540 000bps约表示接口运行速率的1/6。你可能会感到奇怪,为什么输入速率比接口输出速率大将近一个数量级,回答在于接口的连接。在这一特定的路由器使用环境中,以太网接口连接到一个只具有一个另外的站(即公司WEB服务器)的10BASE-T LAN。WEB页请求以统一资源定位器(URL)的形式流动,而对URL请求的响应是WEB页;这解释了为什么输入和输出方向上的流量级别不成正比。现在,我们了解了5分钟I/O速率,接下来让我们介绍可为某个接口显示的特定分组的输入和输出信息。 (19) 分组和字节输入 此字段首先表示路由器接收的无错误分组的总数量。其次,它还表示路由器接收的无错误分组的总字节数。 如果用字节数除以分组数,就可以获得字节表示的平均分组长度。此信息可用于为在接口上流动的流量类型提供一般表示。例如,相对短的分组通常传输交互式的查询/响应流量,而相对长的分组通常传输包括WEB页的文件及包含在大多数这些页中的图形。 (20) 无缓冲 无缓冲字段表示接口所接收的、由于路由器缺乏缓冲空间而不得不丢弃的分组数。不要将此缓冲空间与接口的内部缓冲弄混。当出现连续的“无缓冲”情形时,通常表示路由器需要更多的内存。但是,如果定期遇到no buffers值,则可能是由于LAN上的广播风暴或者串行端口上的噪音发作所致。可以通过检查下一字段确定出现无缓冲值的原因是否属于广播风暴所致。 (21) 接收的广播 此字段表示接口所接收的广播或多播分组的总数量。要注意的重要一点是许多广播是自然通信过程的一部分。例如,用于将第三层IP地址解析为第2层Mac地址的ARP取决于发放一个广播,以查询与必须获得的第3层地址相关的第2层地址的LAN的每一站,如此才能正确形成侦来传递分组。同样,在Novell IPX环境中,服务器每30s广播服务声明协议(SAP)分组。这些定义了服务器所提供的服务。 如果你是严格的IP环境,那么更有可能从ARP请求获得一部分广播。如果你具有以来于时间的应用程序,那么确确实实可以通过为运行以来于时间的应用程序将固定项设置为路由器的ARP缓存,从而用一个动作解决两个问题。这样做不仅可以避免路由器必须执行ARP操作,还允许解析过程通过检查内存而发生,这比等待广播的响应快得多。因为数据流量在ARP广播期间中断,所以减少ARP广播能够提高接口的信息传输功能。因为ARP表在路由器内部维护。 (22) Runts Runt是一个错误情形术语,与它相关的分组长度小于某个协议相关的最小长度。在以太网环境中,最小分组长度在适配卡上是64字节,而在LAN上是72字节。因此,如果某个接口接收到以太网分组小于72字节,那么它将是一个错误情形,分组将被丢弃。通常,冲突可以引起Runt的产生,而出现故障的适配卡也可以引起此情形的发生。 (23) Giants Giants是又一个错误情形。它表示分组超过了协议最大分组长度。在以太网环境中,适配卡的最大分组长度是1518字节,而在网络中流动的分组最大长度为1526字节。因此长度(包括前导码和起始界符字段)超过1526字节的分组被视为Giant。这样的分组也会被丢弃,而Giant 数表示由于此情形而丢弃的分组数。导致Giant分组的通常原因是滞后冲突或适配卡出现故障。 (24) Throttles 虽然这样情形很少发生,但是如果路由器察觉缓冲或处理器过载,将关掉它的接收器。这一情形称为Throttles,而实际并非通信问题。相反,它是一个路由器功能问题,要求你检查系统缓冲及处理器的状态。如果使用show interfaces命令时指示有大量的“无缓冲”和Throttle,那么通常表示应考虑给路由器添加内存。