Xiaobo Sun

Eclipse-Unix http://umlfact.berlios.de/~s_xsun/

2008年2月13日

Java annotation

版权声明:本文可以自由转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig)
原文:http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html
关键字:Java,annotation,标注


摘要:
本文针对java初学者或者annotation初次使用者全面地说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作 简单的annotation程序,但是对于一些高级的annotation应用(例如使用自定义annotation生成javabean映射xml文 件)还需要进一步的研究和探讨。涉及到深入annotation的内容,作者将在后文《Java Annotation高级应用》中谈到。

同时,annotation运行存在两种方式:运行时、编译时。上文中讨论的都是在运行时的annotation应用,但在编译时的annotation应用还没有涉及,

一、为什么使用Annotation:

在JAVA应用中,我们常遇到一些需要使用模版代码。例如,为了编写一个JAX-RPC web service,我们必须提供一对接口和实现作为模版代码。如果使用annotation对远程访问的方法代码进行修饰的话,这个模版就能够使用工具自动生成。
另外,一些API需要使用与程序代码同时维护的附属文件。例如,JavaBeans需要一个BeanInfo Class与一个Bean同时使用/维护,而EJB则同样需要一个部署描述符。此时在程序中使用annotation来维护这些附属文件的信息将十分便利 而且减少了错误。

二、Annotation工作方式:

在5.0版之前的Java平台已经具有了一些ad hoc annotation机制。比如,使用transient修饰符来标识一个成员变量在序列化子系统中应被忽略。而@deprecated这个 javadoc tag也是一个ad hoc annotation用来说明一个方法已过时。从Java5.0版发布以来,5.0平台提供了一个正式的annotation功能:允许开发者定义、使用 自己的annoatation类型。此功能由一个定义annotation类型的语法和一个描述annotation声明的语法,读取annotaion 的API,一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成。
annotation并不直接影响代码语义,但是它能够工作的方式被看作类似程序的工具或者类库,它会反过来对正在运行的程序语义有所影响。annotation可以从源文件、class文件或者以在运行时反射的多种方式被读取。
当然annotation在某种程度上使javadoc tag更加完整。一般情况下,如果这个标记对java文档产生影响或者用于生成java文档的话,它应该作为一个javadoc tag;否则将作为一个annotation。

三、Annotation使用方法:

1。类型声明方式:
通常,应用程序并不是必须定义annotation类型,但是定义annotation类型并非难事。Annotation类型声明于一般的接口声明极为类似,区别只在于它在interface关键字前面使用“@”符号。
annotation类型的每个方法声明定义了一个annotation类型成员,但方法声明不必有参数或者异常声明;方法返回值的类型被限制在以下的范 围:primitives、String、Class、enums、annotation和前面类型的数组;方法可以有默认值。

下面是一个简单的annotation类型声明:
清单1:


     /**

      * Describes the Request-For-Enhancement(RFE) that led

      * to the presence of the annotated API element.

      */

     public @interface RequestForEnhancement {

         int     id();

         String synopsis();

         String engineer() default "[unassigned]";

         String date();     default "[unimplemented]";

     }


代码中只定义了一个annotation类型RequestForEnhancement。

2。修饰方法的annotation声明方式:
annotation是一种修饰符,能够如其它修饰符(如public、static、final)一般使用。习惯用法是annotaions用在其它的 修饰符前面。annotations由“@+annotation类型+带有括号的成员-值列表”组成。这些成员的值必须是编译时常量(即在运行时不 变)。

A:下面是一个使用了RequestForEnhancement annotation的方法声明:
清单2:


     @RequestForEnhancement(

         id        = 2868724,

         synopsis = "Enable time-travel",

         engineer = "Mr. Peabody",

         date      = "4/1/3007"

     )

     public static void travelThroughTime(Date destination) { ... }



B:当声明一个没有成员的annotation类型声明时,可使用以下方式:
清单3:


     /**

      * Indicates that the specification of the annotated API element

      * is preliminary and subject to change.

      */

     public @interface Preliminary { }



作为上面没有成员的annotation类型声明的简写方式:
清单4:


     @Preliminary public class TimeTravel { ... }



C:如果在annotations中只有唯一一个成员,则该成员应命名为value:
清单5:


     /**

      * Associates a copyright notice with the annotated API element.

      */

     public @interface Copyright {

         String value();

     }



更为方便的是对于具有唯一成员且成员名为value的annotation(如上文),在其使用时可以忽略掉成员名和赋值号(=):
清单6:


     @Copyright("2002 Yoyodyne Propulsion Systems")

     public class OscillationOverthruster { ... }



3。一个使用实例:
结合上面所讲的,我们在这里建立一个简单的基于annotation测试框架。首先我们需要一个annotation类型来表示某个方法是一个应该被测试工具运行的测试方法。
清单7:


     import java.lang.annotation.*;



     /**

      * Indicates that the annotated method is a test method.

      * This annotation should be used only on parameterless static methods.

      */

     @Retention(RetentionPolicy.RUNTIME)

     @Target(ElementType.METHOD)

     public @interface Test { }



值得注意的是annotaion类型声明是可以标注自己的,这样的annotation被称为“meta-annotations”。

在上面的代码中,@Retention(RetentionPolicy.RUNTIME)这个meta-annotation表示了此类型的 annotation将被虚拟机保留使其能够在运行时通过反射被读取。而@Target(ElementType.METHOD)表示此类型的 annotation只能用于修饰方法声明。

下面是一个简单的程序,其中部分方法被上面的annotation所标注:
清单8:


     public class Foo {

         @Test public static void m1() { }

         public static void m2() { }

         @Test public static void m3() {

             throw new RuntimeException("Boom");

         }

         public static void m4() { }

         @Test public static void m5() { }

         public static void m6() { }

         @Test public static void m7() {

             throw new RuntimeException("Crash");

         }

         public static void m8() { }

     }



Here is the testing tool:



     import java.lang.reflect.*;



     public class RunTests {

        public static void main(String[] args) throws Exception {

           int passed = 0, failed = 0;

           for (Method m : Class.forName(args[0]).getMethods()) {

              if (m.isAnnotationPresent(Test.class)) {

                 try {

                    m.invoke(null);

                    passed++;

                 } catch (Throwable ex) {

                    System.out.printf("Test %s failed: %s %n", m, ex.getCause());

                    failed++;

                 }

              }

           }

           System.out.printf("Passed: %d, Failed %d%n", passed, failed);

        }

     }



这个程序从命令行参数中取出类名,并且遍历此类的所有方法,尝试调用其中被上面的测试annotation类型标注过的方法。在此过程中为了找出哪些方法 被annotation类型标注过,需要使用反射的方式执行此查询。如果在调用方法时抛出异常,此方法被认为已经失败,并打印一个失败报告。最后,打印运 行通过/失败的方法数量。
下面文字表示了如何运行这个基于annotation的测试工具:

清单9:


     $ java RunTests Foo

     Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom

     Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash

     Passed: 2, Failed 2



四、Annotation分类:

根据annotation的使用方法和用途主要分为以下几类:

1。内建Annotation——Java5.0版在java语法中经常用到的内建Annotation:
@Deprecated用于修饰已经过时的方法;
@Override用于修饰此方法覆盖了父类的方法(而非重载);
@SuppressWarnings用于通知java编译器禁止特定的编译警告。

下面代码展示了内建Annotation类型的用法:
清单10:


package com.bjinfotech.practice.annotation;



/**

* 演示如何使用java5内建的annotation

* 参考资料:

* http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html

* http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html

* http://mindprod.com/jgloss/annotations.html

* @author cleverpig

*

*/

import java.util.List;



public class UsingBuiltInAnnotation {

         //食物类

         class Food{}

         //干草类

         class Hay extends Food{}

         //动物类

         class Animal{

                 Food getFood(){

                         return null;

                 }

                 //使用Annotation声明Deprecated方法

                 @Deprecated

                 void deprecatedMethod(){

                 }

         }

         //马类-继承动物类

         class Horse extends Animal{

                 //使用Annotation声明覆盖方法

                 @Override

                 Hay getFood(){

                         return new Hay();

                 }

                 //使用Annotation声明禁止警告

                 @SuppressWarnings({"deprecation","unchecked"})

                 void callDeprecatedMethod(List horseGroup){

                         Animal an=new Animal();

                         an.deprecatedMethod();

                         horseGroup.add(an);

                 }

         }

}



2。开发者自定义Annotation:由开发者自定义Annotation类型。
下面是一个使用annotation进行方法测试的sample:

AnnotationDefineForTestFunction类型定义如下:
清单11:


package com.bjinfotech.practice.annotation;



import java.lang.annotation.*;

/**

* 定义annotation

* @author cleverpig

*

*/

//加载在VM中,在运行时进行映射

@Retention(RetentionPolicy.RUNTIME)

//限定此annotation只能标示方法

@Target(ElementType.METHOD)

public @interface AnnotationDefineForTestFunction{}



测试annotation的代码如下:

清单12:


package com.bjinfotech.practice.annotation;



import java.lang.reflect.*;



/**

* 一个实例程序应用前面定义的Annotation:AnnotationDefineForTestFunction

* @author cleverpig

*

*/

public class UsingAnnotation {

         @AnnotationDefineForTestFunction public static void method01(){}

        

         public static void method02(){}

        

         @AnnotationDefineForTestFunction public static void method03(){

                 throw new RuntimeException("method03");

         }

        

         public static void method04(){

                 throw new RuntimeException("method04");

         }

        

         public static void main(String[] argv) throws Exception{

                 int passed = 0, failed = 0;

                 //被检测的类名

                 String className="com.bjinfotech.practice.annotation.UsingAnnotation";

                 //逐个检查此类的方法,当其方法使用annotation声明时调用此方法

             for (Method m : Class.forName(className).getMethods()) {

                if (m.isAnnotationPresent(AnnotationDefineForTestFunction.class)) {

                   try {

                      m.invoke(null);

                      passed++;

                   } catch (Throwable ex) {

                      System.out.printf("测试 %s 失败: %s %n", m, ex.getCause());

                      failed++;

                   }

                }

             }

             System.out.printf("测试结果: 通过: %d, 失败: %d%n", passed, failed);

         }

}



3。使用第三方开发的Annotation类型
这也是开发人员所常常用到的一种方式。比如我们在使用Hibernate3.0时就可以利用Annotation生成数据表映射配置文件,而不必使用Xdoclet。

五、总结:

1。前面的文字说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程序,但是对于一些高级 的annotation应用(例如使用自定义annotation生成javabean映射xml文件)还需要进一步的研究和探讨。

2。同时,annotation运行存在两种方式:运行时、编译时。上文中讨论的都是在运行时的annotation应用,但在编译时的 annotation应用还没有涉及,因为编译时的annotation要使用annotation processing tool。

涉及以上2方面的深入内容,作者将在后文《Java Annotation高级应用》中谈到。

posted @ 2009-05-14 13:04 Xiaobo Sun 阅读(208) | 评论 (0)编辑 收藏

google tips

=========================================================
GOOGLE不支持通配符,如“*”、“?”等,只能做精确查询,关键字后面的“*”或者“?”会被忽略掉。

GOOGLE对英文字符大小写不敏感,“GOD”和“god”搜索的结果是一样的。

GOOGLE的关键字可以是词组(中间没有空格),也可以是句子(中间有空格),但是,用句子做关键字,必须加英文引号。

