別以為是那些軟件開發(fā)定律,別以為是開發(fā)出那些特殊用途的軟件,別以為是軟件設(shè)計技術(shù)本身。只有一條真理決定了一個軟件程序員的成功還是失敗。由于堅持這個真理,一個資深的程序員能在一天的時間里學會一門新的編程語言,而由于不堅持這條真理,一個初級的程序員用十年時間也只能掙到一份糊口的錢、永遠是來實現(xiàn)別人的設(shè)計、永遠不夠優(yōu)秀而得不到晉升的機會。這條真理讓你看清了差的程序員和好的程序員的不同之處,好的程序員和偉大的程序員的不同之處,偉大的程序員和能通過自己的技術(shù)創(chuàng)造出一個億萬美元價值的程序帝國的超級程序員的不同之處。

          不是什么復雜的道理,不是什么難懂的理論。不是具有什么天賦或“編程超能力“才能做到的事情。最終成為的是一個優(yōu)秀的程序員還是一個很爛的程序員,這跟你的出身一點關(guān)系都沒有。

          而真正的原因只有一個,唯一的一個:

          對所做的事情的理解越深,你就會做的越好。

          超級程序員跟那些平庸的、一般的程序員比起來,對自己要做的事情的理解要深的多的多。這就是原因。

          要想成為一名出色的程序員,你所要做的就是完全的理解要在做的事情。

          有人會說,該知道的我都知道了。而對說這話的人的驗證就是看他們能有應用他們知道的知識的能力。是否他能夠構(gòu)造出完美的系統(tǒng)架構(gòu),讓人們能輕松的維護?是否他能在不皺眉頭的情況下把一個普通程序員毫無可能解決的問題輕松解決掉?是否他能在被詢問時能用最簡單的概念把任何問題都闡述明白?如果能夠,那他就是一個杰出的程序員,他能很好的理解了他在做的事情。

          然而,盡管這些人看起來已經(jīng)“無所不知”,很多的程序員(包括我)都感覺他們?nèi)匀辉谥R的海洋里奮斗不已。有如此多的東西需要去學習,一個人幾乎要花費他畢生的心力去學習,但仍然很難說能掌握計算機知識的90%。

          而這場持久戰(zhàn)中的秘密武器、戰(zhàn)勝計算機知識的亞瑟王的神劍,就是透徹理解。對你的領(lǐng)域里的基礎(chǔ)知識理解的越好,你就越容易提升到更高的層次。你對這一層次的知識理解的越好,你就更容易掌握下一層次,以此類推。一旦你從最簡單最基礎(chǔ)的知識走到最高級最復雜的理論,你可以從頭再過一遍,此時你會驚奇的發(fā)現(xiàn),在最低最底的底層,竟然還有那么多知識需要學習。

          看起來這個道理實在是太簡單,難以受到重視,但事實就是這樣。通往杰出的程序員的道路就是完全的深入的理解,從掌握精通最基本的知識開始,從而逐漸牢固掌握更高級的知識。

          我不想騙你—這是一個很長的路程。但你是值得去做的。在路的盡頭,你會突然發(fā)現(xiàn),自己神奇的成為了一位資深的程序員,受到所有人的尊敬。你能成為一位神奇的程序員,任何事情都難不倒的程序員,讓其他程序員都羨慕的程序員。誰能預料到呢?我不能告訴你你該做什么或能成為什么。但我可以告訴你我發(fā)現(xiàn)一些真實的道理和有價值的東西。怎么去做全在于自己。

          -Max

          posted @ 2013-06-24 14:24 小馬歌 閱讀(227) | 評論 (0)編輯 收藏
           
          本文將探討單例模式的各種情況,并給出相應的建議。單例模式應該是設(shè)計模式中比較簡單的一個,但是在多線程并發(fā)的環(huán)境下使用卻是不那么簡單了。
          首先看最原始的單例模式。
          1 package xylz.study.singleton;
          2 
          3 public class Singleton {
          4 
          5     private static Singleton instance = null;
          6 
          7     private Singleton() {
          8     }
          9 
          10     public static Singleton getInstance() {
          11         if (instance == null) {
          12             instance = new Singleton();
          13         }
          14         return instance;
          15     }
          16 }
          17 

          顯然這個寫法在單線程環(huán)境下非常好,但是多線程會導致多個實例出現(xiàn),這個大家都能理解。
          最簡單的改造方式是添加一個同步鎖。
          1 package xylz.study.singleton;
          2 
          3 public class SynchronizedSingleton {
          4 
          5     private static SynchronizedSingleton instance = null;
          6 
          7     private SynchronizedSingleton() {
          8     }
          9 
          10     public static synchronized SynchronizedSingleton getInstance() {
          11         if (instance == null) {
          12             instance = new SynchronizedSingleton();
          13         }
          14         return instance;
          15     }
          16 }
          17 

          顯然上面的方法避免了并發(fā)的問題,但是由于我們只是在第一次構(gòu)造對象的時候才需要同步,以后就不再需要同步,所以這里不可避免的有性能開銷。于是將鎖去掉采用靜態(tài)的屬性來解決同步鎖的問題。
          1 package xylz.study.singleton;
          2 
          3 public class StaticSingleton {
          4 
          5     private static StaticSingleton instance = new StaticSingleton();
          6 
          7     private StaticSingleton() {
          8     }
          9 
          10     public static StaticSingleton getInstance() {
          11         return instance;
          12     }
          13 }
          14 

          上面的方法既沒有鎖又解決了性能問題,看起來已經(jīng)滿足需求了。但是追求“完美”的程序員想延時加載對象,希望在第一次獲取的時候才構(gòu)造對象,于是大家非常聰明的進行改造,也即非常出名的雙重檢查鎖機制出來了。
          1 package xylz.study.singleton;
          2 
          3 public class DoubleLockSingleton {
          4 
          5     private static DoubleLockSingleton instance = null;
          6 
          7     private DoubleLockSingleton() {
          8     }
          9 
          10     public static DoubleLockSingleton getInstance() {
          11         if (instance == null) {
          12             synchronized (DoubleLockSingleton.class) {
          13                 if (instance == null) {
          14                     instance = new DoubleLockSingleton();
          15                 }
          16             }
          17         }
          18         return instance;
          19     }
          20 }
          21 


          雙重鎖機制看起來非常巧妙的避免了上面的問題。但是真的是這樣的嗎?文章《雙重檢查鎖定及單例模式》中談到了非常多演變的雙重鎖機制帶來的問題,包括比較難以理解的指令重排序機制等。總之就是雙重檢查鎖機制仍然對導致錯誤問題而不是性能問題。

          一種避免上述問題的解決方案是使用volatile關(guān)鍵字,此關(guān)鍵字保證對一個對象修改后能夠立即被其它線程看到,也就是避免了指令重排序和可見性問題。參考文章

          指令重排序與happens-before法則

          所以上面的寫法就變成了下面的例子。

          package xylz.study.singleton;

          public class DoubleLockSingleton {

              private static volatile DoubleLockSingleton instance = null;

              private DoubleLockSingleton() {
              }

              public static DoubleLockSingleton getInstance() {
                  if (instance == null) {
                      synchronized (DoubleLockSingleton.class) {
                          if (instance == null) {
                              instance = new DoubleLockSingleton();
                          }
                      }
                  }
                  return instance;
              }
          }


          于是繼續(xù)改造,某個牛人利用JVM的特性來解決上述問題,具體哪個牛人我忘記了,但是不是下面文章的作者。
          (1)《Java theory and practice: Fixing the Java Memory Model, Part 2
          (2)《Initialize-On-Demand Holder Class and Singletons

          1 package xylz.study.singleton;
          2 
          3 public class HolderSingleton {
          4 
          5     private static class HolderSingletonHolder {
          6 
          7         static HolderSingleton instance = new HolderSingleton();
          8     }
          9 
          10     private HolderSingleton() {
          11         //maybe throw an Exception when doing something 
          12     }
          13 
          14     public static HolderSingleton getInstance() {
          15         return HolderSingletonHolder.instance;
          16     }
          17 }
          18 




          上述代碼看起來解決了上面單例模式遇到的所有問題,而且實際上工作的很好,沒有什么問題。但是卻有一個致命的問題,如果第11行拋出了一個異常,也就是第一次構(gòu)造函數(shù)失敗將導致永遠無法再次得到構(gòu)建對象的機會。
          使用下面的代碼測試下。
          1 package xylz.study.singleton;
          2 
          3 public class HolderSingletonTest {
          4 
          5     private static class HolderSingletonHolder {
          6 
          7         static HolderSingletonTest instance = new HolderSingletonTest();
          8     }
          9 
          10     private static boolean init = false;
          11     
          12     private HolderSingletonTest() {
          13         //maybe throw an Exception when doing something 
          14         if(!init) {
          15             init=true;
          16             throw new RuntimeException("fail");
          17         }
          18     }
          19 
          20     public static HolderSingletonTest getInstance() {
          21         return HolderSingletonHolder.instance;
          22     }
          23     public static void main(String[] args) {
          24         for(int i=0;i<3;i++) {
          25             try {
          26                 System.out.println(HolderSingletonTest.getInstance());
          27             } catch (Exception e) {
          28                 System.err.println("one->"+i);
          29                 e.printStackTrace();
          30             }catch(ExceptionInInitializerError err) {
          31                 System.err.println("two->"+i);
          32                 err.printStackTrace();
          33             }catch(Throwable t) {
          34                 System.err.println("three->"+i);
          35                 t.printStackTrace();
          36             }
          37         }
          38     }
          39 }
          40 
          很不幸將得到以下輸出:
          1 two->0
          2 java.lang.ExceptionInInitializerError
          3     at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
          4     at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
          5 Caused by: java.lang.RuntimeException: fail
          6     at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
          7     at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
          8     at xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
          9      2 more
          10 three->1
          11 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
          12     at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
          13     at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
          14 three->2
          15 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
          16     at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
          17     at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
          18 

          很顯然我們想著第一次加載失敗第二次能夠加載成功,非常不幸,JVM一旦加載某個類失敗將認為此類的定義有問題,將來不再加載,這樣就導致我們沒有機會再加載。目前看起來沒有辦法避免此問題。如果要使用JVM的類加載特性就必須保證類加載一定正確,否則此問題將比并發(fā)和性能更嚴重。如果我們的類需要初始話那么就需要想其它辦法避免在構(gòu)造函數(shù)中完成。看起來像是又回到了老地方,難道不是么?

          總之,結(jié)論是目前沒有一個十全十美的單例模式,而大多數(shù)情況下我們只需要滿足我們的需求就行,沒必有特意追求最“完美”解決方案。
          原文[http://www.imxylz.info/p/177.html]
          posted @ 2013-05-22 13:58 小馬歌 閱讀(188) | 評論 (0)編輯 收藏
           


          1.1內(nèi)存分配方面

          :一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式是類似于鏈表。可能用到的關(guān)鍵字如下:new、malloc、delete、free等等。

          :由編譯器(Compiler)自動分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

          1.2申請方式方面:

          :需要程序員自己申請,并指明大小。在c中malloc函數(shù)如p1 = (char *)malloc(10);在C++中用new運算符,但是注意p1、p2本身是在棧中的。因為他們還是可以認為是局部變量。

          :由系統(tǒng)自動分配。 例如,聲明在函數(shù)中一個局部變量 int b;系統(tǒng)自動在棧中為b開辟空間。

          1.3系統(tǒng)響應方面:

          :操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表,當系統(tǒng)收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結(jié)點,然后將該結(jié)點從空閑結(jié)點鏈表中刪除,并將該結(jié)點的空間分配給程序,另外,對于大多數(shù)系統(tǒng),會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外由于找到的堆結(jié)點的大小不一定正好等于申請的大小,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中。

          :只要棧的剩余空間大于所申請空間,系統(tǒng)將為程序提供內(nèi)存,否則將報異常提示棧溢出。

          1.4大小限制方面:

          :是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。

          :在Windows下, 棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的,在WINDOWS下,棧的大小是固定的(是一個編譯時就確定的常數(shù)),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。

          1.5效率方面:

          :是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內(nèi)存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內(nèi)存,雖然用起來最不方便。但是速度快,也最靈活。

          :由系統(tǒng)自動分配,速度較快。但程序員是無法控制的。

          1.6存放內(nèi)容方面:

          :一般是在堆的頭部用一個字節(jié)存放堆的大小。堆中的具體內(nèi)容有程序員安排。

          :在函數(shù)調(diào)用時第一個進棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址然后是函數(shù)的各個參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧,然后是函數(shù)中的局部變量。 注意: 靜態(tài)變量是不入棧的。當本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令,程序由該點繼續(xù)運行。

          1.7存取效率方面:

          :char *s1 = "Hellow Word";是在編譯時就確定的;

          :char s1[] = "Hellow Word"; 是在運行時賦值的;用數(shù)組比用指針速度要快一些,因為指針在底層匯編中需要用edx寄存器中轉(zhuǎn)一下,而數(shù)組在棧上直接讀取。


          小結(jié)

          1、靜態(tài)變量不入棧。 
          2、棧由編譯器自動分配和釋放。棧中存放局部變量和參數(shù),函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù)。 
          3、數(shù)組比用指針速度要快一些,因為指針在底層匯編中需要用edx寄存器中轉(zhuǎn)一下,而數(shù)組在棧上直接讀取。 
          4、堆是由程序員通過new、malloc、free、delete等指令進行分配和釋放。如果程序員沒有進行釋放,程序結(jié)束時可能有OS回收。 
          5、堆是由new分配的內(nèi)存,速度較慢;棧是由系統(tǒng)自動分配,速度較快。 
          6、比如存放在棧里面的數(shù)組,是在運行時賦值。而存在堆里面的指針數(shù)據(jù),是在編譯時就確定的。

          附:

          一. 在c中分為這幾個存儲區(qū)

          1.棧 - 由編譯器自動分配釋放
          2.堆 - 一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由OS回收
          3.全局區(qū)(靜態(tài)區(qū)),全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。- 程序結(jié)束釋放
          4.另外還有一個專門放常量的地方。- 程序結(jié)束釋放
                                                                                                                                                        
          在函數(shù)體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內(nèi)存的函數(shù)分配得到的就是在堆上。在所有函數(shù)體外定義的是全局量,加了static修飾符后不管在哪里都存放在全局區(qū)(靜態(tài)區(qū)),在所有函數(shù)體外定義的static變量表示在該文件中有效,不能extern到別的文件用,在函數(shù)體內(nèi)定義的static表示只在該函數(shù)體內(nèi)有效。另外,函數(shù)中的"adgfdf"這樣的字符串存放在常量區(qū)。比如:

          int a = 0//全局初始化區(qū)
          char *p1; //全局未初始化區(qū)
          void main()
          {
              int b; //
              char s[] = "abc"; //
              char *p2; //
              char *p3 = "123456"; //123456{post.content}在常量區(qū),p3在棧上
              static int c = 0; //全局(靜態(tài))初始化區(qū)
               p1 = (char *)malloc(10); //分配得來得10字節(jié)的區(qū)域在堆區(qū)
               p2 = (char *)malloc(20); //分配得來得20字節(jié)的區(qū)域在堆區(qū)
               strcpy(p1, "123456");
              //123456{post.content}放在常量區(qū),編譯器可能會將它與p3所指向的"123456"優(yōu)化成一塊
          }


          二.在C++中,內(nèi)存分成5個區(qū),他們分別是堆、棧、自由存儲區(qū)、全局/靜態(tài)存儲區(qū)和常量存儲區(qū)
          1.棧,
          就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變量的存儲區(qū)。里面的變量通常是局部變量、函數(shù)參數(shù)等。
          2.堆,就是那些由new分配的內(nèi)存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個new就要對應一個delete。如果程序員沒有釋放掉,那么在程序結(jié)束后,操作系統(tǒng)會自動回收。
          3.自由存儲區(qū),就是那些由malloc等分配的內(nèi)存塊,他和堆是十分相似的,不過它是用free來結(jié)束自己的生命的。
          4.全局/靜態(tài)存儲區(qū),全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個區(qū)分了,他們共同占用同一塊內(nèi)存區(qū)。
          5.常量存儲區(qū),這是一塊比較特殊的存儲區(qū),他們里面存放的是常量,不允許修改(當然,你要通過非正當手段也可以修改)

          三. 談談堆與棧的關(guān)系與區(qū)別
          具體地說,現(xiàn)代計算機(串行執(zhí)行機制),都直接在代碼底層支持棧的數(shù)據(jù)結(jié)構(gòu)。這體現(xiàn)在,有專門的寄存器指向棧所在的地址,有專門的機器指令完成數(shù)據(jù)入棧出棧的操作。這種機制的特點是效率高,支持的數(shù)據(jù)有限,一般是整數(shù),指針,浮點數(shù)等系統(tǒng)直接支持的數(shù)據(jù)類型,并不直接支持其他的數(shù)據(jù)結(jié)構(gòu)。因為棧的這種特點,對棧的使用在程序中是非常頻繁的。對子程序的調(diào)用就是直接利用棧完成的。機器的call指令里隱含了把返回地址推入棧,然后跳轉(zhuǎn)至子程序地址的操作,而子程序中的ret指令則隱含從堆棧中彈出返回地址并跳轉(zhuǎn)之的操作。C/C++中的自動變量是直接利用棧的例子,這也就是為什么當函數(shù)返回時,該函數(shù)的自動變量自動失效的原因。 

          和棧不同,堆的數(shù)據(jù)結(jié)構(gòu)并不是由系統(tǒng)(無論是機器系統(tǒng)還是操作系統(tǒng))支持的,而是由函數(shù)庫提供的。基本的malloc/realloc/free 函數(shù)維護了一套內(nèi)部的堆數(shù)據(jù)結(jié)構(gòu)。當程序使用這些函數(shù)去獲得新的內(nèi)存空間時,這套函數(shù)首先試圖從內(nèi)部堆中尋找可用的內(nèi)存空間,如果沒有可以使用的內(nèi)存空間,則試圖利用系統(tǒng)調(diào)用來動態(tài)增加程序數(shù)據(jù)段的內(nèi)存大小,新分配得到的空間首先被組織進內(nèi)部堆中去,然后再以適當?shù)男问椒祷亟o調(diào)用者。當程序釋放分配的內(nèi)存空間時,這片內(nèi)存空間被返回內(nèi)部堆結(jié)構(gòu)中,可能會被適當?shù)奶幚?比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內(nèi)存分配申請。這套復雜的分配機制實際上相當于一個內(nèi)存分配的緩沖池(Cache),使用這套機制有如下若干原因:
          1. 系統(tǒng)調(diào)用可能不支持任意大小的內(nèi)存分配。有些系統(tǒng)的系統(tǒng)調(diào)用只支持固定大小及其倍數(shù)的內(nèi)存請求(按頁分配);這樣的話對于大量的小內(nèi)存分類來說會造成浪費。
          2. 系統(tǒng)調(diào)用申請內(nèi)存可能是代價昂貴的。系統(tǒng)調(diào)用可能涉及用戶態(tài)和核心態(tài)的轉(zhuǎn)換。
          3. 沒有管理的內(nèi)存分配在大量復雜內(nèi)存的分配釋放操作下很容易造成內(nèi)存碎片。

          堆和棧的對比
          從以上知識可知,棧是系統(tǒng)提供的功能,特點是快速高效,缺點是有限制,數(shù)據(jù)不靈活;而棧是函數(shù)庫提供的功能,特點是靈活方便,數(shù)據(jù)適應面廣泛,但是效率有一定降低。棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對于進程/線程是唯一的;堆是函數(shù)庫內(nèi)部數(shù)據(jù)結(jié)構(gòu),不一定唯一。不同堆分配的內(nèi)存無法互相操作。棧空間分靜態(tài)分配和動態(tài)分配兩種。靜態(tài)分配是編譯器完成的,比如自動變量(auto)的分配。動態(tài)分配由alloca函數(shù)完成。棧的動態(tài)分配無需釋放(是自動的),也就沒有釋放函數(shù)。為可移植的程序起見,棧的動態(tài)分配操作是不被鼓勵的!堆空間的分配總是動態(tài)的,雖然程序結(jié)束時所有的數(shù)據(jù)空間都會被釋放回系統(tǒng),但是精確的申請內(nèi)存/ 釋放內(nèi)存匹配是良好程序的基本要素。

              1.碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進的棧內(nèi)容已經(jīng)被彈出,詳細的可以>參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。
              2.生長方向:對于堆來講,生長方向是向上的,也就是向著內(nèi)存地址增加的方向;對于棧來講,它的生長方向是向下的,是向著內(nèi)存地址減小的方向增長。
              3.分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進行釋放,無需我們手工實現(xiàn)。
              4.分配效率:棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機制是很復雜的,例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機會分到足夠大小的內(nèi)存,然后進行返回。顯然,堆的效率比棧要低得多。

              明確區(qū)分堆與棧:
              在bbs上,堆與棧的區(qū)分問題,似乎是一個永恒的話題,由此可見,初學者對此往往是混淆不清的,所以我決定拿他第一個開刀。
              首先,我們舉一個例子:

          void f()

              int* p=new int[5];
          }

          這條短短的一句話就包含了堆與棧,看到new,我們首先就應該想到,我們分配了一塊堆內(nèi)存,那么指針p呢?他分配的是一塊棧內(nèi)存,所以這句話的意思就是:在棧內(nèi)存中存放了一個指向一塊堆內(nèi)存的指針p。在程序會先確定在堆中分配內(nèi)存的大小,然后調(diào)用operator new分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧中,他在VC6下的匯編代碼如下:
              00401028    push         14h
              0040102A    call            operator new (00401060)
              0040102F    add          esp,4
              00401032    mov          dword ptr [ebp-8],eax
              00401035    mov          eax,dword ptr [ebp-8]
              00401038    mov          dword ptr [ebp-4],eax
              這里,我們?yōu)榱撕唵尾]有釋放內(nèi)存,那么該怎么去釋放呢?是delete p么?澳,錯了,應該是delete []p,這是為了告訴編譯器:我刪除的是一個數(shù)組,VC6就會根據(jù)相應的Cookie信息去進行釋放內(nèi)存的工作。
              好了,我們回到我們的主題:堆和棧究竟有什么區(qū)別?
              主要的區(qū)別由以下幾點:
              1、管理方式不同;
              2、空間大小不同;
              3、能否產(chǎn)生碎片不同;
              4、生長方向不同;
              5、分配方式不同;
              6、分配效率不同;
              管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。
              空間大小:一般來講在32位系統(tǒng)下,堆內(nèi)存可以達到4G的空間,從這個角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認的棧空間大小是1M(好像是,記不清楚了)。當然,我們可以修改:
              打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。
          注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁文件里面,它設(shè)置的較大會使棧開辟較大的值,可能增加內(nèi)存的開銷和啟動時間。
              堆和棧相比,由于大量new/delete的使用,容易造成大量的內(nèi)存碎片;由于沒有專門的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶態(tài)和核心態(tài)的切換,內(nèi)存的申請,代價變得更加昂貴。所以棧在程序中是應用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。

          另外對存取效率的比較:
          代碼:

          char s1[] = "aaaaaaaaaaaaaaa";
          char *s2 = "bbbbbbbbbbbbbbbbb";

          aaaaaaaaaaa是在運行時刻賦值的;
          而bbbbbbbbbbb是在編譯時就確定的;
          但是,在以后的存取中,在棧上的數(shù)組比指針所指向的字符串(例如堆)快
          比如:

          void main()
          {
              char a = 1;
              char c[] = "1234567890";
              char *p ="1234567890";
               a = c[1];
               a = p[1];
              return;
          }

          對應的匯編代碼
          10: a = c[1];
          00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
          0040106A 88 4D FC mov byte ptr [ebp-4],cl
          11: a = p[1];
          0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
          00401070 8A 42 01 mov al,byte ptr [edx+1]
          00401073 88 45 FC mov byte ptr [ebp-4],al
          第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據(jù)edx讀取字符,顯然慢了.
              無論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生(除非你是故意使其越界),因為越界的結(jié)果要么是程序崩潰,要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運行過程中,沒有發(fā)生上面的問題,你還是要小心,說不定什么時候就崩掉,編寫穩(wěn)定安全的代碼才是最重要的

          posted @ 2013-05-20 16:40 小馬歌 閱讀(285) | 評論 (0)編輯 收藏
           

          淺談CSRF攻擊方式

          2009-04-09 22:44 by hyddd, 18997 閱讀, 39 評論, 收藏編輯

          一.CSRF是什么?

            CSRF(Cross-site request forgery),中文名稱:跨站請求偽造,也被稱為:one click attack/session riding,縮寫為:CSRF/XSRF。

          二.CSRF可以做什么?

            你這可以這么理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發(fā)送惡意請求。CSRF能夠做的事情包括:以你名義發(fā)送郵件,發(fā)消息,盜取你的賬號,甚至于購買商品,虛擬貨幣轉(zhuǎn)賬......造成的問題包括:個人隱私泄露以及財產(chǎn)安全。

          三.CSRF漏洞現(xiàn)狀

            CSRF這種攻擊方式在2000年已經(jīng)被國外的安全人員提出,但在國內(nèi),直到06年才開始被關(guān)注,08年,國內(nèi)外的多個大型社區(qū)和交互網(wǎng)站分別爆出CSRF漏洞,如:NYTimes.com(紐約時報)、Metafilter(一個大型的BLOG網(wǎng)站),YouTube和百度HI......而現(xiàn)在,互聯(lián)網(wǎng)上的許多站點仍對此毫無防備,以至于安全業(yè)界稱CSRF為“沉睡的巨人”。

          四.CSRF的原理

            下圖簡單闡述了CSRF攻擊的思想:

            

            從上圖可以看出,要完成一次CSRF攻擊,受害者必須依次完成兩個步驟

            1.登錄受信任網(wǎng)站A,并在本地生成Cookie

            2.在不登出A的情況下,訪問危險網(wǎng)站B

            看到這里,你也許會說:“如果我不滿足以上兩個條件中的一個,我就不會受到CSRF的攻擊”。是的,確實如此,但你不能保證以下情況不會發(fā)生:

            1.你不能保證你登錄了一個網(wǎng)站后,不再打開一個tab頁面并訪問另外的網(wǎng)站。

            2.你不能保證你關(guān)閉瀏覽器了后,你本地的Cookie立刻過期,你上次的會話已經(jīng)結(jié)束。(事實上,關(guān)閉瀏覽器不能結(jié)束一個會話,但大多數(shù)人都會錯誤的認為關(guān)閉瀏覽器就等于退出登錄/結(jié)束會話了......)

            3.上圖中所謂的攻擊網(wǎng)站,可能是一個存在其他漏洞的可信任的經(jīng)常被人訪問的網(wǎng)站。

           

            上面大概地講了一下CSRF攻擊的思想,下面我將用幾個例子詳細說說具體的CSRF攻擊,這里我以一個銀行轉(zhuǎn)賬的操作作為例子(僅僅是例子,真實的銀行網(wǎng)站沒這么傻:>)

            示例1:

            銀行網(wǎng)站A,它以GET請求來完成銀行轉(zhuǎn)賬的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000

            危險網(wǎng)站B,它里面有一段HTML的代碼如下:

            <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

            首先,你登錄了銀行網(wǎng)站A,然后訪問危險網(wǎng)站B,噢,這時你會發(fā)現(xiàn)你的銀行賬戶少了1000塊......

            為什么會這樣呢?原因是銀行網(wǎng)站A違反了HTTP規(guī)范,使用GET請求更新資源。在訪問危險網(wǎng)站B的之前,你已經(jīng)登錄了銀行網(wǎng)站A,而B中的<img>以GET的方式請求第三方資源(這里的第三方就是指銀行網(wǎng)站了,原本這是一個合法的請求,但這里被不法分子利用了),所以你的瀏覽器會帶上你的銀行網(wǎng)站A的Cookie發(fā)出Get請求,去獲取資源“http://www.mybank.com/Transfer.php?toBankId=11&money=1000”,結(jié)果銀行網(wǎng)站服務器收到請求后,認為這是一個更新資源操作(轉(zhuǎn)賬操作),所以就立刻進行轉(zhuǎn)賬操作......

            示例2:

            為了杜絕上面的問題,銀行決定改用POST請求完成轉(zhuǎn)賬操作。

            銀行網(wǎng)站A的WEB表單如下:  

            <form action="Transfer.php" method="POST">
              <p>ToBankId: <input type="text" name="toBankId" /></p>
              <p>Money: <input type="text" name="money" /></p>
              <p><input type="submit" value="Transfer" /></p>
            </form>

            后臺處理頁面Transfer.php如下:

          復制代碼
            <?php
              session_start();
              if (isset($_REQUEST['toBankId'&& isset($_REQUEST['money']))
              {
                  buy_stocks(
          $_REQUEST['toBankId'], $_REQUEST['money']);
              }
            ?>
          復制代碼

            危險網(wǎng)站B,仍然只是包含那句HTML代碼:

            <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

            和示例1中的操作一樣,你首先登錄了銀行網(wǎng)站A,然后訪問危險網(wǎng)站B,結(jié)果.....和示例1一樣,你再次沒了1000塊~T_T,這次事故的原因是:銀行后臺使用了$_REQUEST去獲取請求的數(shù)據(jù),而$_REQUEST既可以獲取GET請求的數(shù)據(jù),也可以獲取POST請求的數(shù)據(jù),這就造成了在后臺處理程序無法區(qū)分這到底是GET請求的數(shù)據(jù)還是POST請求的數(shù)據(jù)。在PHP中,可以使用$_GET和$_POST分別獲取GET請求和POST請求的數(shù)據(jù)。在JAVA中,用于獲取請求數(shù)據(jù)request一樣存在不能區(qū)分GET請求數(shù)據(jù)和POST數(shù)據(jù)的問題。

            示例3:

            經(jīng)過前面2個慘痛的教訓,銀行決定把獲取請求數(shù)據(jù)的方法也改了,改用$_POST,只獲取POST請求的數(shù)據(jù),后臺處理頁面Transfer.php代碼如下:

          復制代碼
            <?php
              
          session_start();
              
          if (isset($_POST['toBankId'&& isset($_POST['money']))
              {
                  buy_stocks(
          $_POST['toBankId'], $_POST['money']);
              }
            
          ?>
          復制代碼

            然而,危險網(wǎng)站B與時俱進,它改了一下代碼:

          復制代碼
          <html>
            <head>
              <script type="text/javascript">
                function steal()
                {
                         iframe 
          = document.frames["steal"];
                         iframe.document.Submit(
          "transfer");
                }
              </script>
            </head>

            
          <body onload="steal()">
              <iframe name="steal" display="none">
                <form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
                  
          <input type="hidden" name="toBankId" value="11">
                  
          <input type="hidden" name="money" value="1000">
                
          </form>
              </iframe>
            </body>
          </html>
          復制代碼

          如果用戶仍是繼續(xù)上面的操作,很不幸,結(jié)果將會是再次不見1000塊......因為這里危險網(wǎng)站B暗地里發(fā)送了POST請求到銀行!

            總結(jié)一下上面3個例子,CSRF主要的攻擊模式基本上是以上的3種,其中以第1,2種最為嚴重,因為觸發(fā)條件很簡單,一個<img>就可以了,而第3種比較麻煩,需要使用JavaScript,所以使用的機會會比前面的少很多,但無論是哪種情況,只要觸發(fā)了CSRF攻擊,后果都有可能很嚴重。

            理解上面的3種攻擊模式,其實可以看出,CSRF攻擊是源于WEB的隱式身份驗證機制!WEB的身份驗證機制雖然可以保證一個請求是來自于某個用戶的瀏覽器,但卻無法保證該請求是用戶批準發(fā)送的

          五.CSRF的防御

            我總結(jié)了一下看到的資料,CSRF的防御可以從服務端客戶端兩方面著手,防御效果是從服務端著手效果比較好,現(xiàn)在一般的CSRF防御也都在服務端進行。

            1.服務端進行CSRF防御

            服務端的CSRF方式方法很多樣,但總的思想都是一致的,就是在客戶端頁面增加偽隨機數(shù)

            (1).Cookie Hashing(所有表單都包含同一個偽隨機值):

            這可能是最簡單的解決方案了,因為攻擊者不能獲得第三方的Cookie(理論上),所以表單中的數(shù)據(jù)也就構(gòu)造失敗了:>

            <?php
              //構(gòu)造加密的Cookie信息
              $value = “DefenseSCRF”;
              setcookie(”cookie”, $value, time()+3600);
            ?>

            在表單里增加Hash值,以認證這確實是用戶發(fā)送的請求。

          復制代碼
            <?php
              $hash = md5($_COOKIE['cookie']);
            ?>
            <form method=”POST” action=”transfer.php”>
              <input type=”text” name=”toBankId”>
              <input type=”text” name=”money”>
              <input type=”hidden” name=”hash” value=<?=$hash;?>>
              <input type=”submit” name=”submit” value=”Submit”>
            </form>
          復制代碼

            然后在服務器端進行Hash值驗證

          復制代碼
                <?php
                  if(isset($_POST['check'])) {
                       
          $hash = md5($_COOKIE['cookie']);
                       if($_POST['check'== $hash) {
                            doJob();
                       } 
          else {
                  //...

                       }
                  } 
          else {
                //...

                  }
                
          ?>
          復制代碼

            這個方法個人覺得已經(jīng)可以杜絕99%的CSRF攻擊了,那還有1%呢....由于用戶的Cookie很容易由于網(wǎng)站的XSS漏洞而被盜取,這就另外的1%。一般的攻擊者看到有需要算Hash值,基本都會放棄了,某些除外,所以如果需要100%的杜絕,這個不是最好的方法。
            (2).驗證碼

            這個方案的思路是:每次的用戶提交都需要用戶在表單中填寫一個圖片上的隨機字符串,厄....這個方案可以完全解決CSRF,但個人覺得在易用性方面似乎不是太好,還有聽聞是驗證碼圖片的使用涉及了一個被稱為MHTML的Bug,可能在某些版本的微軟IE中受影響。

            (3).One-Time Tokens(不同的表單包含一個不同的偽隨機值)

            在實現(xiàn)One-Time Tokens時,需要注意一點:就是“并行會話的兼容”。如果用戶在一個站點上同時打開了兩個不同的表單,CSRF保護措施不應該影響到他對任何表單的提交。考慮一下如果每次表單被裝入時站點生成一個偽隨機值來覆蓋以前的偽隨機值將會發(fā)生什么情況:用戶只能成功地提交他最后打開的表單,因為所有其他的表單都含有非法的偽隨機值。必須小心操作以確保CSRF保護措施不會影響選項卡式的瀏覽或者利用多個瀏覽器窗口瀏覽一個站點。

            以下我的實現(xiàn):

            1).先是令牌生成函數(shù)(gen_token()):

          復制代碼
               <?php
               function gen_token() {
              //這里我是貪方便,實際上單使用Rand()得出的隨機數(shù)作為令牌,也是不安全的。
              //這個可以參考我寫的Findbugs筆記中的《Random object created and used only once》
                    $token = md5(uniqid(rand(), true));
                    
          return $token;
               }
          復制代碼

            2).然后是Session令牌生成函數(shù)(gen_stoken()):

          復制代碼
               <?php
               
            function gen_stoken() {
                $pToken = "";
                if($_SESSION[STOKEN_NAME]  == $pToken){
                  //沒有值,賦新值
                
            $_SESSION[STOKEN_NAME] = gen_token();
                }    
                else{
                  //繼續(xù)使用舊的值
                }

                 }
               
          ?>
          復制代碼

            3).WEB表單生成隱藏輸入域的函數(shù):  

          復制代碼
               <?php
                 function gen_input() {
                      gen_stoken();
                      echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
                           value=\”" . $_SESSION[STOKEN_NAME] . “\”> “;
                 }
               ?>
          復制代碼

            4).WEB表單結(jié)構(gòu):

          復制代碼
               <?php
                    
          session_start();
                    
          include(”functions.php”);
               
          ?>
               
          <form method=”POST” action=”transfer.php”>
                    
          <input type=”text” name=”toBankId”>
                    
          <input type=”text” name=”money”>
                    
          <? gen_input(); ?>
                    
          <input type=”submit” name=”submit” value=”Submit”>
               
          </FORM>
          復制代碼

            5).服務端核對令牌:

            這個很簡單,這里就不再啰嗦了。

            上面這個其實不完全符合“并行會話的兼容”的規(guī)則,大家可以在此基礎(chǔ)上修改。

           

            其實還有很多想寫,無奈精力有限,暫且打住,日后補充,如果錯漏,請指出:>

            PS:今天下午寫這篇文檔的時候FF崩潰了一次,寫了一半文章的全沒了,郁悶好久T_T.......

            轉(zhuǎn)載請說明出處,謝謝[hyddd(http://www.cnblogs.com/hyddd/)]

          posted @ 2013-04-24 17:00 小馬歌 閱讀(248) | 評論 (0)編輯 收藏
           

          分布式領(lǐng)域CAP理論,
          Consistency(一致性), 數(shù)據(jù)一致更新,所有數(shù)據(jù)變動都是同步的
          Availability(可用性), 好的響應性能
          Partition tolerance(分區(qū)容錯性) 可靠性
          定理:任何分布式系統(tǒng)只可同時滿足二點,沒法三者兼顧。
          忠告:架構(gòu)師不要將精力浪費在如何設(shè)計能滿足三者的完美分布式系統(tǒng),而是應該進行取舍。
          關(guān)系數(shù)據(jù)庫的ACID模型擁有 高一致性 + 可用性 很難進行分區(qū):
          Atomicity原子性:一個事務中所有操作都必須全部完成,要么全部不完成。
          Consistency一致性. 在事務開始或結(jié)束時,數(shù)據(jù)庫應該在一致狀態(tài)。
          Isolation隔離層. 事務將假定只有它自己在操作數(shù)據(jù)庫,彼此不知曉。
          Durability. 一旦事務完成,就不能返回。
          跨數(shù)據(jù)庫事務:2PC (two-phase commit), 2PC is the anti-scalability pattern (Pat Helland) 是反可伸縮模式的,JavaEE中的JTA事務可以支持2PC。因為2PC是反模式,盡量不要使用2PC,使用BASE來回避。
          BASE模型反ACID模型,完全不同ACID模型,犧牲高一致性,獲得可用性或可靠性:
          Basically Available基本可用。支持分區(qū)失敗(e.g. sharding碎片劃分數(shù)據(jù)庫)
          Soft state軟狀態(tài) 狀態(tài)可以有一段時間不同步,異步。
          Eventually consistent最終一致,最終數(shù)據(jù)是一致的就可以了,而不是時時高一致。
          BASE思想的主要實現(xiàn)有
          1.按功能劃分數(shù)據(jù)庫
          2.sharding碎片 
          BASE思想主要強調(diào)基本的可用性,如果你需要High 可用性,也就是純粹的高性能,那么就要以一致性或容錯性為犧牲,BASE思想的方案在性能上還是有潛力可挖的。
          現(xiàn)在NoSQL運動豐富了拓展了BASE思想,可按照具體情況定制特別方案,比如忽視一致性,獲得高可用性等等,NOSQL應該有下面兩個流派:
          1. Key-Value存儲,如Amaze Dynamo等,可根據(jù)CAP三原則靈活選擇不同傾向的數(shù)據(jù)庫產(chǎn)品。
          2. 領(lǐng)域模型 + 分布式緩存 + 存儲 (Qi4j和NoSQL運動),可根據(jù)CAP三原則結(jié)合自己項目定制靈活的分布式方案,難度高。
          這兩者共同點:都是關(guān)系數(shù)據(jù)庫SQL以外的可選方案,邏輯隨著數(shù)據(jù)分布,任何模型都可以自己持久化,將數(shù)據(jù)處理和數(shù)據(jù)存儲分離,將讀和寫分離,存儲可以是異步或同步,取決于對一致性的要求程度。
          不同點:NOSQL之類的Key-Value存儲產(chǎn)品是和關(guān)系數(shù)據(jù)庫頭碰頭的產(chǎn)品BOX,可以適合非Java如PHP RUBY等領(lǐng)域,是一種可以拿來就用的產(chǎn)品,而領(lǐng)域模型 + 分布式緩存 + 存儲是一種復雜的架構(gòu)解決方案,不是產(chǎn)品,但這種方式更靈活,更應該是架構(gòu)師必須掌握的。

          posted @ 2013-04-23 16:18 小馬歌 閱讀(236) | 評論 (0)編輯 收藏
           

          主要闡述以下內(nèi)容:

          磁盤的內(nèi)部結(jié)構(gòu)及實現(xiàn)機制
          分區(qū)的結(jié)構(gòu)及實現(xiàn)機制
          塊的構(gòu)造及原理
          扇區(qū)的構(gòu)造及結(jié)構(gòu)

           

          由于機械硬盤的訪問機制為移動磁頭,并等待磁碟旋轉(zhuǎn),因此在設(shè)計磁盤時,需要考慮如何組織數(shù)據(jù)為順序訪問,并且最大限度提供一次順序訪問盡可能多的數(shù)據(jù)

          由于塊大小設(shè)計直影響到性能、內(nèi)存、空間等因素,因此需要合理設(shè)置塊大小,同時需要合理的為業(yè)務擴展預留升級空間

          機械磁盤讀寫原理

           

          機械硬盤獲取數(shù)據(jù)的方式為:

          1、通過查找meta數(shù)據(jù)(圖中的Data Meta,它是用于描述數(shù)據(jù)的數(shù)據(jù),所以稱為數(shù)據(jù)元,通常這些數(shù)據(jù)會被Cache在磁盤Cache或OS Cache中),捕獲數(shù)據(jù)存儲物理區(qū)域。包括:磁頭指向的磁道位置、磁道的起始、結(jié)束位置等,其中還包含了數(shù)據(jù)塊的標記,如使用狀態(tài),分區(qū)、塊、卷、扇區(qū)等等存儲細節(jié)等,是磁盤運作機制的核心組件

          2、驅(qū)動磁碟旋轉(zhuǎn)(服務器通常磁盤會一直旋轉(zhuǎn),但為了省電及減少對驅(qū)動軸的損耗,通常會對旋轉(zhuǎn)進行優(yōu)化,即空閑時降低磁盤旋轉(zhuǎn)速度或停止轉(zhuǎn)動,但重新驅(qū)動磁盤也會消耗大量的功耗,廠家進行了很多節(jié)能減排的優(yōu)化措施,為綠色環(huán)保做了不少貢獻)

          3、將磁頭(圖中的head)移動到指定的磁道,并從指定的起始位置開始讀取bit流,一直至指定的結(jié)束位置,并將信號傳送至處理程序,最后轉(zhuǎn)化為OS識別的數(shù)據(jù)


          機械硬盤核心組件:

          1、磁盤控制器:內(nèi)部包含用于控制多磁碟并行訪問的機制,包括磁頭移動、盤片旋轉(zhuǎn)、供電、緩存、寫保護等,是整個磁盤的核心組件

          2、分區(qū)(LUN):機械硬盤要被使用,通常先要被分區(qū),并基于分區(qū)進行格式化,格式化將產(chǎn)生Meta數(shù)據(jù),Meta數(shù)據(jù)通常會占用部分磁盤空間。空間大小取決于Block大小、分區(qū)量,Block越小,需要消耗的空間,用于于索引磁盤數(shù)據(jù),接下來我們將還會介紹塊及扇區(qū)的組合方式,多碟磁盤中,磁盤控制器將在每塊磁碟中劃分一塊空間用于該分區(qū)使用,達到并行訪問的目的,提升響應速度(通常某些訪問需要集中訪問的數(shù)據(jù)都集中在某些分區(qū)中),此處的分區(qū)不同于OS中的分區(qū),此處為物理分區(qū)

          3、塊:塊由扇區(qū)構(gòu)成,塊大小決定了數(shù)據(jù)訪問的性能,如果大數(shù)據(jù)如果存儲在小塊中會導致浪費大量的數(shù)據(jù)元空間,并消耗更多的Cache、更多的尋道時間(數(shù)據(jù)是被分散再分區(qū)中的各個位置),所以當應用數(shù)據(jù)塊比較小的時候,我們建議將數(shù)據(jù)劃分成更大的塊,提升性能,但如果塊劃的太大,會導致存儲空間的浪費,當這些不需要的數(shù)據(jù)被LOAD到應用中時,同樣會消耗額外的OS內(nèi)存,通常我們建議根據(jù)業(yè)務的類型,在應用層選擇合適的數(shù)據(jù)集大小,并設(shè)置合理的磁盤塊大小

          塊由扇區(qū)構(gòu)成,扇區(qū)直接構(gòu)建再磁碟的磁面上,每個扇區(qū)為512byte,業(yè)績意味著4KB的塊大小將需要8個扇區(qū)組成(通常Linux設(shè)置塊大小為4K),但再某些數(shù)據(jù)庫應用中我們將數(shù)據(jù)庫的數(shù)據(jù)設(shè)置為更大,如8K、16K、64K,日志設(shè)置為更大,如8K、16K、64K等,結(jié)構(gòu)如下

           

          4、扇區(qū):扇區(qū)是磁盤存儲的單元,扇區(qū)顧名思義,機械硬盤是圓形的,通過分區(qū)格式化后將得到一個一個的扇形結(jié)構(gòu),一條磁道的存儲空間被格式化后將的到大量的扇形結(jié)構(gòu),磁盤的扇區(qū)大小為512byte,其在磁盤的結(jié)構(gòu)如下圖:


          圖中我們可以看到扇區(qū)結(jié)構(gòu)非常復雜,包含數(shù)據(jù)區(qū)域、扇區(qū)分界標識區(qū)域、扇區(qū)間隙區(qū)域,磁盤在處理數(shù)據(jù)時為了更好的保護數(shù)據(jù)及容錯、設(shè)計了扇區(qū)分界標識及扇區(qū)間隙(當然遠遠不止如此)

          posted @ 2013-04-23 09:56 小馬歌 閱讀(275) | 評論 (0)編輯 收藏
           

          Hadoop是Apache的一個項目(http://hadoop.apache.org/),它是一個實現(xiàn)了MapReduce計算模型的可以運用于大型集群并行計算的分布式并行計算編程框架。

          目前,整個Hadoop家族由以下幾個子項目組成:

          Hadoop Common
          Hadoop體系最底層的一個模塊,為Hadoop各子項目提供各種工具,如:配置文件和日志操作等。

          Avro
          Avro是doug cutting主持的RPC項目,有點類似Google的protobuf和Facebook的thrift。avro用來做以后hadoop的RPC,使hadoop的RPC模塊通信速度更快、數(shù)據(jù)結(jié)構(gòu)更緊湊。

          Chukwa
          Chukwa是基于Hadoop的大集群監(jiān)控系統(tǒng),由yahoo貢獻。

          HBase
          基于Hadoop Distributed File System,是一個開源的,基于列存儲模型的分布式數(shù)據(jù)庫。

          HDFS
          分布式文件系統(tǒng)

          Hive
          hive類似CloudBase,也是基于hadoop分布式計算平臺上的提供data warehouse的sql功能的一套軟件。使得存儲在hadoop里面的海量數(shù)據(jù)的匯總,即席查詢簡單化。hive提供了一套QL的查詢語言,以sql為基礎(chǔ),使用起來很方便。

          MapReduce
          實現(xiàn)了MapReduce編程框架

          Pig
          Pig是SQL-like語言,是在MapReduce上構(gòu)建的一種高級查詢語言,把一些運算編譯進MapReduce模型的Map和Reduce中,并且用戶可以定義自己的功能。Yahoo網(wǎng)格運算部門開發(fā)的又一個克隆Google的項目Sawzall。

          ZooKeeper
          Zookeeper是Google的Chubby一個開源的實現(xiàn)。它是一個針對大型分布式系統(tǒng)的可靠協(xié)調(diào)系統(tǒng),提供的功能包括:配置維護、名字服務、分布式同步、組服務等。ZooKeeper的目標就是封裝好復雜易出錯的關(guān)鍵服務,將簡單易用的接口和性能高效、功能穩(wěn)定的系統(tǒng)提供給用戶。

          posted @ 2013-04-22 12:39 小馬歌 閱讀(294) | 評論 (0)編輯 收藏
           
          到http://nginx.org/en/download.html下載最新版本的Nginx并安裝.
          一 下載并安裝pcre庫ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
             tar zxvf pcre-8.30.tar.gz
             ./configure     make    make install
          二 安裝openssl 
             yum -y install openssl openssl-devel
          三 下載tcp_proxy_module
          到 https://github.com/yaoweibin/nginx_tcp_proxy_module 下載
          四 安裝nginx
          export NGINX_VERSION=1.2.1
          curl -O http://nginx.org/downlad/nginx-$NGINX_VERSION.tar.gz
          tar -xvzf nginx-$NGINX_VERSION.tar.gz
          cd nginx-$NGINX_VERSION
          patch -p1 < ../nginx_tcp_proxy_module/tcp.patch
          ./configure --add-module=../nginx_tcp_proxy_module/
          sudo make && make install
           
          啟動nginx上時,服務無法啟動,出現(xiàn)libpcre.so.1 not found的錯誤,解決方法如下:
          先執(zhí)行下述命令,查看
          ---#ldd $(which /usr/sbin/nginx)
          顯示如下:
              linux-vdso.so.1 =>  (0x00007fff7e9db000)
              libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe4629d0000)
              libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fe462799000)
              libpcre.so.1 => not found//果然沒找到
              libz.so.1 => /lib64/libz.so.1 (0x00007fe462582000)
              libc.so.6 => /lib64/libc.so.6 (0x00007fe4621e1000)
              /lib64/ld-linux-x86-64.so.2 (0x00007fe462bfa000)
              libfreebl3.so => /lib64/libfreebl3.so (0x00007fe461f7e000)
              libdl.so.2 => /lib64/libdl.so.2 (0x00007fe461d7a000)
          執(zhí)行如下:
           ----#cd /lib64
           ----#ln -s libpcre.so.0.0.1 libpcre.so.1
          再次查看一下:
           ----#ldd $(which /usr/sbin/nginx)
          顯示已經(jīng)ok了:
              linux-vdso.so.1 =>  (0x00007fff4d7ff000)
              libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb06f13e000)
              libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fb06ef07000)
              libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fb06ecda000)
              libz.so.1 => /lib64/libz.so.1 (0x00007fb06eac4000)
              libc.so.6 => /lib64/libc.so.6 (0x00007fb06e723000)
              /lib64/ld-linux-x86-64.so.2 (0x00007fb06f368000)
              libfreebl3.so => /lib64/libfreebl3.so (0x00007fb06e4c0000)
              libdl.so.2 => /lib64/libdl.so.2 (0x00007fb06e2bc000)
           
          為websocket應用實現(xiàn)負載均衡 http://cnodejs.org/topic/4f16442ccae1f4aa270010b3 
          Reverse Proxy Web Sockets with Nginx and Socket.IO http://www.letseehere.com/reverse-proxy-web-sockets
          posted @ 2013-04-18 17:38 小馬歌 閱讀(501) | 評論 (0)編輯 收藏
           

          前言: 


          websocket相信經(jīng)常逛cnode社區(qū)的孩紙們都知道..具體API使用方式也再熟悉不過了..使用nodejs開發(fā)的websocket服務端也是品種繁多..github上總有你喜歡的..平時耍耍當然沒問題..如果真要是承載一個生產(chǎn)環(huán)境服務的核心..總會有些問題需要你去解決. 

          不可避免的問題: 

          按照一個web請求占用線程數(shù)為參照..我們可以把nodejs稱之為單線程語言..而java的servlet這種應該就是多線程語言了..我們可以想象在高并發(fā)情況下..單線程語言的運行風險還是蠻高的..畢竟如果一個請求出事那么整個線程(進程)就退出了..于是乎停止服務了..為了規(guī)避風險..我們常常會使用負載均衡技術(shù)..保證整個系統(tǒng)的對外正常服務.. 

          解決方案: 

          負載均衡方案目前來講..用apache的也不多了吧..普遍的解決方案是使用nginx配置多個upstream.實現(xiàn)負載均衡..例子如下: 
          http{ 
          upstream http_sr {
          server 192.168.0.2:8080;
          server 192.168.0.3:8080;
          }
          server {
          listen 80 ;
          proxy_pass http_sr;
          }
          }

          這樣對于部署在192.168.0.2和3這兩臺機器上http服務..就通過這臺nginx服務器實現(xiàn)了負載均衡...但是websocket的服務端nginx的http反向代理是不能支持的.從websocket的specs我們可以很明確的其實基于tcp協(xié)議的..http協(xié)議雖然也是基于tcp..它們都使用了tcp的握手方式..但是nginx的http_proxy_pass是不能支持websocket的.. 

          于是我們可以尋根問主..讓nginx支持tcp_proxy_pass..那websocket負載均衡的問題不就迎刃而解了..nginx有豐富的第三方擴展..一頓搜索在github上找到了yaoweibin老師的nginx_tcp_proxy_module 

            

          二話不說下載此模塊..重新編譯nginx(此過程略吧大家應該都懂) ..修改nginx配置文件加入下面conf: 
          tcp { 
          upstream websocket {
          server 192.168.0.2:8080;
          server 192.168.0.3:8080;
          check interval=3000 rise=2 fall=5 timeout=1000;
          }
          server {
          listen 80;
          proxy_pass websocket;
          }
          }

          這個module的作者在description寫到: 
           The motivation of writing these modules is Nginx's high performance and 
          robustness. At first, I developed this module just for general TCP
          proxy. And now, this module is frequently used in websocket reverse
          proxying.

          目前基本就是用來做websocket反向代理的..測試后確實是支持的..非常感謝module的開發(fā)者另外值得注意的是你要在nginx里同時配置tcp和http..那么它們是不能listen同一端口的..
          posted @ 2013-04-18 17:38 小馬歌 閱讀(2271) | 評論 (0)編輯 收藏
           

          nginx_tcp_proxy_module 為 Nginx 增加對 TCP 的反向代理支持,提供連接有效性檢測和狀態(tài)監(jiān)控。

          配置示例:

          upstream cluster {
              # simple round-robin
              server 127.0.0.1:3306;
              server 127.0.0.1:1234;

              check interval=3000 rise=2 fall=5 timeout=1000;

              #check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;

              #check interval=3000 rise=2 fall=5 timeout=1000 type=http;
              #check_http_send "GET / HTTP/1.0\r\n\r\n";
              #check_http_expect_alive http_2xx http_3xx;
          }

          add the feature of tcp proxy with nginx, with health check and status monitor — More...

          http://yaoweibin.github.com/nginx_tcp_proxy_module

          Issues
          #74may be a debug in ngx_tcp_upstream_check_broken_connection?by chenbk85  2013-04-17
          #73reverse proxy tcp and http on the same portby beurdouche  2013-03-26
          #72TCP error_logby splitice  2013-03-24
          #31basic auth htpsswdby manguz  2013-03-21
          #71upstream ssl suppoprt!by sleets  2013-03-13

          master分支代碼最近更新:2013-03-28

          posted @ 2013-04-18 17:19 小馬歌 閱讀(334) | 評論 (0)編輯 收藏
          僅列出標題
          共95頁: First 上一頁 26 27 28 29 30 31 32 33 34 下一頁 Last 
           
          主站蜘蛛池模板: 珠海市| 古交市| 洛阳市| 方城县| 岚皋县| 虎林市| 东丰县| 寻乌县| 扶风县| 静乐县| 靖州| 莱芜市| 石楼县| 洛阳市| 津市市| 延安市| 木里| 台南县| 乌兰察布市| 安徽省| 阿坝县| 久治县| 石林| 东莞市| 金阳县| 牡丹江市| 双辽市| 赤水市| 政和县| 海晏县| 铜山县| 灵山县| 德兴市| 西吉县| 葫芦岛市| 米易县| 清原| 淮南市| 监利县| 双桥区| 都江堰市|