Xiaobo Sun

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

          2008年7月11日

          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 閱讀(218) | 評論 (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 閱讀(216) | 評論 (0)編輯 收藏

          MySQL

          1。啟動:mysqld --console
          2。調試:mysql -u root

          posted @ 2009-05-06 17:16 Xiaobo Sun 閱讀(201) | 評論 (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 閱讀(280) | 評論 (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 閱讀(253) | 評論 (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 閱讀(240) | 評論 (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 閱讀(290) | 評論 (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 閱讀(398) | 評論 (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 閱讀(597) | 評論 (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 閱讀(590) | 評論 (0)編輯 收藏

          spring mvc

          spring mvc 這部機器 說白了其實很簡單,DispatcherServlet 把 HandlerMapping Controller ViewResolver 3個組件 組裝在一起就完成了整個處理過程。

          想起來就像人吃飯一樣。把飯放入嘴巴,經過喉嚨,然后到胃里,處理一下,再接著往下傳,到腸子 最后把處理的結果 排泄出來。呵呵,惡心一把  
          當然還吸收了一些,這就類似存了一些數據到數據庫。說白了還真這么回事。

          posted @ 2008-12-09 18:03 Xiaobo Sun 閱讀(153) | 評論 (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 閱讀(308) | 評論 (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 閱讀(1062) | 評論 (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 閱讀(337) | 評論 (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 閱讀(285) | 評論 (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 閱讀(157) | 評論 (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 閱讀(168) | 評論 (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 閱讀(3335) | 評論 (1)編輯 收藏

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

          wc

          posted @ 2008-10-09 16:11 Xiaobo Sun 閱讀(306) | 評論 (0)編輯 收藏

          refresh >tail -f /var/log/messages

           refresh  tail -f /var/log/messages

          posted @ 2008-10-09 16:08 Xiaobo Sun 閱讀(283) | 評論 (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 閱讀(388) | 評論 (0)編輯 收藏

          ps -aux | grep "lct*"

          ps -aux | grep "lct*"

          posted @ 2008-10-09 16:07 Xiaobo Sun 閱讀(204) | 評論 (0)編輯 收藏

          umlenkung >> , 2>

          umlenkung >> , 2>

          posted @ 2008-10-09 16:06 Xiaobo Sun 閱讀(177) | 評論 (0)編輯 收藏

          linux kernel version >uname -r

          linux kernel  version
           uname -r

          posted @ 2008-10-09 16:04 Xiaobo Sun 閱讀(288) | 評論 (0)編輯 收藏

          Dir size >du -k (kilo byte)

          Dir size      du -k (kilo byte)

          posted @ 2008-10-09 16:03 Xiaobo Sun 閱讀(193) | 評論 (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 閱讀(215) | 評論 (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 閱讀(989) | 評論 (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 閱讀(169) | 評論 (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 閱讀(9685) | 評論 (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 閱讀(179) | 評論 (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 閱讀(3061) | 評論 (0)編輯 收藏

          Vim cut, copy and paste

          posted @ 2008-07-11 08:53 Xiaobo Sun 閱讀(366) | 評論 (0)編輯 收藏

          <2008年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導航

          統計

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 绥阳县| 洪江市| 利川市| 永登县| 天祝| 山丹县| 普定县| 手游| 东至县| 鹤庆县| 尉犁县| 襄樊市| 丰原市| 长治市| 瓦房店市| 河北省| 平遥县| 奇台县| 普定县| 麻城市| 竹山县| 高尔夫| 南溪县| 卢湾区| 东乡族自治县| 九龙城区| 荣昌县| 义马市| 娱乐| 建宁县| 噶尔县| 滕州市| 拉萨市| 石狮市| 永宁县| 柳林县| 珠海市| 安多县| 丽江市| 马公市| 仙居县|