示例:搜索包含“long, long ago”字串的页面。
搜索:“"long, long ago"”
结果:已向英特网搜索"long, long ago". 共约有28,300项查询结果,这是第1-10项。搜索用时0.28秒。

注意:和搜索英文关键字串不同的是,GOOGLE对中文字串的处理并不十分完善。比如,搜索“"啊,我的太阳"”,我们希望结果中含有这个句子,事实并非 如此。查询的很多结果,“啊”、“我的”、“太阳”等词语是完全分开的,但又不是“啊 我的 太阳”这样的与查询。显然,GOOGLE对中文的支持尚有欠缺之处。

GOOGLE对一些网路上出现频率极高的词(主要是英文单词),如“i”、“com”,以及一些符号如“*”、“.”等,作忽略处理,如果用户必须要求关键字中包含这些常用词,就要用强制语法“+”。

示例:搜索包含“Who am I ?”的网页。如果用“"who am i ?"”,“Who”、“I”、“?”会被省略掉,搜索将只用“am”作关键字,所以应该用强制搜索。
搜索:“"+who +am +i"”
结果:已向英特网搜索"+who +am +i". 共约有362,000项查询结果,这是第1-10项。搜索用时0.30秒。

注意:英文符号(如问号,句号,逗号等)无法成为搜索关键字,加强制也不行。

==============================================================
inurl语法返回的网页链接中包含第一个关键字,后面的关键字则出现在链接中或者网页文档中。有很多网站把某一类具有相同属性的资源名称显示在目录名称 或者网页名称中,比如“MP3”、“GALLARY”等,于是,就可以用INURL语法找到这些相关资源链接,然后,用第二个关键词确定是否有某项具体资 料。INURL语法和基本搜索语法的最大区别在于,前者通常能提供非常精确的专题资料。

示例:查找MIDI曲“沧海一声笑”。
搜索:“inurl:midi 沧海一声笑”
结果:已搜索有关inurl:midi 沧海一声笑的中文(简体)网页。共约有14项查询结果,这是第1-10项。搜索用时0.01秒。

示例:查找微软网站上关于windows2000的安全课题资料。
搜索:“inurl:security windows2000 site:microsoft.com”
结果:已在microsoft.com内搜索有关 inurl:security windows2000的网页。共约有198项查询结果,这是第1-10项。搜索用时0.37秒。

注意:“inurl:”后面不能有空格,GOOGLE也不对URL符号如“/”进行搜索。GOOGLE对“cgi-bin/phf”中的“/”当成空格处理。

posted @ 2009-05-14 12:41 Xiaobo Sun 阅读(206) | 评论 (0)编辑 收藏

MySQL

1。启动:mysqld --console
2。调试:mysql -u root

posted @ 2009-05-06 17:16 Xiaobo Sun 阅读(189) | 评论 (0)编辑 收藏

void* (like Object in Java)

void   *从本质上讲是一种指针的类型,就像   (char   *)、(int   *)类型一样.但是其又具有  
  特殊性,它可以存放其他任何类型的指针类型:例如:  
                                        char   *array="I   am   the   pointer   of   string";  
                                        void   *   temp;                   //temp可以存放其他任何类型的指针(地址)  
                                        temp=array;                       //   temp   的指针类型  
                                        cout<<array<<endl;  
                                        cout<<temp<<endl;  
                                        cout<<(char   *)temp<<endl;  
  运行结果:  
                                      I   am   the   pointer   of   string  
                                      0x0042510C   (这个值就是array指针变量所存储的值)  
                                      I   am   the   pointer   of   string  
   
   
   
  2.但是不能将void*类型的值赋给其他既定的类型,除非经过显示转换:   例如:  
                                                                    int   a=20;  
                                                                    int   *   pr=&a;  
                                                                      void   *p;  
                                                                      pr=p               //error,不能将空的类型赋给int   *  
 
                                                                      pr=(int   *)p;     //ok,经过转换 

posted @ 2009-02-05 14:07 Xiaobo Sun 阅读(267) | 评论 (0)编辑 收藏

Servlet Lifecycle

begin with first request : web.xml - init
end when container is hsut down: web.xml - destroy

By default setting: each Servlet has a Threadpool to support multithreads.

posted @ 2009-01-09 23:41 Xiaobo Sun 阅读(241) | 评论 (0)编辑 收藏

Java Class Loader

 Class loader priority is bootstrap >extension >application (or system)

1. bootstrap: 主要是负责装载jre/lib下的jar文件,当然,你也可以通过-Xbootclasspath参数定义。该ClassLoader不能被Java代码实例化,因为它是JVM本身的一部分
2. extension: 该ClassLoader是Bootstrap classLoader的子class loader。它主要负责加载jre/lib/ext/下的所有jar文件。只要jar包放置这个位置,就会被虚拟机加载。一个常见的、类似的问题是,你 将mysql的低版本驱动不小心放置在这儿,但你的Web应用程序的lib下有一个新的jdbc驱动,但怎么都报错,譬如不支持JDBC2.0的 DataSource,这时你就要当心你的新jdbc可能并没有被加载。这就是ClassLoader的delegate现象。常见的有log4j、 common-log、dbcp会出现问题,因为它们很容易被人塞到这个ext目录,或是Tomcat下的common/lib目录。
3. application loader: 也称为System ClassLoaer。它负责加载CLASSPATH环境变量下的classes。缺省情况下,它是用户创建的任何ClassLoader的父 ClassLoader,我们创建的standalone应用的main class缺省情况下也是由它加载(通过Thread.currentThread().getContextClassLoader()查看)。

我们实际开发中,用ClassLoader更多时候是用其加载classpath下的资源,特别是配置文件,如ClassLoader.getResource(),比FileInputStream直接。

ClassLoader是一种分级(hierarchy)的代理(delegation)模型。
Delegation:其实是Parent Delegation,当需要加载一个class时,当前线程的ClassLoader首先会将请求代理到其父classLoader,递归向上,如果该 class已经被父classLoader加载,那么直接拿来用,譬如典型的ArrayList,它最终由Bootstrap ClassLoader加载。并且,每个ClassLoader只有一个父ClassLoader。
Class查找的位置和顺序依次是:Cache、parent、self。
Hierarchy: 上面的delegation已经暗示了一种分级结构,同时它也说明:一个ClassLoader只能看到被它自己加载的 classes,或是看到其父(parent) ClassLoader或祖先(ancestor) ClassLoader加载的Classes。
在一个单虚拟机环境下,标识一个类有两个因素:class的全路径名、该类的ClassLoader。

===================Tomcat Class Loading==========================================


posted @ 2008-12-21 12:23 Xiaobo Sun 阅读(230) | 评论 (0)编辑 收藏

Design Pattern: delegation

 class A {
     void f() { System.out.println("A: doing f()"); }
     void g() { System.out.println("A: doing g()"); }
}

class C {
     // delegation
     A a = new A();

     void f() { a.f(); }
     void g() { a.g(); }

     // normal attributes
     X x = new X();
     void y() { /* do stuff */ }
}

public class Main {
     public static void main(String[] args) {
         C c = new C();
         c.f();
         c.g();
     }
}

posted @ 2008-12-14 09:34 Xiaobo Sun 阅读(276) | 评论 (0)编辑 收藏

Design Pattern: Proxy, dynamic Proxy

代理模式

 Proxy Pattern's 3 roles:

1.  (abstract common)Subject:common interface

2.  ProxySubject:含有the reference to the RealSubject //delegation

3.  RealSubject:实现逻辑的类

类图如下:

图1

Java 动态代理

从JDK1.3开始,Java就引入了动态代理的概念。动态代理(Dynamic Proxy)可以帮助你减少代码行数,真正提高代码的可复用度。

 类图如下:

图2 

动态代理和普通的代理模式的区别,就是动态代理中的代理类是由java.lang.reflect.Proxy类在运行期时根据接口定义,采用Java反射功能动态生成的(图2的匿名实现类)。和java.lang.reflect.InvocationHandler结合,可以加强现有类的方法实现。如图2,图中的自定义Handler实现InvocationHandler接口,自定义Handler实例化时,将实现类传入自定义Handler对象。自定义Handler需要实现invoke方法,该方法可以使用Java反射调用实现类的实现的方法,同时当然可以实现其他功能,例如在调用实现类方法前后加入Log。而Proxy类根据Handler和需要代理的接口动态生成一个接口实现类的对象。当用户调用这个动态生成的实现类时,实际上是调用了自定义Handler的invoke方法。

下面是使用动态代理的步骤:

1.  Client向Proxy请求一个具有某个功能的实例;

2.  Proxy根据Subject,以自定义Handler创建一个匿名内部类,并返回给Client;

3.  Client获取该匿名内部类的引用,调用在Subject接口种定义的方法;

4.  匿名内部类将对方法的调用转换为对自定义Handler中invoke方法的调用

5. invoke方法根据一些规则做处理,如记录log,然后调用SubjectImpl中的方法

 

Examples

 

Here is a simple example that prints out a message before and after a method invocation on an object that implements an arbitrary list of interfaces:

public interface Foo {
Object bar(Object obj) throws BazException;
}
public class FooImpl implements Foo {
Object bar(Object obj) throws BazException {
// ...
}
}
public class DebugProxy implements java.lang.reflect.InvocationHandler {
private Object obj;
public static Object newInstance(Object obj) {
return java.lang.reflect.Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new DebugProxy(obj));
}
private DebugProxy(Object obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object result;
try {
System.out.println("before method " + m.getName());
result = m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " +
e.getMessage());
} finally {
System.out.println("after method " + m.getName());
}
return result;
}
}

To construct a DebugProxy for an implementation of the Foo interface and call one of its methods:

    Foo foo = (Foo) DebugProxy.newInstance(new FooImpl());
foo.bar(null);


 

posted @ 2008-12-14 09:21 Xiaobo Sun 阅读(381) | 评论 (0)编辑 收藏

Linux 启动

前言
linux有自己一套完整的启动体系,抓住了linux启动的脉络,linux的启动过程将不再神秘。
阅读之前建议先看一下附图。

本文中假设inittab中设置的init tree为:
/etc/rc.d/rc0.d
/etc/rc.d/rc1.d
/etc/rc.d/rc2.d
/etc/rc.d/rc3.d
/etc/rc.d/rc4.d
/etc/rc.d/rc5.d
/etc/rc.d/rc6.d
/etc/rc.d/init.d

目录
1. 关于linux的启动
2. 关于rc.d
3. 启动脚本示例
4. 关于rc.local
5. 关于bash启动脚本
6. 关于开机程序的自动启动




1. 关于linux的启动
init是所有进程之父
init读取/etc/inittab,执行rc.sysinit脚本
(注意文件名是不一定的,有些unix甚至会将语句直接写在inittab中)
rc.sysinit脚本作了很多工作:

init $PATH
config network
start swap function
set hostname
check root file system, repair if needed
check root space
....

rc.sysinit根据inittab执行rc?.d脚本
linux是多用户系统,getty是多用户与单用户的分水岭
在getty之前运行的是系统脚本

2. 关于rc.d
所有启动脚本放置在 /etc/rc.d/init.d下
rc?.d中放置的是init.d中脚本的链接,命名格式是:
S{number}{name}
K{number}{name}
S开始的文件向脚本传递start参数
K开始的文件向脚本传递stop参数
number决定执行的顺序

3. 启动脚本示例

这是一个用来启动httpd的 /etc/rc.d/init.d/apache 脚本:

代码:
#!/bin/bash

source /etc/sysconfig/rc
source $rc_functions

case "$1" in
        start)
                echo "Starting Apache daemon..."
                /usr/local/apache2/bin/apachectl -k start
                evaluate_retval
                ;;

        stop)
                echo "Stopping Apache daemon..."
                /usr/local/apache2/bin/apachectl -k stop
                evaluate_retval
                ;;

        restart)
                echo "Restarting Apache daemon..."
                /usr/local/apache2/bin/apachectl -k restart
                evaluate_retval
                ;;

        status)
                statusproc /usr/local/apache2/bin/httpd
                ;;
                
        *)
                echo "Usage: $0 {start|stop|restart|status}"
                exit 1
                ;;
esac
可以看出他接受start,stop,restart,status参数

然后可以这样建立rc?.d的链接:

代码:
cd /etc/rc.d/init.d &&
ln -sf ../init.d/apache ../rc0.d/K28apache &&
ln -sf ../init.d/apache ../rc1.d/K28apache &&
ln -sf ../init.d/apache ../rc2.d/K28apache &&
ln -sf ../init.d/apache ../rc3.d/S32apache &&
ln -sf ../init.d/apache ../rc4.d/S32apache &&
ln -sf ../init.d/apache ../rc5.d/S32apache &&
ln -sf ../init.d/apache ../rc6.d/K28apache

4. 关于rc.local

经常使用的 rc.local 则完全是习惯问题,不是标准。
各个发行版有不同的实现方法,可以这样实现:

代码:
touch /etc/rc.d/rc.local
chmod +x /etc/rc.d/rc.local
ln -sf /etc/rc.d/rc.local /etc/rc.d/rc1.d/S999rc.local &&
ln -sf /etc/rc.d/rc.local /etc/rc.d/rc2.d/S999rc.local &&
ln -sf /etc/rc.d/rc.local /etc/rc.d/rc3.d/S999rc.local &&
ln -sf /etc/rc.d/rc.local /etc/rc.d/rc4.d/S999rc.local &&
ln -sf /etc/rc.d/rc.local /etc/rc.d/rc5.d/S999rc.local &&
ln -sf /etc/rc.d/rc.local /etc/rc.d/rc6.d/S999rc.local

5. 关于bash启动脚本

/etc/profile
/etc/bashrc
~/.bash_profile
~/.bashrc
是bash的启动脚本

一般用来设置单用户的启动环境,也可以实现开机单用户的程序,但要明确他们都是属于bash范畴而不是系统范畴。

他们的具体作用介绍如下:

/bin/bash这个命令解释程序(后面简称shell)使用了一系列启动文件来建立一个运行环境:

/etc/profile
/etc/bashrc
~/.bash_profile
~/.bashrc
~/.bash_logout
每一个文件都有特殊的功用并对登陆和交互环境有不同的影响。
/etc/profile 和 ~/.bash_profile 是在启动一个交互登陆shell的时候被调用。
/etc/bashrc 和 ~/.bashrc 是在一个交互的非登陆shell启动的时候被调用。
~/.bash_logout 在用户注销登陆的时候被读取

一个交互的登陆shell会在 /bin/login 成功登陆之后运行。一个交互的非登陆shell是通过命令行来运行的,如 [prompt]$/bin/bash。一般一个非交互的shell出现在运行shell脚本的时候。之所以叫非交互的shell,是因为它不在命令行上 等待输入而只是执行脚本程序。
=====================================================================================================
本文以RedHat9.0和i386平台为例,剖析了从用户打开电源直到屏幕出现命令行提示符的整个Linux启动过程。并且介绍了启动中涉及到的各种文件。

  阅读Linux源代码,无疑是深入学习Linux的最好方法。在本文对Linux启动过程的介绍中,我们也尝试从源代码的视角来更深入的剖析Linux的启动过程,所以其中也简单涉及到部分相关的Linux源代码,Linux启动这部分的源码主要使用的是C语言,也涉及到了少量的汇编。而启动过程中也执行了大量的shell(主要是bash shell)所写脚本。为了方便读者阅读,笔者将整个Linux启动过程分成以下几个部分逐一介绍,大家可以参考下图:

  当用户打开PC的电源,BIOS开机自检,按BIOS中设置的启动设备(通常是硬盘)启动,接着启动设备上安装的引导程序lilo 或grub开始引导Linux,Linux首先进行内核的引导,接下来执行init程序,init程序调用了rc.sysinit和rc等程 序,rc.sysinit和rc当完成系统初始化和运行服务的任务后,返回init;init启动了mingetty后,打开了终端供用户登录系统,用户 登录成功后进入了Shell,这样就完成了从开机到登录的整个启动过程。

下面就将逐一介绍其中几个关键的部分:

  第一部分:内核的引导(核内引导)

  Red Hat9.0可以使用lilo或grub等引导程序开 始引导Linux系统,当引导程序成功完成引导任务后,Linux从它们手中接管了CPU的控制权,然后CPU就开始执行Linux的核心映象代码,开始 了Linux启动过程。这里使用了几个汇编程序来引导Linux,这一步泛及到Linux源代码树中的“arch/i386/boot”下的这几个文 件:bootsect.S、setup.S、video.S等。

  其中bootsect.S是生成引导扇区的汇编源码,它完成加载动作后直接跳转到setup.S的程序入口。setup.S的主要功能就是将系 统参数(包括内存、磁盘等,由BIOS返回)拷贝到特别内存中,以便以后这些参数被保护模式下的代码来读取。此外,setup.S还将video.S中的 代码包含进来,检测和设置显示器和显示模式。最后,setup.S将系统转换到保护模式,并跳转到 0x100000。

  那么0x100000这个内存地址中存放的是什么代码?而这些代码又是从何而来的呢?

  0x100000这个内存地址存放的是解压后的内核,因为Red Hat提供的内核包含了众多驱动和 功能而显得比较大,所以在内核编译中使用了“makebzImage”方式,从而生成压缩过的内核,在RedHat中内核常常被命名为vmlinuz,在 Linux的最初引导过程中,是通过"arch/i386/boot/compressed/"中的head.S利用misc.c中定义的 decompress_kernel()函数,将内核vmlinuz解压到0x100000的。

  当CPU跳到0x100000时,将执行"arch/i386/kernel/head.S"中的startup_32,它也是vmlinux 的入口,然后就跳转到start_kernel()中去了。start_kernel()是"init/main.c"中的定义的函 数,start_kernel()中调用了一系列初始化函数,以完成kernel本身的设置。start_kernel()函数中,做了大量的工作来建立 基本的Linux核心环境。如果顺利执行完start_kernel(),则基本的Linux核心环境已经建立起来了。

  在start_kernel()的最后,通过调用init()函数,系统创建第一个核心线程,启动了init过程。而核心线程init()主要 是来进行一些外设初始化的工作的,包括调用do_basic_setup()完成外设及其驱动程序的加载和初始化。并完成文件系统初始化和root文件系 统的安装。

  当do_basic_setup()函数返回init(),init()又打开了/dev/console设备,重定向三个标准的输入输出文件 stdin、stdout和stderr到控制台,最后,搜索文件系统中的init程序(或者由init=命令行参数指定的程序),并使用 execve()系统调用加载执行init程序。到此init()函数结束,内核的引导部分也到此结束了,

第二部分:运行init

  init的进程号是1,从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序,。init程序需要读取配置文件/etc/inittab。inittab是一个不可执行的文本文件,它有若干行指令所组成。在Redhat系统中,inittab的内容如下所示(以“###"开始的中注释为笔者增加的):

  #
# inittab       This file describes how the INIT process should set up
#               the system in a certain run-level.
#
# Author:       Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
#               Modified for RHS Linux by Marc Ewing and Donnie Barnes
#

  # Default runlevel. The runlevels used by RHS are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not havenetworking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
###表示当前缺省运行级别为5(initdefault);
id:5:initdefault:

  ###启动时自动执行/etc/rc.d/rc.sysinit脚本(sysinit)
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

  l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
###当运行级别为5时,以5为参数运行/etc/rc.d/rc脚本,init将等待其返回(wait)
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

  ###在启动过程中允许按CTRL-ALT-DELETE重启系统
# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

  # When our UPS tells us power has failed, assume we have a few minutes
# of power left.  Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly.
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

  # If power was restored before the shutdown kicked in, cancel it.
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"

  ###在2、3、4、5级别上以ttyX为参数执行/sbin/mingetty程序,打开ttyX终端用于用户登录,
###如果进程退出则再次运行mingetty程序(respawn)
# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

  ###在5级别上运行xdm程序,提供xdm图形方式登录界面,并在退出时重新执行(respawn)
# Run xdm in runlevel 5
x:5:respawn:/etc/X11/prefdm -nodaemon

以上面的inittab文件为例,来说明一下inittab的格式。其中以#开始的行是注释行,除了注释行之外,每一行都有以下格式:

  id:runlevel:action:process

  对上面各项的详细解释如下:

  1. id

  id是指入口标识符,它是一个字符串,对于getty或mingetty等其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。

  2. runlevel

  runlevel是init所处于的运行级别的标识,一般使用0-6以及S或s。0、1、6运行级别被系统保留:其中0作为shutdown动 作,1作为重启至单用户模式,6为重启;S和s意义相同,表示单用户模式,且无需inittab文件,因此也不在inittab中出现,实际上,进入单用 户模式时,init直接在控制台(/dev/console)上运行/sbin/sulogin。在一般的系统实现中,都使用了2、3、4、5几个级别, 在Redhat系统中,2表示无NFS支持的多用户模式,3表示完全多用户模式(也是最常用的级别),4保留给用户自定义,5表示XDM图形登录方式。 7-9级别也是可以使用的,传统的Unix系统没有定义这几个级别。runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来 说,仅当runlevel与当前运行级别匹配成功才会执行。

  3. action

  action是描述其后的process的运行方式的。action可取的值包括:initdefault、sysinit、boot、bootwait等:

  initdefault是一个特殊的action值,用于标识缺省的启动级别;当init由核心激活以后,它将读取inittab中的 initdefault项,取得其中的runlevel,并作为当前的运行级别。如果没有inittab文件,或者其中没有initdefault 项,init将在控制台上请求输入runlevel。

  sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的runlevel。

  其余的action(不含initdefault)都与某个runlevel相关。各个action的定义在inittab的man手册中有详细的描述。

  4. process

  process为具体的执行程序。程序后面可以带参数。

  第三部分:系统初始化

  在init的配置文件中有这么一行:

  si::sysinit:/etc/rc.d/rc.sysinit

  它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bash shell的脚本,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。

  rc.sysinit约有850多行,但是每个单一的功能还是比较简单,而且带有注释,建议有兴趣的用户可以自行阅读自己机器上的该文件,以了解系统初始化所详细情况。由于此文件较长,所以不在本文中列出来,也不做具体的介绍。

  当rc.sysinit程序执行完毕后,将返回init继续下一步。

第四部分:启动对应运行级别的守护进程

  在rc.sysinit执行后,将返回init继续其它的动作,通常接下来会执行到/etc/rc.d/rc程序。以运行级别3为例,init将执行配置文件inittab中的以下这行:

  l5:5:wait:/etc/rc.d/rc 5

  这一行表示以5为参数运行/etc/rc.d/rc,/etc/rc.d/rc是一个Shell脚本,它接受5作为参数,去执行/etc /rc.d/rc5.d/目录下的所有的rc启动脚本,/etc/rc.d/rc5.d/目录中的这些启动脚本实际上都是一些链接文件,而不是真正的rc 启动脚本,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。而这些rc启动脚本有着类似的用法,它们一般能接受start、 stop、restart、status等参数。

  /etc/rc.d/rc5.d/中的rc启动脚本通常是K或S开头的链接文件,对于以以S开头的启动脚本,将以start参数来运行。而如果 发现存在相应的脚本也存在K打头的链接,而且已经处于运行态了(以/var/lock/subsys/下的文件作为标志),则将首先以stop为参数停止 这些已经启动了的守护进程,然后再重新运行。这样做是为了保证是当init改变运行级别时,所有相关的守护进程都将重启。

  至于在每个运行级中将运行哪些守护进程,用户可以通过chkconfig或setup中的"System Services"来自行设定。常见的守护进程有:

  amd:自动安装NFS守护进程
apmd:高级电源管理守护进程
arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和IP地址对数据库
autofs:自动安装管理进程automount,与NFS相关,依赖于NIS
crond:Linux下的计划任务的守护进程
named:DNS服务器
netfs:安装NFS、Samba和NetWare网络文件系统
network:激活已配置网络接口的脚本程序
nfs:打开NFS服务
portmap:RPC portmap管理器,它管理基于RPC服务的连接
sendmail:邮件服务器sendmail
smb:Samba文件共享/打印服务
syslog:一个让系统引导时起动syslog和klogd系统日志守候进程的脚本
xfs:X Window字型服务器,为本地和远程X服务器提供字型集
Xinetd:支持多种网络服务的核心守护进程,可以管理wuftp、sshd、telnet等服务

  这些守护进程也启动完成了,rc程序也就执行完了,然后又将返回init继续下一步。

第五部分:建立终端

  rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。init接下来会打开6个终端,以便用户登录系统。通过按Alt+Fn(n对应1-6)可以在这6个终端中切换。在inittab中的以下6行就是定义了6个终端:

  1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

  从上面可以看出在2、3、4、5的运行级别中都将以respawn方式运行mingetty程序,mingetty程序能打开终端、设置模式。同时它会显示一个文本登录界面,这个界面就是我们经常看到的登录界面,在这个登录界面中会提示用户输入用户名,而用户输入的用户将作为参数传给login程序来验证用户的身份。

  第六部分:登录系统,启动完成

  对于运行级别为5的图形方式用户来说,他们的登录是通过一个图形化的登录界面。登录成功后可以直接进入KDE、Gnome等窗口管理器。而本文主要讲的还是文本方式登录的情况:

  当我们看到mingetty的登录界面时,我们就可以输入用户名和密码来登录系统了。

Linux的账号验证程序是 login,login会接收mingetty传来的用户名作为用户名参数。然后login会对用户名进行分析:如果用户名不是root,且存在/etc /nologin文件,login将输出nologin文件的内容,然后退出。这通常用来系统维护时防止非root用户登录。只有/etc /securetty中登记了的终端才允许root用户登录,如果不存在这个文件,则root可以在任何终端上登录。/etc/usertty文件用于对 用户作出附加访问限制,如果不存在这个文件,则没有其他限制。

  在分析完用户名后,login将搜索/etc/passwd以及/etc/shadow来验证密码以及设置账户的其它信息,比如:主目录是什么、使用何种shell。如果没有指定主目录,将默认为根目录;如果没有指定shell,将默认为/bin/bash。

  login程序成功后,会向对应的终端在输出最近一次登录的信息(在/var/log/lastlog中有记录),并检查用户是否有新邮件(在 /usr/spool/mail/的对应用户名目录下)。然后开始设置各种环境变量:对于bash来说,系统首先寻找/etc/profile脚本文件, 并执行它;然后如果用户的主目录中存在.bash_profile文件,就执行它,在这些文件中又可能调用了其它配置文件,所有的配置文件执行后后,各种 环境变量也设好了,这时会出现大家熟悉的命令行提示符,到此整个启动过程就结束了。

  希望通过上面对Linux启动过程的剖析能帮助那些想深入学习Linux用户建立一个相关Linux启动过程的清晰概念,进而可以进一步研究Linux接下来是如何工作的。



posted @ 2008-12-10 18:23 Xiaobo Sun 阅读(583) | 评论 (1)编辑 收藏

Tomcat remote debugging with Eclipse (JPDA debugging)

===============Tomcat setting -- enable JPDA debugging================================
>cd %CATALINA_HOME%\bin
>SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5888
>catalina start
================Eclipse Setting===================================================
Debug Configuration->Remote Java Application->new
--Connect--
Name: Debug Tomcat
Project: jpetstore (which project is copied to the tomcat)
Host: localhost
Port: 5888
--Source--
all libs and src (
此时可以看到程序会停在本机设的断点上,要提醒的是我们程序实际是跑在服务器上的,却能使用本机的断点,挺奇妙的!)

posted @ 2008-12-10 17:09 Xiaobo Sun 阅读(579) | 评论 (0)编辑 收藏

spring mvc

spring mvc 这部机器 说白了其实很简单,DispatcherServlet 把 HandlerMapping Controller ViewResolver 3个组件 组装在一起就完成了整个处理过程。

想起来就像人吃饭一样。把饭放入嘴巴,经过喉咙,然后到胃里,处理一下,再接着往下传,到肠子 最后把处理的结果 排泄出来。呵呵,恶心一把  
当然还吸收了一些,这就类似存了一些数据到数据库。说白了还真这么回事。

posted @ 2008-12-09 18:03 Xiaobo Sun 阅读(145) | 评论 (0)编辑 收藏

Ctrl-Z,bg,jobs,fg

Linux/Unix 区别于微软平台最大的优点就是真正的多用户,多任务。因此在任务管理上也有别具特色的管理思想。
我们知道,在 Windows 上面,我们要么让一个程序作为服务在后台一直运行,要么停止这个服务。而不能让程序在前台后台之间切换。而 Linux 提供了 fg 和 bg 命令,让你轻松调度正在运行的任务。

假设你发现前台运行的一个程序需要很长的时间,但是需要干其他的事情,你就可以用 Ctrl-Z ,终止这个程序,然后可以看到系统提示:
[1]+ Stopped /root/bin/rsync.sh
然后我们可以把程序调度到后台执行:(bg 后面的数字为作业号)
#bg 1
[1]+ /root/bin/rsync.sh &
用 jobs 命令查看正在运行的任务:
#jobs
[1]+ Running /root/bin/rsync.sh &
如果想把它调回到前台运行,可以用
#fg 1
/root/bin/rsync.sh
这样,你在控制台上就只能等待这个任务完成了。

posted @ 2008-12-02 15:33 Xiaobo Sun 阅读(299) | 评论 (0)编辑 收藏

Vi: Search and Replace

Vi: Search and Replace

Change to normal mode with <ESC>.

Search (Wraped around at end of file):

Search STRING forward : / STRING.

Search STRING backward: ? STRING.

Repeat search: n

Repeat search in opposite direction: N (SHIFT-n

Replace: Same as with sed, Replace OLD with NEW:

First occurrence on current line: :s/OLD/NEW

Globally (all) on current line: :s/OLD/NEW/g

Between two lines #,#: :#,#s/OLD/NEW/g

Every occurrence in file: :%s/OLD/NEW/g 



posted @ 2008-11-06 17:18 Xiaobo Sun 阅读(1051) | 评论 (0)编辑 收藏

extern

1 基本解释

extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

另外,extern也可用来进行链接指定。
2 问题:extern 变量

在一个源文件里定义了一个数组:

char a[6];

在另外一个文件里用下列语句进行了声明:

extern char *a;

请问,这样可以吗?

答案与分析:

1)、不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的指针并不等价于类型T的数组。extern char *a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern char a[ ]。

2)、例子分析如下,如果a[] = "abcd",则外部变量a=0x61626364 (abcd的ASCII码值),*a显然没有意义,如下图:


显然a指向的空间(0x61626364)没有意义,易出现非法内存访问。

3)、这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。

4)、extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明。

3 问题:extern 函数1

常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?

答案与分析:

如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:

extern int f(); 和int f();

当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。

4 问题:extern 函数2
 
当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会照成系统错误,这种情况应该如何解决?

答案与分析:

目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。以避免这种错误。

宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。
===============================================================================
4.extern "C"的惯用法

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"
{
#include "cExample.h"
}


而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。

笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:
C/C++ code


/* c语言头文件:cExample.h */

#ifndef C_EXAMPLE_H

#define C_EXAMPLE_H

extern int add(int x,int y);

#endif

/* c语言实现文件:cExample.c */

#include
"cExample.h"

int add( int x, int y )

{

return x + y;

}

// c++实现文件,调用add:cppFile.cpp

extern "C"

{

#include
"cExample.h"

}

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

{

add(
2,3);

return 0;

}





如果C
++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。



2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。

笔者编写的C引用C
++函数例子工程中包含的三个文件的源代码如下:



//C++头文件 cppExample.h

#ifndef CPP_EXAMPLE_H

#define CPP_EXAMPLE_H

extern "C" int add( int x, int y );

#endif

//C++实现文件 cppExample.cpp

#include "cppExample.h"

int add( int x, int y )

{

return x + y;

}

/* C实现文件 cFile.c

/* 这样会编译出错:#include "cExample.h"
*/

extern int add( int x, int y );

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

{

add(
2, 3 );

return 0;

}





posted @ 2008-10-30 13:36 Xiaobo Sun 阅读(327) | 评论 (0)编辑 收藏

Windows&Linux copy&paste

Linux : "highlighted" means "copy", "middle mouse" means "paste"

To make Xming's clipboard work
change /etc/init.d/custom.conf

    in [daemon]
        KillInitClients=false

posted @ 2008-10-30 12:07 Xiaobo Sun 阅读(274) | 评论 (0)编辑 收藏

Ref as function param

常引用

常引用声明方式:const 类型标识符 &引用名=目标变量名;
用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为
const,达到了引用的安全性。
【例3】:
int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确
这不光是让代码更健壮,也有些其它方面的需要。
【例4】:假设有如下函数声明:
string foo( );
void bar(string & s);
那么下面的表达式将是非法的:
bar(foo( ));
bar("hello world");
原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是
const 类型的。因此上面的表达式就是试图将一个const 类型的对象转换为非const 类型,
这是非法的。
引用型参数应该在能被定义为const 的情况下,尽量定义为const 。

posted @ 2008-10-23 12:15 Xiaobo Sun 阅读(146) | 评论 (0)编辑 收藏

shell技巧




1.test测试命令
test命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试,
其测试符和相应的功能分别如下:
(1)数值测试:
-eq:等于则为真
-ne:不等于则为真
-gt:大于则为真
-ge:大于等于则为真
-lt:小于则为真
-le:小于等于则为真
(2)字符串测试:
=:等于则为真
!=:不相等则为真
-z 字符串:字符串长度伪则为真
-n 字符串:字符串长度不伪则为真
(3)文件测试:
-e 文件名:如果文件存在则为真
-r 文件名:如果文件存在且可读则为真
-w 文件名:如果文件存在且可写则为真
-x 文件名:如果文件存在且可执行则为真
-s 文件名:如果文件存在且至少有一个字符则为真
-d 文件名:如果文件存在且为目录则为真
-f 文件名:如果文件存在且为普通文件则为真
-c 文件名:如果文件存在且为字符型特殊文件则为真
-b 文件名:如果文件存在且为块特殊文件则为真
另外,Linux还提供了与(“!”)、或(“-o)、非(“-a”)三个逻辑操作符用于将测试条件连接起来,
其优先级为:“!”最高,“-a”次之,“-o”最低。
同时,bash也能完成简单的算术运算,格式如下:
$[expression]
例如:var1=2
var2=$[var1*10+1]
则:var2的值为21。

2.if条件语句
if [ -x /sbin/quotaon ]; then
echo "Turning on Quota for root filesystem"
/sbin/quotaon /
elif [ -x /sbin/quotaon ]; then
/usr/bin/bash
else
echo "ok"
fi

3.for 循环
#!/bin/sh
WORD="a b c d e f g h i j l m n o p q r s t u v w x y z"
for i in $WORD ; do
echo $i
done

#!/bin/sh
FILES=`ls /txt/*.txt`
for txt in $FILES ; do
doc=`echo $txt | sed "s/.txt/.doc/"`
mv $txt $doc
done

4.while和until 循环
#!/bin/sh
while [ -f /var/run/ppp0.pid ] ; do
killall pppd
done

#!/bin/sh
until [ -f /var/run/ppp0.pid ] ; do
sleep 1
done

Shell还提供了true和false两条命令用于建立无限循环结构的需要,
它们的返回状态分别是总为0或总为非0

5.case 条件选择
#!/bin/sh
case $1 in
start | begin)
echo "start something"
;;
stop | end)
echo "stop something"
;;
*)
echo "Ignorant"
;;
esac
case表达式中也可以使用shell的通配符(“*”、“?”、“[ ]”)。

6.无条件控制语句break和continue
break 用于立即终止当前循环的执行,而contiune用于不执行循环中后面的语句
而立即开始下一个循环的执行。这两个语句只有放在do和done之间才有效。

7.函数定义
在shell中还可以定义函数。函数实际上也是由若干条shell命令组成的,
因此它与shell程序形式上是相似的,不同的是它不是一个单独的进程,
而是shell程序的一部分。函数定义的基本格式为:
functionname
{
若干命令行
}
调用函数的格式为:
functionname param1 param2 ……
shell函数可以完成某些例行的工作,而且还可以有自己的退出状态,
因此函数也可以作为if、while等控制结构的条件。
在函数定义时不用带参数说明,但在调用函数时可以带有参数,此时
shell将把这些参数分别赋予相应的位置参数$1、$2、...及$*。

8.命令分组
在shell中有两种命令分组的方法:“()”和“{}”,前者当shell执行()
中的命令时将再创建一个新的子进程,然后这个子进程去执行圆括弧中的命令。
当用户在执行某个命令时不想让命令运行时对状态集合(如位置参数、环境变量、
当前工作目录等)的改变影响到下面语句的执行时,就应该把这些命令放在圆括
弧中,这样就能保证所有的改变只对子进程产生影响,而父进程不受任何干扰;
{}用于将顺序执行的命令的输出结果用于另一个命令的输入(管道方式)。当我们
要真正使用圆括弧和花括弧时(如计算表达式的优先级),则需要在其前面加上转
义符(\)以便让shell知道它们不是用于命令执行的控制所用。

9.信号
trap命令用于在shell程序中捕捉到信号,之后可以有三种反应方式:
(1)执行一段程序来处理这一信号
(2)接受信号的默认操作
(3)忽视这一信号
trap对上面三种方式提供了三种基本形式:
第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,
将执行双引号中的命令串。
trap 'commands' signal-list
trap "commands" signal-list
为了恢复信号的默认操作,使用第二种形式的trap命令:
trap signal-list
第三种形式的trap命令允许忽视信号:
trap " " signal-list
注意:
(1)对信号11(段违例)不能捕捉,因为shell本身需要捕捉该信号去进行内存的转储。
(2)在trap中可以定义对信号0的处理(实际上没有这个信号),shell程序在其终止
(如执行exit语句)时发出该信号。
(3)在捕捉到signal-list中指定的信号并执行完相应的命令之后,如果这些命令没有将
shell程序终止的话,shell程序将继续执行收到信号时所执行的命令后面的命令,这样
将很容易导致shell程序无法终止。
另外,在trap语句中,单引号和双引号是不同的,当shell程序第一次碰到trap语句时,
将把commands中的命令扫描一遍。此时若commands是用单引号括起来的话,那么shell
不会对commands中的变量和命令进行替换,否则commands中的变量和命令将用当时具体
的值来替换。

10. 运行shell程序的方法
执行shell程序的方法有三种:
(1)sh shell程序文件名
格式为:
bash shell 程序文件名
这实际上是调用一个新的bash命令解释程序,而把shell程序文件名作为参数传递给它。
新启动的shell将去读指定的文件,执行文件中列出的命令,当所有的命令都执行完结束。
该方法的优点是可以利用shell调试功能。
(2)sh<shell程序文件名
格式为:
bash<shell 程序文件名
这种方式就是利用输入重定向,使shell命令解释程序的输入取自指定的程序文件。
(3)用chmod命令使shell程序成为可执行的

11. bash程序的调试
bash -选择项 shell程序文件名
几个常用的选择项是:
-e:如果一个命令失败就立即退出
-n:读入命令但是不执行它们
-u:置换时把未设置的变量看作出错
-v:当读入shell输入行时把它们显示出来
-x:执行命令时把命令和它们的参数显示出来
上面的所有选项也可以在shell程序内部用“set -选择项”的形式引用,而“set +选择项”则
将禁止该选择项起作用。如果只想对程序的某一部分使用某些选择项时,则可以将该部分用
上面两个语句包围起来。
1.未置变量退出和立即退出
未置变量退出特性允许用户对所有变量进行检查,如果引用了一个未赋值的变量就终止shell
程序的执行。shell通常允许未置变量的使用,在这种情况下,变量的值为空。如果设置了未
置变量退出选择项,则一旦使用了未置变量就显示错误信息,并终止程序的运行。未置变量退
出选择项为“-u”。
当shell运行时,若遇到不存在或不可执行的命令、重定向失败或命令非正常结束等情况时,如
果未经重新定向,该出错信息会打印在终端屏幕上,而shell程序仍将继续执行。要想在错误发
生时迫使shell程序立即结束,可以使用“-e”选项将shell程序的执行立即终止。
2.shell程序的跟踪
调试shell程序的主要方法是利用shell命令解释程序的“-v”或“-x”选项来跟踪程序的执行。“-v”
选择项使shell在执行程序的过程中,把它读入的每一个命令行都显示出来,而“-x”选择项使shell
在执行程序的过程中把它执行的每一个命令在行首用一个“+”加上命令名显示出来。并把每一个变量
和该变量所取的值也显示出来,因此,它们的主要区别在于:在执行命令行之前无“-v”则打印出命
令行的原始内容,而有“-v”则打印出经过替换后的命令行的内容。
除了使用shell的“-v”和“-x”选择项以外,还可以在shell程序内部采取一些辅助调试的措施。
例如,可以在shell程序的一些关键地方使用echo命令把必要的信息显示出来,它的作用相当于C语
言中的printf语句,这样就可以知道程序运行到什么地方及程序目前的状态。

12. bash的内部命令
bash命令解释程序包含了一些内部命令。内部命令在目录列表时是看不见的,它们由shell本身提供。
常用的内部命令有:echo、eval、exec、export、readonly、read、shift、wait和点(.)。
下面简单介绍其命令格式和功能。
1.echo
命令格式:echo arg
功能:在屏幕上打印出由arg指定的字符串。
2.eval
命令格式:eval args
功能:当shell程序执行到eval语句时,shell读入参数args,并将它们组合成一个新的命令,然后
执行。
3.exec
命令格式:exec 命令 命令参数
功能:当shell执行到exec语句时,不会去创建新的子进程,而是转去执行指定的命令,
当指定的命令执行完时,该进程,也就是最初的shell就终止了,所以shell程序中exec
后面的语句将不再被执行。
4.export
命令格式:export 变量名 或:export 变量名=变量值
功能:shell可以用export把它的变量向下带入子shell从而让子进程继承父进程中的环境变量。
但子shell不能用export把它的变量向上带入父shell。
注意:不带任何变量名的export语句将显示出当前所有的export变量。
5.readonly
命令格式:readonly 变量名
功能:将一个用户定义的shell变量标识为不可变的。不带任何参数的readonly命令将显示出
所有只读的shell变量。
6.read
命令格式:
read变量名表
功能:从标准输入设备读入一行,分解成若干字,赋值给shell程序内部定义的变量。
7.shift语句
功能:shift语句按如下方式重新命名所有的位置参数变量:$2成为$1,$3成为$2……在程序中
每使用一次shift语句,都使所有的位置参数依次向左移动一个位置,并使位置参数“$#”减一,
直到减到0。
8.wait
功能:是shell等待在后台启动的所有子进程结束。Wait的返回值总是真。
9.exit
功能:退出shell程序。在exit之后可有选择地指定一个数字作为返回状态。
10.“.”(点)
命令格式:. Shell程序文件名
功能:使shell读入指定的shell程序文件并依次执行文件中的所有语句。

13. 特殊参数:
1. $*: 代表所有参数,其间隔为IFS内定参数的第一个字元
2. $@: 与*星号类同。不同之处在於不参照IFS
3. $#: 代表参数数量
4. $?: 执行上一个指令的返回值
5. $-: 最近执行的foreground pipeline的选项参数
6. $$: 本身的Process ID
7. $!: 执行上一个背景指令的PID
8. $_: 显示出最後一个执行的命令

posted @ 2008-10-15 13:13 Xiaobo Sun 阅读(158) | 评论 (0)编辑 收藏

pkg-config学习


 首先说下/etc/ld.so.conf:

这个文件记录了编译时使用的动态链接库的路径。
默认情况下,编译器只会使用/lib和/usr/lib这两个目录下的库文件
如果你安装了某些库,比如在安装gtk+-2.4.13时它会需要glib-2.0 >= 2.4.0,辛苦的安装好glib后
没有指定 —prefix=/usr 这样glib库就装到了/usr/local下,而又没有在/etc/ld.so.conf中添加/usr/local/lib这个搜索路径,所以编译gtk+-2.4.13就会出错了 对于这种情况有两种方法解决:
一:在编译glib-2.4.x时,指定安装到/usr下,这样库文件就会放在/usr/lib中,gtk就不会找不到需要的库文件了 对于安装库文件来说,这是个好办法,这样也不用设置PKG_CONFIG_PATH了

二:将/usr/local/lib加入到/etc/ld.so.conf中,这样安装gtk时就会去搜索/usr/local/lib,同样可以找到需要的库
将/usr/local/lib加入到/etc/ld.so.conf也是必须的,这样以后安装东东到local下,就不会出现这样的问题了。
将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择
添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:
/usr/X11R6/lib
/usr/local/lib
/opt/lib

再来看看ldconfig :

它是一个程序,通常它位于/sbin下,是root用户使用的。具体作用及用法可以man ldconfig查到
简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件 缓存到/etc/ld.so.cache 以供使用
因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下/sbin/ldconfig
使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果编译过程中报错,缺少xxx库
我曾经编译KDE时就犯过这个错误,(它需要每编译好一个东东,都要运行一遍),所以切记改动库文件后一定要运行一下ldconfig,在任何目录下运行都可以。

再来说说 PKG_CONFIG_PATH这个变量吧:

经常在论坛上看到有人问”为什么我已经安装了glib-2.4.x,但是编译gtk+-2.4.x 还是提示glib版本太低阿?
为什么我安装了glib-2.4.x,还是提示找不到阿?。。。。。。”都是这个变量搞的鬼。
先说说它是哪冒出来的,当安装了pkgconfig-x.x.x这个包后,就多出了pkg-config,它就是需要PKG_CONFIG_PATH的东东
pkgconfig-x.x.x又是做什么的? 来看一段说明:

The pkgconfig package contains tools for passing the include path and/or library paths to build tools during the make file execution.

pkg-config is a function that returns meta information for the specified library.

The default setting for PKG_CONFIG_PATH is /usr/lib/pkgconfig because of the prefix we use to install pkgconfig. You may add to PKG_CONFIG_PATH by exporting additional paths on your system where pkgconfig files are installed. Note that PKG_CONFIG_PATH is only needed when compiling packages, not during run-time.

其实pkg-config就是向configure程序提供系统信息的程序,比如软件的版本啦,库的版本啦,库的路径啦,等等
这些信息只是在编译其间使用。你可以 ls /usr/lib/pkgconfig 下,会看到许多的*.pc,用文本编辑器打开
会发现类似下面的信息:

prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

glib_genmarshal=glib-genmarshal
gobject_query=gobject-query
glib_mkenums=glib-mkenums

Name: GLib
Description: C Utility Library
Version: 2.4.7
Libs: -L${libdir} -lglib-2.0
Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include

明白了吧,configure就是靠这些信息判断你的软件版本是否符合要求。并且得到这些东东所在的位置,要不去哪里找呀。
不用我说你也知道为什么会出现上面那些问题了吧。

解决的办法很简单,设定正确的PKG_CONFIG_PATH,假如将glib-2.x.x装到了/usr/local/下,那么glib-2.0.pc就会在
/usr/local/lib/pkgconfig下,将这个路径添加到PKG_CONFIG_PATH下就可以啦。并且确保configure找到的是正确的
glib-2.0.pc,就是将其他的lib/pkgconfig目录glib-2.0.pc干掉就是啦。(如果有的话 ^-^)
设定好后可以加入到~/.bashrc中,例如:
PKG_CONFIG_PATH=/opt/kde-3.3.0/lib/pkgconfig:/usr/lib/pkgconfig:/usr/local/pkgconfig:
/usr/X11R6/lib/pkgconfig
[root@NEWLFS ~]#echo $PKG_CONFIG_PATH
/opt/kde-3.3.0/lib/pkgconfig:/usr/lib/pkgconfig:/usr/local/pkgconfig:/usr/X11R6/lib/pkgconfig

从上面可以看出,安装库文件时,指定安装到/usr,是很有好处的,无论是/etc/ld.so.conf还是PKG_CONFIG_PATH
默认都会去搜索/usr/lib的,可以省下许多麻烦,不过从源码包管理上来说,都装在/usr下
管理是个问题,不如装在/usr/local下方便管理
其实只要设置好ld.so.conf,PKG_CONFIG_PATH路径后,就OK啦

posted @ 2008-10-15 12:12 Xiaobo Sun 阅读(3326) | 评论 (1)编辑 收藏

wc - print newline, word, and byte counts for each file

wc

posted @ 2008-10-09 16:11 Xiaobo Sun 阅读(301) | 评论 (0)编辑 收藏

refresh >tail -f /var/log/messages

 refresh  tail -f /var/log/messages

posted @ 2008-10-09 16:08 Xiaobo Sun 阅读(277) | 评论 (0)编辑 收藏

tar -xvf foo.tar, tar -xzvf foo.tar.gz

tar -xvf foo.tar, tar -xzvf foo.tar.gz

posted @ 2008-10-09 16:07 Xiaobo Sun 阅读(375) | 评论 (0)编辑 收藏

ps -aux | grep "lct*"

ps -aux | grep "lct*"

posted @ 2008-10-09 16:07 Xiaobo Sun 阅读(202) | 评论 (0)编辑 收藏

umlenkung >> , 2>

umlenkung >> , 2>

posted @ 2008-10-09 16:06 Xiaobo Sun 阅读(166) | 评论 (0)编辑 收藏

linux kernel version >uname -r

linux kernel  version
 uname -r

posted @ 2008-10-09 16:04 Xiaobo Sun 阅读(279) | 评论 (0)编辑 收藏

Dir size >du -k (kilo byte)

Dir size      du -k (kilo byte)

posted @ 2008-10-09 16:03 Xiaobo Sun 阅读(185) | 评论 (0)编辑 收藏

cd

cd ~ : go to your home directory
cd
- : go to the last directory you were in
cd
.. : go up a directory

posted @ 2008-10-09 15:28 Xiaobo Sun 阅读(204) | 评论 (0)编辑 收藏

Makefile example

#libxml2 and glibmm are not neccessary to compile the main.cpp
#macro
libxml2:=../../libxml2
libxml++:=..
glibmm:=../../glibmm
#adds include directives
CC:=g++
CCINCLUDE:= -I$(libxml++)/include/libxml++-2.6 -I$(libxml++)/lib/libxml++-2.6/include "
        -I$(libxml2)/include -I$(glibmm)/include
#linking
LD:=g++
LDSTDLIBS:= -L$(libxml++)/lib -L$(libxml2)/lib -L$(glibmm)/lib
LDLIBS:= -lxml++-2.6
LDLIBFLAGS:= -shared $(LDLIBS) $(LDSTDLIBS)
LDEXEFLAGS:= $(LDSTDLIBS) $(LDLIBS)
#extending library path
#export VAR:=... means make it effective in the subprocess(Makefile)
#export LD_LIBRARY_PATH:=... doesn't work because it's only effective in the subprocesses, not parentprocess(shell)
#therefore the LD_RUN_PATH which allocates dynamic libs to the exe file must be set and export to take effect in the ld cmd
LD_LIBRARY_PATH:=$(libxml++)/lib:$(LD_LIBRARY_PATH)
export LD_RUN_PATH:=$(LD_LIBRARY_PATH)
#list of source files for building the target
SRC:= main.cpp
OBJ:=$(patsubst %.cpp,%.o,$(filter %.cpp,$(SRC)))
#targets
# $^ everyone behinds the : , and $< first one behinds the :
# $(OBJ): %.o: %.cpp is another common implicit rule is for the construction of .o files out of .cpp
.PHONY: all clean
all: $(OBJ)
        $(LD) $(LDEXEFLAGS) $^ -o $@

$(OBJ): %.o: %.cpp
        $(CC) $(CCINCLUDE) -c $< -o $@
clean:
        rm -f $(OBJ) all *~ \.*.swp

posted @ 2008-10-09 13:55 Xiaobo Sun 阅读(979) | 评论 (3)编辑 收藏

umask

1) 文件的最大权限rwx rwx rwx (777)
2 ) u m a s k值为0 2 2 - - - -w- -w-
3) 目录权限rwx r-x r-x (755) 这就是目录创建缺省权限
4) 文件权限rw- r-- r-- (644) 这就是文件创建缺省权限

posted @ 2008-08-08 11:41 Xiaobo Sun 阅读(160) | 评论 (0)编辑 收藏

TCP: SYN ACK FIN RST PSH URG

三次握手Three-way Handshake

一个虚拟连接的建立是通过三次握手来实现的

1. (B) --> [SYN] --> (A)

假如服务器A和客户机B通讯. 当A要和B通信时,B首先向A发一个SYN (Synchronize) 标记的包,告诉A请求建立连接.

注意: 一个 SYN包就是仅SYN标记设为1的TCP包(参见TCP包头Resources). 认识到这点很重要,只有当A受到B发来的SYN包,才可建立连接,除此之外别无他法。因此,如果你的防火墙丢弃所有的发往外网接口的SYN包,那么你将不 能让外部任何主机主动建立连接。

2. (B) <-- [SYN/ACK] <--(A)

接着,A收到后会发一个对SYN包的确认包(SYN/ACK)回去,表示对第一个SYN包的确认,并继续握手操作.

注意: SYN/ACK包是仅SYN 和 ACK 标记为1的包.

3. (B) --> [ACK] --> (A)

B收到SYN/ACK 包,B发一个确认包(ACK),通知A连接已建立。至此,三次握手完成,一个TCP连接完成

Note: ACK包就是仅ACK 标记设为1的TCP包. 需要注意的是当三此握手完成、连接建立以后,TCP连接的每个包都会设置ACK位

这就是为何连接跟踪很重要的原因了. 没有连接跟踪,防火墙将无法判断收到的ACK包是否属于一个已经建立的连接.一般的包过滤(Ipchains)收到ACK包时,会让它通过(这绝对不是个 好主意). 而当状态型防火墙收到此种包时,它会先在连接表中查找是否属于哪个已建连接,否则丢弃该包

四次握手Four-way Handshake

四次握手用来关闭已建立的TCP连接

1. (B) --> ACK/FIN --> (A)

2. (B) <-- ACK <-- (A)

3. (B) <-- ACK/FIN <-- (A)

4. (B) --> ACK --> (A)

注意: 由于TCP连接是双向连接, 因此关闭连接需要在两个方向上做。ACK/FIN 包(ACK 和FIN 标记设为1)通常被认为是FIN(终结)包.然而, 由于连接还没有关闭, FIN包总是打上ACK标记. 没有ACK标记而仅有FIN标记的包不是合法的包,并且通常被认为是恶意的

连接复位Resetting a connection

四次握手不是关闭TCP连接的唯一方法. 有时,如果主机需要尽快关闭连接(或连接超时,端口或主机不可达),RST (Reset)包将被发送. 注意在,由于RST包不是TCP连接中的必须部分, 可以只发送RST包(即不带ACK标记). 但在正常的TCP连接中RST包可以带ACK确认标记

请注意RST包是可以不要收到方确认的?

无效的TCP标记Invalid TCP Flags

到目前为止,你已经看到了 SYN, ACK, FIN, 和RST 标记. 另外,还有PSH (Push) 和URG (Urgent)标记.

最常见的非法组合是SYN/FIN 包. 注意:由于 SYN包是用来初始化连接的, 它不可能和 FIN和RST标记一起出现. 这也是一个恶意攻击.

由于现在大多数防火墙已知 SYN/FIN 包, 别的一些组合,例如SYN/FIN/PSH, SYN/FIN/RST, SYN/FIN/RST/PSH。很明显,当网络中出现这种包时,很你的网络肯定受到攻击了。

别的已知的非法包有FIN (无ACK标记)和"NULL"包。如同早先讨论的,由于ACK/FIN包的出现是为了关闭一个TCP连接,那么正常的FIN包总是带有 ACK 标记。"NULL"包就是没有任何TCP标记的包(URG,ACK,PSH,RST,SYN,FIN都为0)。

到目前为止,正常的网络活动下,TCP协议栈不可能产生带有上面提到的任何一种标记组合的TCP包。当你发现这些不正常的包时,肯定有人对你的网络不怀好意。

UDP (用户数据包协议User Datagram Protocol)
TCP是面向连接的,而UDP是非连接的协议。UDP没有对接受进行确认的标记和确认机制。对丢包的处理是在应用层来完成的。(or accidental arrival).

此处需要重点注意的事情是:在正常情况下,当UDP包到达一个关闭的端口时,会返回一个UDP复位包。由于UDP是非面向连接的, 因此没有任何确认信息来确认包是否正确到达目的地。因此如果你的防火墙丢弃UDP包,它会开放所有的UDP端口(?)。

由于Internet上正常情况下一些包将被丢弃,甚至某些发往已关闭端口(非防火墙的)的UDP包将不会到达目的,它们将返回一个复位UDP包。

因为这个原因,UDP端口扫描总是不精确、不可靠的。

看起来大UDP包的碎片是常见的DOS (Denial of Service)攻击的常见形式 (这里有个DOS攻击的例子,http://grc.com/dos/grcdos.htm ).

ICMP (网间控制消息协议Internet Control Message Protocol)
如同名字一样, ICMP用来在主机/路由器之间传递控制信息的协议。 ICMP包可以包含诊断信息(ping, traceroute - 注意目前unix系统中的traceroute用UDP包而不是ICMP),错误信息(网络/主机/端口 不可达 network/host/port unreachable), 信息(时间戳timestamp, 地址掩码address mask request, etc.),或控制信息 (source quench, redirect, etc.) 。

你可以在http://www.iana.org/assignments/icmp-parameters中找到ICMP包的类型。

尽管ICMP通常是无害的,还是有些类型的ICMP信息需要丢弃。

Redirect (5), Alternate Host Address (6), Router Advertisement (9) 能用来转发通讯。

Echo (8), Timestamp (13) and Address Mask Request (17) 能用来分别判断主机是否起来,本地时间 和地址掩码。注意它们是和返回的信息类别有关的。 它们自己本身是不能被利用的,但它们泄露出的信息对攻击者是有用的。

ICMP消息有时也被用来作为DOS攻击的一部分(例如:洪水ping flood ping,死 ping ?呵呵,有趣 ping of death)?/p>

包碎片注意A Note About Packet Fragmentation

如果一个包的大小超过了TCP的最大段长度MSS (Maximum Segment Size) 或MTU (Maximum Transmission Unit),能够把此包发往目的的唯一方法是把此包分片。由于包分片是正常的,它可以被利用来做恶意的攻击。

因为分片的包的第一个分片包含一个包头,若没有包分片的重组功能,包过滤器不可能检测附加的包分片。典型的攻击Typical attacks involve in overlapping the packet data in which packet header is 典型的攻击Typical attacks involve in overlapping the packet data in which packet header isnormal until is it overwritten with different destination IP (or port) thereby bypassing firewall rules。包分片能作为 DOS 攻击的一部分,它可以crash older IP stacks 或涨死CPU连接能力。

Netfilter/Iptables中的连接跟踪代码能自动做分片重组。它仍有弱点,可能受到饱和连接攻击,可以把CPU资源耗光。

posted @ 2008-08-07 09:20 Xiaobo Sun 阅读(9660) | 评论 (4)编辑 收藏

gdb

>gdb ./sysprocess
(gdb)set args -id 0 -taskcfg ub900proc
(gdb)r //run
Starting program: ../sysprocess -id 0 -taskcfg ub900proc
..
Program received signal SIGSEGV, Segmentation fault.
(gdb)backtrace //打印当前的函数调用栈的所有信息。
(gdb)frame 7 //查看信息,n是一个从0开始的整数,是栈中的层编号
(gdb)up
(gdb)down
(gdb)quit

posted @ 2008-08-05 15:42 Xiaobo Sun 阅读(170) | 评论 (0)编辑 收藏

#find ./ -type f -name "*.swp"|xargs rm -rf #find . -name "*.cpp" -exec grep "SoapEvMessage" \; -print

 #find ... -exec rm {} \;
 #find ... | xargs rm -rf

两者都可以把find命令查找到的结果删除,其区别简单的说是前者是把find发现的结果一次性传给exec选项,这样当文件数量较多的时候,就 可能会出现“参数太多”之类的错误,相比较而言,后者就可以避免这个错误,因为xargs命令会分批次的处理结果。这样看来,“find ... | xargs rm -rf”是更通用的方法,推荐使用!

rm不接受标准输入,所以不能用find / -name "tmpfile" |rm

-exec   必须由一个   ;   结束,而因为通常   shell   都会对   ;   进行处理,所以用   \;   防止这种情况。  
  {}   可能需要写做   '{}',也是为了避免被   shell   过滤

find ./ -type f -exec grep iceskysl {} /dev/null \;
./表示从当前目录找
-type f,表示只找file,文件类型的,目录和其他字节啥的不要
-exec 把find到的文件名作为参数传递给后面的命令行,代替{}的部分
-exec后便跟的命令行,必须用“ \;”结束

#find ./ -type f -name "*.cpp"|xargs grep "test" -n
#find . -name "*cpp" -exec grep "test" {} \; -print

posted @ 2008-07-29 14:27 Xiaobo Sun 阅读(3054) | 评论 (0)编辑 收藏

Vim cut, copy and paste

posted @ 2008-07-11 08:53 Xiaobo Sun 阅读(360) | 评论 (0)编辑 收藏

GridLayout

Each SWT widget has a (possible) containing area as known as the cell.

horizontalAlignment, verticalAlignment is used to set the widget position
grabExcessHorizontalSpace, grabExcessVerticalSpace is used to set the cell.

posted @ 2008-06-26 11:22 Xiaobo Sun 阅读(272) | 评论 (0)编辑 收藏

View the Listening port under linux: #netstat -nl | grep 3141

netstat -nl | grep 3141

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

nmap -sT -O localhost

cat /etc/services | grep 3141

netstat -anp | grep 3141

lsof -i | grep 3141

posted @ 2008-06-18 08:48 Xiaobo Sun 阅读(327) | 评论 (0)编辑 收藏

LD_LIBRARY_PATH

vim /etc/ld.so.conf
====================
LD_LIBRARY_PATH: defines the path of dynamic libraries used at the run time
LD_RUN_PATH: at compile time tells the exe file what is path of dynamic libraries used at the run time
======================
ldd exe // show the used dynamic libs

posted @ 2008-06-16 13:21 Xiaobo Sun 阅读(397) | 评论 (0)编辑 收藏

Linux cmd : #du -k, #uname -r,# >> 2> , #tail -f, #tar -xzvf

 Dir size
 du -k (kilo byte)
 linux kernel  version
 uname -r
 umlenkung
 >> , 2>
   ps -aux | grep "lct*"
   tar -xvf foo.tar, tar -xzvf foo.tar
 refresh  tail -f /var/log/messages


posted @ 2008-06-15 13:46 Xiaobo Sun 阅读(310) | 评论 (0)编辑 收藏

mysql.exe -u root -p

mysql.exe -u root -p

posted @ 2008-06-08 12:19 Xiaobo Sun 阅读(330) | 评论 (0)编辑 收藏

JSP Scope

There are 4 scopes application, session, request and page in the order of thier significance. 
 
Application represent the ServletContext. The scope is accesible throught out the application. 
 
session represents HTTPSession object. This is valid till the user requests the application. 
 
Request represent the HTTPServletRequest and valdi to a particular request. A request may span a single JSP page or multiple depending upon teh situations. 
 
Page is the least valid scope. It is valid to the particular JSP page only This is some thing like private variable 

posted @ 2008-06-03 16:01 Xiaobo Sun 阅读(365) | 评论 (0)编辑 收藏

wget : #wget -r -np -nd http://example.com/packages/

  1. $ wget -r -np -nd http://example.com/packages/

    这条命令可以下载 http://example.com 网站上 packages 目录中的所有文件。其中,-np 的作用是不遍历父目录,-nd 表示不在本机重新创建目录结构。

  2. $ wget -r -np -nd --accept=iso http://example.com/centos-5/i386/

    与上一条命令相似,但多加了一个 --accept=iso 选项,这指示 wget 仅下载 i386 目录中所有扩展名为 iso 的文件。你也可以指定多个扩展名,只需用逗号分隔即可。

  3. $ wget -i filename.txt

    此命令常用于批量下载的情形,把所有需要下载文件的地址放到 filename.txt 中,然后 wget 就会自动为你下载所有文件了。

  4. $ wget -c http://example.com/really-big-file.iso

    这里所指定的 -c 选项的作用为断点续传。

  5. $ wget -m -k (-H) http://www.example.com/

    该命令可用来镜像一个网站,wget 将对链接进行转换。如果网站中的图像是放在另外的站点,那么可以使用 -H 选项。

posted @ 2008-06-02 17:28 Xiaobo Sun 阅读(672) | 评论 (0)编辑 收藏

Java Stream

stream应该是水龙头里的水资源,
InputStream:是一个出水龙头(把水封装在里头)的一个实物对象,该对象的read方法呢,就想成这
个"出水龙头"这一机制对象的开关钮,你read或openStream(其他对象包容InputStream对象的对象方法)
一下呢,就等于打开了出水龙头的按钮,水就出来了,里头封装的水是什么性质的呢,
你就用相应的容器来装,如string或byte[].....
OutputStream:你就在InputStream基础上反着想就ok了
=====================================================================================
当然,我们可以在Inputstream和OutputStream数据源的基础上,从实际需要触发,
      来重新封装出不同性能机制的输入、输出流了,java.io包中提供了很丰富的输入、输出流对象,如:
     基于字节流的stream:
      DataOutputStream----DataInputStream:
         FileOutputStream-----FileInputStream:
File->inputstream->...
...->outputstream->File

        .............等,可以用InputStream和OutputStream从JDK文档查阅
     基于字符流的stream(典型的以write 和reader来标识的):
      FileWriter---FileReader:
         StringWriter---StringReader:
          .........等,你自己可以用Writer和Reader从JDK文档里头查看说明
======================================================================================

InputStreamReader r = new InputStreamReader(System. in ); // InputStream from console
BufferedReader in = new BufferedReader ( r );
String line ;
while (( line = in . readLine ()) != null) {
  System. out . println ( "> "+line );
}

FileReader r = new FileReader ( args [0]); // InputStream from file
BufferedReader in = new BufferedReader ( r );
String line ;
while (( line = in . readLine ()) != null) {
  System. out . println ( "> "+line );
}

FileWriter w = new FileWriter ( args [0]); //OutputStream to file
BufferedWriter bw = new BufferedWriter (w);
PrintWriter out = new PrintWriter (bw);
out . println ( "dies" );
out . println ( " ... ist ein Log!" );
out . println ( "Ciao!" );
out . close (); // schliessen nicht vergessen !
===================================================================
FileInputStream是InputStream的子类,由名称上就可以知道, FileInputStream主要就是从指定的File中读取资料至目的地。

FileOutputStream是OutputStream的子类,顾名思义,FileOutputStream主要就是从来源地写入资料至指定的File中。

标准输入输出串流物件在程式一开始就会开启,但只有当您建立一个FileInputStream或FileOutputStream的实例时,实际的串流才会开启,而不使用串流时,也必须自行关闭串流,以释放与串流相依的系统资源。

下面这个程式可以复制档案,程式先从来源档案读取资料至一个位元缓冲区中,然后再将位元阵列的资料写入目的档案:

  • FileStreamDemo.java
package onlyfun.caterpillar;

import java.io.*;

public class FileStreamDemo {
public static void main(String[] args) {
try {
byte[] buffer = new byte[1024];

FileInputStream fileInputStream =
new FileInputStream(new File(args[0]));
FileOutputStream fileOutputStream =
new FileOutputStream(new File(args[1]));

System.out.println("复制档案:" +
fileInputStream.available() + "位元组");
while(true) { // 从来源档案读取资料至缓冲区
if(fileInputStream.available() < 1024) {
int remain;
while((remain = fileInputStream.read())
!= -1) {
fileOutputStream.write(remain);
}
break;
}
else {
fileInputStream.read(buffer);
// 将阵列资料写入目的档案
fileOutputStream.write(buffer);
}
}

// 关闭串流
fileInputStream.close();
fileOutputStream.close();

System.out.println("复制完成");
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(
"using: java FileStreamDemo src des");
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
}
}

这个程式示范了两个 read() 方法,一个可以读入指定长度的资料至阵列,一个一次可以读入一个位元组,每次读取之后,读取的指标都会往前进,您使用available()方法获得还有多少位元组可以读取;除了使用File来建立FileInputStream、FileOutputStream的实例之外,您也可以直接使用字串指定路径来建立。

不使用串流时,记得使用close()方法自行关闭串流,以释放与串流相依的系统资源。

posted @ 2008-05-19 15:21 Xiaobo Sun 阅读(954) | 评论 (1)编辑 收藏

VM shared folder

Shared Folders是Vmware4 的一个新功能,更加方便了在Host,Guest操作系统间共享文件。它是把Host机器上的一个目录共享给Guest机器使用。点击Edit- >;Virtual machine settigns->;Option->;Shared Folders->;Add,选定要共享的文件夹并给这个文件夹命名,下一步。选定“Enable this share”,还有只读和重启后失效的选项,根据需要选择。
确定以后,vmware会把这个文件夹自动mount到/mnt/hgfs目录下

posted @ 2008-05-18 10:10 Xiaobo Sun 阅读(576) | 评论 (0)编辑 收藏

虚拟语气

1. 德语
1)非现实
Wenn das Wetter doch schoen waere!
Wenn wir jetzt Ferien haetten!

Wenn ich Zeit haette, kaeme ich gerne zur einer Party.
Wenn ich Zeit haette, wuerde ich gerne zur einer Party kommen.
2) 客气
Haettest du Lust zu einer Party?
Koennten Sie...
Duerfte ich...

2.英语
1)条件从句:If I were you, I would(should,could,might) study hard.
2) wish宾语:I wish I were as young as you.
3) suggest, advise, ask, decide..宾语:I advise that we (should) stay and wait here.

posted @ 2008-05-13 14:10 Xiaobo Sun 阅读(179) | 评论 (0)编辑 收藏

Dom


posted @ 2008-02-28 14:00 Xiaobo Sun 阅读(230) | 评论 (0)编辑 收藏

Thread and Runnable

Thread is the 进程, Runnable is the 进程对象

[第一需要弄清的问题]

  如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对象和线程的区别

  线程对象是可以产生线程的对象。比如在java平台中Thread对象,Runnable对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始,运行run方法体中的那一段相对独立的过程。

  鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明白它们所代表的真实含义。

  天下难事必始于易,天下大事必始于细。

  让我们先从最简单的"单线程"来入手:(1)带引号说明只是相对而言的单线程,(2)基于java。

    class BeginClass{
public static void main(String[] args){
for(int i=0;i<100;i++)
System.out.println("Hello,World!");
}
}

  如果我们成功编译了该java文件,然后在命令行上敲入:

  java BeginClass

  现在发生了什么呢?每一个java程序员,从他开始学习java的第一分钟里都会接触到这个问

  题,但是,你知道它到底发生发什么?

JVM进程被启动,在同一个JVM进程中,有且只有一个进程,就是它自己。然后在这个JVM环境中,所有程序的运行都是以线程来运行。JVM最先会产生 一个主线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main方法开始运行。当main方法结束后,主线程运行完成。JVM进程也随之退 出。

  我们看到的是一个主线程在运行main方法,这样的只有一个线程执行程序逻辑的流程我们称

  之为单线程。这是JVM提供给我们的单线程环境,事实上,JVM底层还至少有垃圾回收这样的后台线程以及其它非java线程,但这些线程对我们而言不可访问,我们只认为它是单线程的。

  主线程是JVM自己启动的,在这里它不是从线程对象产生的。在这个线程中,它运行了main方法这个指令序列。理解它,但它没有更多可以研究的内容。

  [接触多线程]

    class MyThread extends Thread{
public void run(){
System.out.println("Thread say:Hello,World!");
}
}

public class MoreThreads{
public static void main(String[] args){
new MyThread();
new MyThread().start();
System.out.println("Main say:Hello,World");
}
}

  执行这个程序,main方法第一行产生了一个线程对象,但并没有线程启动。

  main方法第二行产生了一个线程对象,并启动了一个线程。

  main方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句。

  我们先不研究Thread对象的具体内容,稍微来回想一下上面的两个概念,线程对象线程。在JAVA中,线程对象是JVM产生的一个普通的Object子类。而线程是CPU分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要急,但你要记得它们不是一回事就行了。


现在我们来开始考察JAVA中线程对象。

  在JAVA中,要开始一个线程,有两种方式。一是直接调用Thread实例的start()方法,二是
将Runable实例传给一个Thread实例然后调用它的start()方法。

  在前面已经说过,线程对象和线程是两个完全不同的概念。这里我们再次深入一下,生成一个线程的实例,并不代表启动了线程。而启动线程是说在某个线程对象上启动了该实例对应的线程,当该线程结束后,并不会就立即消失。

  对于从很多书籍上可以看到的基础知识我就不用多说了。既然是基础知识,我也着重于从普通文档上读不到的内容。所以本节我重点要说的是两种线程对象产生线程方式的区别。

class MyThread extends Thread{
public int x = 0;

public void run(){

for(int i=0;i<100;i++){
try{
Thread.sleep(10);
}catch(Exception e){}
System.out.println(x++);

}
}
}

  如果我们生成MyThread的一个实例,然后调用它的start()方法,那么就产生了这个实例对应的线程:

public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
}
}

  不用说,最终会打印出0到99,现在我们稍微玩一点花样:

public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
System.out.println(101);
}
}

  也不用说,在基础篇(一)中我们知道由于单CPU的原因,一般会先打印101,然后打印0到99。不过我们可以控制线程让它按我们的意思来运行:

public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
mt.join();
System.out.println(101);
}
}

  好了,我们终于看到,mt实例对应的线程(假如我有时说mt线程请你不要怪我,不过我尽量不这么说)。在运行完成后,主线程才打印101。因为 我们让当前线程(这里是主线程)等待mt线程的运行结束。"在线程对象a上调用join()方法,就是让当前正在执行的线程等待线程对象a对应的线程运行 完成后才继续运行。" 请大家一定要深刻理解并熟记这句话,而我这里引出这个知识点的目的是为了让你继续看下面的例子:

public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
mt.join();
Thread.sleep(3000);
mt.start();
}
}

  当线程对象mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程对象上启动线程。结果我们看到:

  Exception in thread "main" java.lang.IllegalThreadStateException

  也就是这种线程对象一时运行一次完成后,它就再也不能运行第二次了。我们可以看一下它有具体实现:

    public synchronized void start() {
if (started)
throw new IllegalThreadStateException();
started = true;
group.add(this);
start0();
}

  一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true,事实中不管这个线程后来有没有执行到底,只要调用了一次start()就再也没有机会运行了,这意味着:

[通过Thread实例的start(),一个Thread的实例只能产生一个线程]

  那么如果要在一个实例上产生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能。

class R implements Runnable{
private int x = 0;
public void run(){

for(int i=0;i<100;i++){
try{
Thread.sleep(10);
}catch(Exception e){}
System.out.println(x++);

}
}
}

  
正如它的名字一样,Runnable的实例是可运行的,但它自己并不能直接运行,它需要被Thread对象来包装才行运行:

public class Test {
public static void main(String[] args) throws Exception{
new Thread(new R()).start();
}
}

  当然这个结果和mt.start()没有什么区别。但如果我们把一个Runnable实例给Thread对象多次包装,我们就可以看到它们实际是在同一实例上启动线程:

public class Test {
public static void main(String[] args) throws Exception{
R r = new R();
for(int i=0;i<10;i++)
new Thread(r).start();
}
}

  x是实例对象,但结果是x被加到了999,说明这10个线程是在同一个r对象上运行的。请大家注意,因为这个例子是在单CPU上运行的,所以没 有对多个线程同时操作共同的对象进行同步。这里是为了说明的方便而简化了同步,而真正的环境中你无法预知程序会在什么环境下运行,所以一定要考虑同步。

  到这里我们做一个完整的例子来说明线程产生的方式不同而生成的线程的区别:

package debug;

import java.io.*;
import java.lang.Thread;


class MyThread extends Thread{
public int x = 0;

public void run(){
System.out.println(++x);
}
}

class R implements Runnable{
private int x = 0;
public void run(){
System.out.println(++x);
}
}

public class Test {
public static void main(String[] args) throws Exception{

for(int i=0;i<10;i++){
Thread t = new MyThread();
t.start();
}
Thread.sleep(10000);//让上面的线程运行完成
R r = new R();
for(int i=0;i<10;i++){
Thread t = new Thread(r);
t.start();
}
}
}

  上面10个线程对象产生的10个线程运行时打印了10次1。下面10个线程对象产生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnable实例)的多个线程



posted @ 2008-02-28 13:57 Xiaobo Sun 阅读(2268) | 评论 (0)编辑 收藏

Eclipse Workbench




posted @ 2008-02-28 13:31 Xiaobo Sun 阅读(240) | 评论 (0)编辑 收藏

TCP connect/deconnect

Wireshark is a powerful tool..
.

posted @ 2008-02-28 13:24 Xiaobo Sun 阅读(195) | 评论 (0)编辑 收藏

Display.getDefualt.syncExec

Causes the <code>run()</code> method of the runnable to be invoked by the user-interface thread at the next reasonable opportunity. The caller of this method continues to run in parallel, and is not notified when the runnable has completed.  Specifying <code>null</code> as the runnable simply wakes the user-interface thread when run.
Note that at the time the runnable is invoked, widgets that have the receiver as their display may have been disposed. Therefore, it is necessary to check for this case inside the runnable before accessing the widget.
Display.getDefault.asysnExec(Runnable)
Causes the <code>run()</code> method of the runnable to be invoked by the user-interface thread at the next reasonable opportunity. The thread which calls this method is suspended until the runnable completes.  Specifying <code>null</code> as the runnable simply wakes the user-interface thread.
Note that at the time the runnable is invoked, widgets that have the receiver as their display may have been disposed. Therefore, it is necessary to check for this case inside the runnable before accessing the widget.
Display.getDefault.sysnExec(Runnable)

posted @ 2008-02-13 11:25 Xiaobo Sun 阅读(525) | 评论 (0)编辑 收藏

<2008年2月>
272829303112
3456789
10111213141516
17181920212223
2425262728291
2345678

导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