• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            colorful

            zc qq:1337220912

             

            關于#pragma once(轉)

            在所有的預處理指令中,#pragma指令可能是最復雜的了,它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個 編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統專有的特征。依據定義,編譯指示是機器或操作系統專有的,且對于每個 編譯器都是不同的。其格式一般為:#pragma para,其中para為參數,下面來看一些常用的參數。

            (Each   implementation   of   C   and   C++   supports   some   features   unique   to   its   host   machine   or   operating   system.   Some   programs,   for   instance,   need   to   exercise   precise   control   over   the   memory   areas   where   data   is   placed   or   to   control   the   way   certain   functions   receive   parameters.   The   #pragma   directives   offer   a   way   for   each   compiler   to   offer   machine-   and   operating-system-specific   features   while   retaining   overall   compatibility   with   the   C   and   C++   languages.   Pragmas   are   machine-   or   operating-system-specific   by   definition,   and   are   usually   different   for   every   compiler. )

            (1)message參數。Message參數是我最喜歡的一個參數,它能夠在編譯信息輸出窗口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:

            #pragma message(“消息文本”),當編譯器遇到這條指令時就在編譯輸出窗口中將消

            息文本打印出來。

            當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什么地方定義了_X86這個宏可以用下面的方法:    

            #ifdef   _X86    

            #pragma   message(“_X86   macro   activated!”)    

            #endif    

            當我們定義了_X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示

            “_X86   macro activated!”。我們就不會因為不記得自己定義的一些特定的宏而抓耳

            撓腮了。    

               

            (2)另一個使用得比較多的pragma參數是code_seg。格式如:    

            #pragma   code_seg(   ["section-name"[,"section-class"]   ]   )    

            它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。    

            (3)#pragma once (比較常用)。只要在頭文件的最開始加入這條指令就能夠保證

            頭文件被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到兼容性并沒有太多的使用它。    

            (4)#pragma hdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB

            可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragma   startup指定編

            譯優先級,如果使用了#pragma package(smart_init),BCB就會根據優先級的大小先

            后編譯。    

               

            (5)#pragma   resource   "*.dfm"表示把*.dfm文件中的資源加入工程。*.dfm中包括窗

            體外觀的定義。    

               

            (6)#pragma   warning(disable : 4507 34; once   :   4385;   error   :   164   )     等價于:    

            #pragma   warning(disable:4507   34)   //   不顯示4507和34號警告信息    

            #pragma   warning(once:4385)   //   4385號警告信息僅報告一次    

            #pragma   warning(error:164)   //   把164號警告信息作為一個錯誤。    

            同時這個pragma   warning   也支持如下格式:    

            #pragma   warning(   push   [   ,n   ]   )    

            #pragma   warning(   pop   )    

            這里n代表一個警告等級(1---4)。    

            #pragma   warning(   push   )保存所有警告信息的現有的警告狀態。    

            #pragma   warning(   push,   n)保存所有警告信息的現有的警告狀態,并且全局警告    

            等級設定為n。    

            #pragma   warning(   pop   )向棧中彈出最后一個警告信息,在入棧和出棧之間所作

            的一切改動取消。例如:    

            #pragma   warning(   push   )    

            #pragma   warning(   disable   :   4705   )    

            #pragma   warning(   disable   :   4706   )    

            #pragma   warning(   disable   :   4707   )    

            //.......    

            #pragma   warning(   pop   )    

            在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。    

            (7)#pragma   comment(...) 該指令將一個注釋記錄放入一個對象文件或可執行

            文件中。常用的lib關鍵字,可以幫我們連入一個庫文件。  

               

            (8)#pragma   pack() 我們知道在VC中,對于想結構體Struct這樣的類型,VC采

            用8字節對齊的方式,如果我們不想使用8字節對齊(在網絡變成中經常需要這樣),我們可以在結構體前面加上    

            #pragma   pack(1)    

            struct    

            {    

            ......    

            }    

            #pragma   pack()

            二.#if _MSC_VER > 1000    #pragma once    #endif  

            (1)_MSC_VER。 Defines   the   compiler   version.   Defined   as   1200   for   Microsoft   Visual   C++   6.0.   Always   defined.   

            (2)#if   _MSC_VER   > 1000的意思是指如果vc編譯器的版本大于1000則這個語句

            被編譯!大概小于1000的版本不支持#pragma   once這個語句。

            (3)#pragma   once 。Specifies   that   the   file,   in   which   the   pragma   resides,

            will   be   included   (opened)   only   once   by   the   compiler   in   a   build.   A   common   use   for   this   pragma   is   the   following:  

            //header.h  

            #pragma   once  

            //   Your   C   or   C++   code   would   follow:  

            #pragma   once    加入頭文件的第一行 指示這個文件在編譯時只被編譯器文件編譯

            (打開)一次!一般用到.h中防止文件被重復包括!  

            三.#pragma once    與   #ifndef    #define   #endif  

            (1)從定義上即可看出,pragmas指令是某種機器或者操作系統獨有的,并且不同編譯器也常常有別。#pragma once這個是編譯器相關指令,就是說在這個編譯系統

            上能用,但是在其他編譯系統 不一定型,也就是說移植型差。不過現在基本上

            已經是每個編譯器都有這個定義了。

            #ifndef   #define #endif這個是語言支持指令,這是C/C++語言中的宏定義,通過

            宏定義避免文件多次編譯。所以在所有支持C++語言的編譯器上都是有效的。如果寫的程序要   跨平臺,最好使用這種方式。

            (2)#ifndef   #define #endif   #ifndef 還有其它作用,防止頭文件重復引用只是

            其中一個應用而已。#pragma只有微軟支持。

            (3)#ifndef   #define #endif   他讀到#ifndef之后,如果已經定義過了,就會跳過

            這一大片,一直到#endif為止。這將增加build時間,因為每次compiler都會打開這個文件,然后搜索全文件一遍。而如果碰到了#pragma once,他就會立刻停止,

            關閉打開的這個文件。在某種程度上減少 了build時間。一般用法:    

            #ifndef  

            #define  

            #pragma   once  

            .....  

            #endif  

            四. #pragma   data_seg(".mdata").....#pragma data_seg()可以讓編譯器把兩者之間

            的所有已初始化變量放入一個新的.mdata段中。應用之一是單應用程序。

            有的時候我們可能想讓一個應用程序只啟動一次,就像單件模式(singleton)一樣,實現的方法可能有多種,這里說說用#pragma data_seg的實現,很是簡潔便利。

            應用程序的入口文件前面加上:

            #pragma data_seg("flag_data")

            int app_count = 0;

            #pragma data_seg()

            #pragma comment(linker,"/SECTION:flag_data,RWS")

            然后程序啟動的地方加上

            if(app_count>0) // 如果計數大于0,則退出應用程序。

            {

            //MessageBox(NULL, "已經啟動一個應用程序", "Warning", MB_OK);

            //printf("no%d application", app_count);

            return FALSE;

            } app_count++;

            總結:

            1. #ifndef 由語言支持所以移植性好,#pragma 可以避免名字沖突

            2. 調查一下<stdlib.h>和<iostream>等標準庫, 用得都是#ifndef, 我個人推薦這種方式.

            posted @ 2012-03-08 10:09 多彩人生 閱讀(553) | 評論 (0)編輯 收藏

            兩種高性能I/O設計模式的比較

            涉及到事件分享器的兩種模式稱為:Reactor and Proactor [1]. Reactor模式是基于同步I/O的,而Proactor模式是和異步I/O相關的. 在Reactor模式中,事件分離者等待某個事件或者可應用或個操作的狀態發生(比如文件描述符可讀寫,或者是socket可讀寫),事件分離者就把這個事件傳給事先注冊的事件處理函數或者回調函數,由后者來做實際的讀寫操作。

            而在Proactor模式中,事件處理者(或者代由事件分離者發起)直接發起一個異步讀寫操作(相當于請求),而實際的工作是由操作系統來完成的。發起時,需要提供的參數包括用于存放讀到數據的緩存區,讀的數據大小,或者用于存放外發數據的緩存區,以及這個請求完后的回調函數等信息。事件分離者得知了這個請求,它默默等待這個請求的完成,然后轉發完成事件給相應的事件處理者或者回調。舉例來說,在Windows上事件處理者投遞了一個異步IO操作(稱有overlapped的技術),事件分離者等IOCompletion事件完成[1]. 這種異步模式的典型實現是基于操作系統底層異步API的,所以我們可稱之為“系統級別”的或者“真正意義上”的異步,因為具體的讀寫是由操作系統代勞的。


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

            這篇文章探討并比較兩種用于TCP服務器的高性能設計模式. 除了介紹現有的解決方案, 還提出了一種更具伸縮性,只需要維護一份代碼并且跨平臺的解決方案(含代碼示例), 以及其在不同平臺上的微調. 此文還比較了java,c#,c++對各自現有以及提到的解決方案的實現性能.

            系統I/O 可分為阻塞型, 非阻塞同步型以及非阻塞異步型[1, 2]. 阻塞型I/O意味著控制權只到調用操作結束了才會回到調用者手里. 結果調用者被阻塞了, 這段時間了做不了任何其它事情. 更郁悶的是,在等待IO結果的時間里,調用者所在線程此時無法騰出手來去響應其它的請求,這真是太浪費資源了。拿read()操作來說吧, 調用此函數的代碼會一直僵在此處直至它所讀的socket緩存中有數據到來.

            相比之下,非阻塞同步是會立即返回控制權給調用者的。調用者不需要等等,它從調用的函數獲取兩種結果:要么此次調用成功進行了;要么系統返回錯誤標識告訴調用者當前資源不可用,你再等等或者再試度看吧。比如read()操作, 如果當前socket無數據可讀,則立即返回EWOULBLOCK/EAGAIN,告訴調用read()者"數據還沒準備好,你稍后再試".

            在非阻塞異步調用中,稍有不同。調用函數在立即返回時,還告訴調用者,這次請求已經開始了。系統會使用另外的資源或者線程來完成這次調用操作,并在完成的時候知會調用者(比如通過回調函數)。拿Windows的ReadFile()或者POSIX的aio_read()來說,調用它之后,函數立即返回,操作系統在后臺同時開始讀操作。

            在以上三種IO形式中,非阻塞異步是性能最高、伸縮性最好的。

            這篇文章探討不同的I/O利用機制并提供一種跨平臺的設計模式(解決方案). 希望此文可以給于TCP高性能服務器開發者一些幫助,選擇最佳的設計方案。下面我們會比較 Java, c#, C++各自對探討方案的實現以及性能. 我們在文章的后面就不再提及阻塞式的方案了,因為阻塞式I/O實在是缺少可伸縮性,性能也達不到高性能服務器的要求。

            兩種IO多路復用方案:Reactor and Proactor

            一般情況下,I/O 復用機制需要事件分享器(event demultiplexor [1, 3]). 事件分享器的作用,即將那些讀寫事件源分發給各讀寫事件的處理者,就像送快遞的在樓下喊: 誰的什么東西送了, 快來拿吧。開發人員在開始的時候需要在分享器那里注冊感興趣的事件,并提供相應的處理者(event handlers),或者是回調函數; 事件分享器在適當的時候會將請求的事件分發給這些handler或者回調函數.

            涉及到事件分享器的兩種模式稱為:Reactor and Proactor [1]. Reactor模式是基于同步I/O的,而Proactor模式是和異步I/O相關的. 在Reactor模式中,事件分離者等待某個事件或者可應用或個操作的狀態發生(比如文件描述符可讀寫,或者是socket可讀寫),事件分離者就把這個事件傳給事先注冊的事件處理函數或者回調函數,由后者來做實際的讀寫操作。

            而在Proactor模式中,事件處理者(或者代由事件分離者發起)直接發起一個異步讀寫操作(相當于請求),而實際的工作是由操作系統來完成的。發起時,需要提供的參數包括用于存放讀到數據的緩存區,讀的數據大小,或者用于存放外發數據的緩存區,以及這個請求完后的回調函數等信息。事件分離者得知了這個請求,它默默等待這個請求的完成,然后轉發完成事件給相應的事件處理者或者回調。舉例來說,在Windows上事件處理者投遞了一個異步IO操作(稱有overlapped的技術),事件分離者等IOCompletion事件完成[1]. 這種異步模式的典型實現是基于操作系統底層異步API的,所以我們可稱之為“系統級別”的或者“真正意義上”的異步,因為具體的讀寫是由操作系統代勞的。

            舉另外個例子來更好地理解Reactor與Proactor兩種模式的區別。這里我們只關注read操作,因為write操作也是差不多的。下面是Reactor的做法:

            • 某個事件處理者宣稱它對某個socket上的讀事件很感興趣;
            • 事件分離者等著這個事件的發生;
            • 當事件發生了,事件分離器被喚醒,這負責通知先前那個事件處理者;
            • 事件處理者收到消息,于是去那個socket上讀數據了. 如果需要,它再次宣稱對這個socket上的讀事件感興趣,一直重復上面的步驟;

            下面再來看看真正意義的異步模式Proactor是如何做的:

            • 事件處理者直接投遞發一個寫操作(當然,操作系統必須支持這個異步操作). 這個時候,事件處理者根本不關心讀事件,它只管發這么個請求,它魂牽夢縈的是這個寫操作的完成事件。這個處理者很拽,發個命令就不管具體的事情了,只等著別人(系統)幫他搞定的時候給他回個話。
            • 事件分離者等著這個讀事件的完成(比較下與Reactor的不同);
            • 當事件分離者默默等待完成事情到來的同時,操作系統已經在一邊開始干活了,它從目標讀取數據,放入用戶提供的緩存區中,最后通知事件分離者,這個事情我搞完了;
            • 事件分享者通知之前的事件處理者: 你吩咐的事情搞定了;
            • 事件處理者這時會發現想要讀的數據已經乖乖地放在他提供的緩存區中,想怎么處理都行了。如果有需要,事件處理者還像之前一樣發起另外一個寫操作,和上面的幾個步驟一樣。

            現行做法

            開源C++開發框架 ACE[1, 3](Douglas Schmidt, et al.開發) 提供了大量平臺獨立的底層并發支持類(線程、互斥量等). 同時在更高一層它也提供了獨立的幾組C++類,用于實現Reactor及Proactor模式。 盡管它們都是平臺獨立的單元,但他們都提供了不同的接口.

            ACE Proactor在MS-Windows上無論是性能還在健壯性都更勝一籌,這主要是由于Windows提供了一系列高效的底層異步API. [4, 5].

            (這段可能過時了點吧) 不幸的是,并不是所有操作系統都為底層異步提供健壯的支持。舉例來說, 許多Unix系統就有麻煩.因此, ACE Reactor可能是Unix系統上更合適的解決方案. 正因為系統底層的支持力度不一,為了在各系統上有更好的性能,開發者不得不維護獨立的好幾份代碼: 為Windows準備的ACE Proactor以及為Unix系列提供的ACE Reactor.

            就像我們提到過的,真正的異步模式需要操作系統級別的支持。由于事件處理者及操作系統交互的差異,為Reactor和Proactor設計一種通用統一的外部接口是非常困難的。這也是設計通行開發框架的難點所在。

            更好的解決方案

            在文章這一段時,我們將嘗試提供一種融合了Proactor和Reactor兩種模式的解決方案. 為了演示這個方案,我們將Reactor稍做調整,模擬成異步的Proactor模型(主要是在事件分離器里完成本該事件處理者做的實際讀寫工作,我們稱這種方法為"模擬異步")。 下面的示例可以看看read操作是如何完成的:

            • 事件處理者宣稱對讀事件感興趣,并提供了用于存儲結果的緩存區、讀數據長度等參數;
            • 調試者等待(比如通過select());
            • 當有事件到來(即可讀),調試者被喚醒, 調試者去執行非阻塞的讀操作(前面事件處理者已經給了足夠的信息了)。讀完后,它去通知事件處理者。
            • 事件處理者這時被知會讀操作已完成,它擁有完整的原先想要獲取的數據了.

            我們看到,通過為分離者(也就上面的調試者)添加一些功能,可以讓Reactor模式轉換為Proactor模式。所有這些被執行的操作,其實是和Reactor模型應用時完全一致的。我們只是把工作打散分配給不同的角色去完成而已。這樣并不會有額外的開銷,也不會有性能上的的損失,我們可以再仔細看看下面的兩個過程,他們實際上完成了一樣的事情:

            標準的經典的 Reactor模式:

            • 步驟 1) 等待事件 (Reactor 的工作)
            • 步驟 2) 發"已經可讀"事件發給事先注冊的事件處理者或者回調 ( Reactor 要做的)
            • 步驟 3) 讀數據 (用戶代碼要做的)
            • 步驟 4) 處理數據 (用戶代碼要做的)

            模擬的Proactor模式:

            • 步驟 1) 等待事件 (Proactor 的工作)
            • 步驟 2) 讀數據(看,這里變成成了讓 Proactor 做這個事情)
            • 步驟 3) 把數據已經準備好的消息給用戶處理函數,即事件處理者(Proactor 要做的)
            • 步驟 4) 處理數據 (用戶代碼要做的)

            在沒有底層異步I/O API支持的操作系統,這種方法可以幫我們隱藏掉socket接口的差異(無論是性能還是其它), 提供一個完全可用的統一"異步接口"。這樣我們就可以開發真正平臺獨立的通用接口了。

            TProactor

            我們提出的TProactor方案已經由TerabitP/L [6]公司實現了. 它有兩種實現: C++的和Java的.C++版本使用了ACE平臺獨立的底層元件,最終在所有操作系統上提供了統一的異步接口。

            TProactor中最重要的組件要數Engine和WaitStrategy了. Engine用于維護異步操作的生命周期;而WaitStrategy用于管理并發策略. WaitStrategy和Engine一般是成對出現的, 兩者間提供了良好的匹配接口.

            Engines和等待策略被設計成高度可組合的(完整的實現列表請參照附錄1)。TProactor是高度可配置的方案,通過使用異步內核API和同步Unix API(select(), poll(), /dev/poll (Solaris 5.8+), port_get (Solaris 5.10),RealTime (RT) signals (Linux 2.4+), epoll (Linux 2.6), k-queue (FreeBSD) ),它內部實現了三種引擎(POSIX AIO, SUN AIO and Emulated AIO)并隱藏了六類等待策略。TProactor實現了和標準的 ACE Proactor一樣的接口。這樣一來,為不同平臺提供通用統一的只有一份代碼的跨平臺解決方案成為可能。

            Engines和WaitStrategies可以像樂高積木一樣自由地組合,開發者可以在運行時通過配置參數來選擇合適的內部機制(引擎和等待策略)。可以根據需求設定配置,比如連接數,系統伸縮性,以及運行的操作系統等。如果系統支持相應的異步底層API,開發人員可以選擇真正的異步策略,否則用戶也可以選擇使用模擬出來的異步模式。所有這一切策略上的實現細節都不太需要關注,我們看到的是一個可用的異步模型。

            舉例來說,對于運行在Sun Solaris上的HTTP服務器,如果需要支持大量的連接數,/dev/poll或者port_get()之類的引擎是比較合適的選擇;如果需要高吞吐量,那使用基本select()的引擎會更好。由于不同選擇策略內在算法的問題,像這樣的彈性選擇是標準ACE Reactor/Proactor模式所無法提供的(見附錄2)。

            在性能方面,我們的測試顯示,模擬異步模式并未造成任何開銷,沒有變慢,反倒是性能有所提升。根據我們的測試結果,TProactor相較標簽的ACE Reactor在Unix/Linux系統上有大約10-35%性能提升,而在Windows上差不多(測試了吞吐量及響應時間)。

            性能比較 (JAVA / C++ / C#).

            除了C++,我們也在Java中實現了TProactor. JDK1.4中, Java僅提供了同步方法, 像C中的select() [7, 8]. Java TProactor基于Java的非阻塞功能(java.nio包),類似于C++的TProactor使用了select()引擎.

            圖1、2顯示了以 bits/sec為單位的傳輸速度以及相應的連接數。這些圖比較了以下三種方式實現的echo服務器:標準ACE Reactor實現(基于RedHat Linux9.0)、TProactor C++/Java實現(Microsoft Windows平臺及RedHat v9.0), 以及C#實現。測試的時候,三種服務器使用相同的客戶端瘋狂地連接,不間斷地發送固定大小的數據包。

            這幾組測試是在相同的硬件上做的,在不同硬件上做的相對結果對比也是類似。

            圖 1. Windows XP/P4 2.6GHz HyperThreading/512 MB RAM.
            圖 2. Linux RedHat 2.4.20-smp/P4 2.6GHz HyperThreading/512 MB RAM.

            用戶代碼示例

            下面是TProactor Java實現的echo服務器代碼框架。總的來說,開發者只需要實現兩個接口:一是OpRead,提供存放讀結果的緩存;二是OpWrite,提供存儲待寫數據的緩存區。同時,開發者需要通過回調onReadComplated()和onWriteCompleted()實現協議相關的業務代碼。這些回調會在合適的時候被調用.

             
            class EchoServerProtocol implements AsynchHandler
            {
             
              AsynchChannel achannel = null;
             
              EchoServerProtocol( Demultiplexor m,  SelectableChannel channel ) 
              throws Exception
              {
                this.achannel = new AsynchChannel( m, this, channel );
              }
             
              public void start() throws Exception
              {
                // called after construction
                System.out.println( Thread.currentThread().getName() + 
            	": EchoServer protocol started" );
                achannel.read( buffer);
              }
             
              public void onReadCompleted( OpRead opRead ) throws Exception
              {
                if ( opRead.getError() != null )
                {
                  // handle error, do clean-up if needed
                  System.out.println( "EchoServer::readCompleted: " + 
                  opRead.getError().toString());
                  achannel.close();
                  return;
                }
             
                if ( opRead.getBytesCompleted () <= 0)
                {
                  System.out.println("EchoServer::readCompleted: Peer closed " 
            	   + opRead.getBytesCompleted();
                  achannel.close();
                  return;
                }
             
                ByteBuffer buffer = opRead.getBuffer();
             
                achannel.write(buffer);
              }
             
              public void onWriteCompleted(OpWrite opWrite) 
              throws Exception
              {
                // logically similar to onReadCompleted
                ...
              }
            }
            

            結束語

            TProactor為多個平臺提供了一個通用、彈性、可配置的高性能通訊組件,所有那些在附錄2中提到的問題都被很好地隱藏在內部實現中了。

            從上面的圖中我們可以看出C++仍舊是編寫高性能服務器最佳選擇,雖然Java已緊隨其后。然而因為Java本身實現上的問題,其在Windows上表現不佳(這已經應該成為歷史了吧)。

            需要注意的是,以上針對Java的測試,都是以裸數據的形式測試的,未涉及到數據的處理(影響性能)。

            縱觀AIO在Linux上的快速發展[9], 我們可以預計Linux內核API將會提供大量更加強健的異步API, 如此一來以后基于此而實現的新的Engine/等待策略將能輕松地解決能用性方面的問題,并且這也能讓標準ACE Proactor接口受益。

            附錄 I

            TProactor中實現的Engines 和 等待策略

            引擎類型 等待策略 操作系統
            POSIX_AIO (true async)
            aio_read()/aio_write()
            aio_suspend()
            Waiting for RT signal
            Callback function
            POSIX complained UNIX (not robust)
            POSIX (not robust)
            SGI IRIX, LINUX (not robust)
            SUN_AIO (true async)
            aio_read()/aio_write()
            aio_wait() SUN (not robust)
            Emulated Async
            Non-blocking read()/write()
            select()
            poll()
            /dev/poll
            Linux RT signals
            Kqueue
            generic POSIX
            Mostly all POSIX implementations
            SUN
            Linux
            FreeBSD

            附錄 II

            所有同步等待策略可劃分為兩組:

            • edge-triggered (e.g. Linux實時信號) - signal readiness only when socket became ready (changes state);
            • level-triggered (e.g. select(), poll(), /dev/poll) - readiness at any time.

            讓我們看看這兩組的一些普遍的邏輯問題:

            • edge-triggered group: after executing I/O operation, the demultiplexing loop can lose the state of socket readiness. Example: the "read" handler did not read whole chunk of data, so the socket remains still ready for read. But the demultiplexor loop will not receive next notification.
            • level-triggered group: when demultiplexor loop detects readiness, it starts the write/read user defined handler. But before the start, it should remove socket descriptior from theset of monitored descriptors. Otherwise, the same event can be dispatched twice.
            • Obviously, solving these problems adds extra complexities to development. All these problems were resolved internally within TProactor and the developer should not worry about those details, while in the synch approach one needs to apply extra effort to resolve them.

            posted @ 2012-03-06 21:04 多彩人生 閱讀(434) | 評論 (0)編輯 收藏

            雙緩沖消息隊列-減少鎖競爭

            在網絡應用服務器端, 為了性能和防止阻塞, 經常會把邏輯處理和I/O處理分離:
            I/O網絡線程處理I/O事件: 數據包的接收和發送, 連接的建立和維護等.
            邏輯線程要對收到的數據包進行邏輯處理.

            通常網絡線程和邏輯線程之間是通過數據包隊列來交換信息, 簡單來說就是一個生產者-消費者模式.
            這個隊列是多個線程在共享訪問必須加鎖, 意味著每次訪問都要加鎖。如何更好的如何減少鎖競爭次數呢 ?

            方案一 雙緩沖消息隊列:

            兩個隊列,一個給邏輯線程讀,一個給IO線程用來寫,當邏輯線程讀完隊列后會將自己的隊列與IO線程的隊列相調換。
            IO線程每次寫隊列時都要加鎖,邏輯線程在調換隊列時也需要加鎖,但邏輯線程在讀隊列時是不需要加鎖的.

            隊列緩沖區的大小要根據數據量的大小進行調整的,如果緩沖區很小,就能更及時的處理數據,但吞吐量以及出現資源競爭的幾率大多了。

            可以給緩沖隊列設置最大上限,超過上限的數量之后,將包丟棄不插入隊列。
            另外,雙緩沖的實現也有不同策略的,

            一是讀操作優先,就是生產者只要發現空閑緩沖,馬上swap,
            二是寫線程只有在當前的緩沖區寫滿了,才進行swap操作。
            三是上層邏輯按照幀率來處理,每一幀的時候將雙層緩沖隊列調換一下,取一個隊列來處理即可

             


            方案二 提供一個隊列容器:

            提供一個隊列容器,里面有多個隊列,每個隊列都可固定存放一定數量的消息。網絡IO線程要給邏輯線程投遞消息時,會從隊列容器中取一個空隊列來使用,直到將該隊列填滿后再放回容器中換另一個空隊列。而邏輯線程取消息時是從隊列容器中取一個有消息的隊列來讀取,處理完后清空隊列再放回到容器中。

            這樣便使得只有在對隊列容器進行操作時才需要加鎖,而IO線程和邏輯線程在操作自己當前使用的隊列時都不需要加鎖,所以鎖競爭的機會大大減少了。

            這里為每個隊列設了個最大消息數,看來好像是打算只有當IO線程寫滿隊列時才會將其放回到容器中換另一個隊列。那這樣有時也會出現IO線程未寫滿一個隊列,而邏輯線程又沒有數據可處理的情況,特別是當數據量很少時可能會很容易出現[這個可以通過設置超時來處理, 如果當前時間-向隊列放入第一個包的時間 > 50 ms, 就將其放回到容器中換另一個隊列]。

            通常我們邏輯服務器會以場景來劃分線程,不同線程執行不同場景.一個線程可以執行多個場景.因為玩家屬于場景,我們會把玩家數據,包括其緩沖池丟給場景 去處理.

            posted @ 2012-03-06 13:49 多彩人生 閱讀(580) | 評論 (0)編輯 收藏

            怎么下載MANGOS源碼

            怎么下載MANGOS源碼

            mangos-gui Mangos的GUI版控制工具(svn地址)
            http://mangos-gui.googlecode.com/svn/trunk/

            MangosWFE網站系統(SVN更新地址)
            http://opensvn.csie.org/MangosWFE/

            mangos-luascript腳本(svn更新地址)
            http://mangos-luascript.googlecode.com/svn/trunk

            HTTP地址 http://download.toaoto.cn FTP地址 ftp://ftp.toaoto.cn FTP帳號和密碼:wow
            Mangos SVN:
            http://opensvn.csie.org/mangoswebsite
            MangosWeb

            http://svn.sourceforge.net/viewvc/mangos/trunk/?view=log
            MangosLog

            http://svn.sourceforge.net/svnroot/mangos/trunk/
            MangosTrunk

            http://www.mangosproject.org/forum/
            MangosForum

            鑒于有些新手不會下載源碼...所以我寫個帖子出來..給新手參考參考.
            1:先下載TortoiseSVN-1.3.5.6804-svn-1.3.2這個工具
            LanguagePack-1.3.5.6804-win32-zh_CN(這個是語言補丁)
            下下來安裝好以后在Setting里面改下語言就OK了
            http://luwakin.gbdisk.com/這位帥哥的網絡硬盤里面有!
            然后安裝...........(廢話....)

            2:源碼地址
            https://opensvn.csie.org/ScriptDev/trunk ---------------ScriptDev

            https://mangos.svn.sourceforge.net/svnroot/mangos--------------Mangos

            Silver DataBase Communities SVN is located here:
            https://opensvn.csie.org/SDB/
            https://opensvn.csie.org/traccgi/SDB/trac.cgi/timeline

            SVN contains the latest table data and DB updates, in XML or SQL formats only. Full DB releases can be found in "Index of the Released DBs" section.

            https://opensvn.csie.org/SDB

            https://opensvn.csie.org/mangoDB

            http://opensvn.csie.org/MangosDatabaseProject

            3:參照我的圖下載吧!!!!
            (1)先創建個文件夾.然后右鍵選SVN取出,在里面輸入上面的源碼地址!
            (2)然后確定后就自動下載了.下載完畢要導出下載的文件...參照最后張圖導出就OK啦!

            1) 首先,你需要有2個軟件,第一個就是下載及更新MANGOS源碼用的TortoiseSVN.第二個就是VS2003或者VS2005.
            注意:這里推薦使用VS2003.因為在使用VS2005時,出現了很多錯誤...導致編譯失敗...
            而用VS2003時,沒有出現任何問題.

            MaNGOS

            2) 在作完第一步后,我們可以在電腦里新建一個文件夾.這里以C:\盤來舉例.例如C:\mangos.
            在建好的文件夾上點擊右鍵.你會看到"SVN Checkout..." , 點了...跳出來一個框框,
            在"URL of repository"里面輸入"https://mangos.svn.sourceforge.net/svnroot/mangos"
            在這個下面有一個選項,你需要選中"HEAD",好了...點擊OK吧.

            3) 作完上面這一步后, 用右鍵點擊MANGOS文件夾,你會看到"SVN Update",點擊他吧,這會讓你的MANGOS源碼升級,以后每次只要點擊"SVN Update",就可以讓你的MANGOS源碼保持在最新版.

            4) 接下來,就是開始編譯MANGOS了~~~是不是很興奮了...
            打開VS2003, 選擇"Open Project",然后打開"C:\Mangos\win\" 選擇"mangosdVC71.sln"
            如果你用的是VS2005, 就要打開"mangosdVC80.sln"

            5) 接下來的這一步,我建議你看一下圖片來明白你需要作什么.

            6) 呵呵.這一步就是等待了.編譯是需要時間的...等VS幫你作好吧.

            7)嗯...過了一會~~~ 編譯好了.

            你可以"C:\Mangos\bin\release\"里面找到編譯好的MANGOS. 需要的是這幾個文件

            libeay32.dll, libmySQL.dll, mangosd.exe, MaNGOSScript.dll, realmd.exe

            嗯, 好像還少什么

            mangosd.conf和realmd.conf是不是, 我們可以在"C:\Mangos\src\mangosd\"和C:\mangos\src\realmd\"里面找到mangosd.conf.in , realmd.conf.in
            啊 名字不一樣 嗯 把后面的.in去掉吧.

            SQL文件了可以在"C:\Mangos\sql\"里面找到...當然只有一個架構的..嘿嘿...
            升級的SQL文件可以在"E:\Mangos\sql\updates"里面找到...

            ScriptDev

            下載最新版本的ScriptDev并將和MaNGOS一起使用,你需要以下幾個步驟:
            1) 新建一個文件夾,并命名。在文件夾上點右鍵。。。
            如同上面Mangos源碼的下載方式一樣, 使用SVN Checkout, URL地址為 https://opensvn.csie.org/ScriptDev/trunk , 然后使用 SVN Update
            2) 在作完上面的步驟后,在這個文件夾上點右鍵, 選擇“TortoiseSVN” ,再選擇 “Export“
            選擇一個新建文件夾.然后點OK,好了 最新版本的ScriptDev已經出來了
            3) 到Export好的這個新文件夾里面,復制 SQL, SRC, WIN 這三個文件夾到Mangos文件夾里,
            好了,當你編譯MANGOS時,將帶著最新版本的ScriptDev...
            重要:當有ScriptDev的新版出來時。。。你必需重復上面的動作。。。

            好了...一個最新版本的MANGOS在你手上出現了...是由你自己完成的哦...是不是特別興奮~~~

            下面的需要的軟件,推薦使用2003...2005好多問題的說...
            TortoiseSVN
            點此下載
            VC++ Express Edition
            點此下載

            數據庫可以用990100的0712數據庫...

            http://bbs.99nets.com/read.php?tid=426801&fpage=2

            編譯好的MANGOS如何安裝也可以參考990100的說明...

            好了。。。帶著ScriptDev的MaNGOS將成為真正的MaNGOS最新版。。。


            posted @ 2012-03-06 11:39 多彩人生 閱讀(1946) | 評論 (0)編輯 收藏

            提高你開發效率的十五個 Visual Studio 使用技巧

            相信做開發的沒有不重視效率的。開發C#,VB的都知道,我們很依賴VS,或者說,我們很感謝VS。能夠對一個IDE產生依賴,說明這個IDE確實 有它的獨特之處。無容置疑,VS是一個非常強大的IDE,它支持多語言編輯。支持C#,VB,C/C++,HTML......它擁有強大的調試編譯功 能。它讓我們不用去記住那些安裝,環境變量設置,服務器設置,編譯的繁瑣過程。高度集成化。凡事有利有弊,在敏捷開發盛行的時代,VS是否值得我們使用是 無容置疑的。但是強大的VS也擁有眾多的設置,眾多的技巧。記住某些小技巧可以讓我們更加方便,快捷地使用VS。這是很有必要的。每個人或多或少記住了一 些小技巧。但是不可能全部都記住,我們按照我們自己的編程習慣記住一些自己比較常用的就好。

            下面是鄙人在編碼過程中發現而且比較經常使用的一些小技巧,希望對你有所幫助。

             

            1.行編輯(復制,剪切,刪除,交換)


             

            當你在光標停留行使用快捷鍵Ctrl+C,X,L時,可以復制,剪切,刪除整行內容。當然,右鍵也是可以的。跟平時的復制,剪切,刪除就是選中和沒選中代碼的區別而已。

            如果你想交換上下兩行,你可以使用快捷鍵(Shift+Alt+T),前提是光標要停留在上面那一行。替換之后,光標會一直跟隨原本的那一行。

             

            2.注釋(//TODO:...)


             

            看標題的話,你可能想打我。那個程序員不知道注釋啊,不就//或者/*.....*/亦或者(HTML/XML注釋)。但是使用過

            // TODO:注釋部分

            的,估計是少數吧。如果你喜歡用“任務列表”記錄一些要做的事情,這個小功能最適合你了。你可以再VS 2010的菜單上找到任務列表窗,點擊“菜單->視圖->任務列表”,你也可以點擊快捷鍵“Ctrl+W,T”。VS還提供了,HACK,UNTODU,UnresolvedMergeConflict標記注釋,你可以在“工具->選項->環境->任務列表”找到并且編輯/添加/刪除標記注釋。下面是圖示:

            提高你開發效率的十五個 Visual Studio 使用技巧

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            3.創建區域(#region和#endregion)


             

            當代碼越來越多的時候,你會期望可以隱藏一些代碼,而#region 和#endregion 就是這樣的功能。你可以在任何位置隱藏任何代碼。即使是隱藏的內容不屬于同一個函數。你可以點擊#region旁邊的+/-,展開/隱藏代碼。在隱藏的時 候,當你的光標放放置在備注上面的時候,VS會顯示出隱藏的代碼內容。

            提高你開發效率的十五個 Visual Studio 使用技巧


            4.選擇一個單詞/選擇一個字符串


             

            如你所知雙擊一個單詞的時候會選擇整個單詞。按住Ctrl鍵單擊單詞的任意位置同樣可以選中單詞。

            雙擊字符串第一個引號的左側可以選中整個字符串。按住Ctrl鍵單擊第一個引號的前面同樣可以選中整個字符串。

             

            5.將代碼放入工具箱


             

            工具箱是拿來放控件的地方。我們在使用控件的時候,只需要從控件當中拖動控件到代碼就可以了,這樣可以省去大量代碼的編輯工作。既然工具箱如此方便,那么是否可以將一段重用性很高的代碼放入工具箱呢。答案當然是可以的。

            你可以選中你的代碼,然后拖入工具箱的空白處,你的代碼就保存到工具箱了。就像你將控件拖 入代碼頁面一樣,也可以將代碼拖入工具箱中。以后你就可以像使用控件一樣使用重用的代碼。這是非常方便的。而且工具箱的內容不會因為你關閉VS而消失,在 你下次打開VS的時候工具箱同樣保存了你的代碼。如果你需要查看工具箱保存的代碼而又不想拖到代碼頁面中,你只需要將光標停留在工具箱的代碼圖標上面。如 圖所示:

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            6.格式化代碼


             

            這個很重要,即使VS在你每次打完“;”回車之后會自動格式化代碼。但是難免代碼的格式會有所變化,譬如粘貼一段代碼之后,代碼的格式往往會受到影響。所以,這個還是很有必要知道的。

            格式化部分代碼:選中代碼->Ctrl+K,F。或者Ctrl+E,F

            格式化整個文檔:編輯->高級->設置文檔的格式。或者 Ctrl+K,D。或者Ctrl+E,D

             

            7.切換設計/代碼圖示


             

            在ASP.NET頁面切換(HTML): Ctrl+PgUp/Ctrl+PgDn

            在windows窗體切換:F7/Shift+F7 (代碼/設計)

             

            8.查找錯誤代碼。


             

            當錯誤列表有錯誤或者警告提示時,你可以雙擊這個錯誤或提示,就可以跳轉到錯誤或警告的語句前。

             

            9.跳轉到指定行號


             

            如果代碼很多的時候,這是很有用的。在ASP.NET編程的時候,很多錯誤只有在運行網站的時候才能發現,而這個錯誤又沒被在錯誤列表提示的時候,你就可以使用這個小技巧跳到錯誤的代碼前面。

            雙擊右下角狀態欄的行號,會跳出一個行號跳轉窗體。或者快捷鍵Ctrl+G調出窗體。當然,還可以從菜單欄點擊“編輯->跳轉..”使用這個功能。

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            10.快速查找


             

            光標停留在需要查找的詞上面,使用快捷鍵Ctrl+F3可以跳轉到下一個相同的詞。按Shift+F3可以往上查找。

             

            11.查找“{/}”


             

            查找:你是否很煩惱有些對應的標記找的到頭找不到尾,找得到尾不知道那個是頭。當你把光標放在“{”的前面,VS會將相對應的”}“標記起來。你也可以將光標停留在“}”的后面,可以達到同樣的效果。

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            12.查找和替換


             

            當你想查找/替換掉某個字符串的時候,你可以按快捷鍵“Ctrl+F”或者“Ctrl+H”,進行這一操作。另外VS支持正則表達式和通配符。

            如果你想從整個項目進行查找/替換,你需要使用快捷鍵“Ctrl+Shift+F”或者“Ctrl+Shift+H”。當然這一切都可以在菜單欄找到。“編輯->查找和替換”。當你想中止全局替換的時候,你可以使用快捷鍵“Ctrl+Pause Break”。

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            13. 書簽


             

            書簽是很有用的功能,用過Chrome的都知道。在VS當中,書簽同樣適用。它可以幫你保存位置,以便你寫代碼。

            放置書簽:Ctrl+B,T

            上一個書簽:Ctrl+B,P

            下一個書簽:Ctrl+B,N

            刪除所有書簽:Ctrl+K,C

            除此之外,VS還提供了其它的書簽操作。

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            14.跳轉到定義


             

            當你查看代碼的時候,往往需要去查看原函數,這是難免的。但是千萬不要去手動尋找函數。這效率往往是很低的。你可以右鍵該函數,選擇跳轉到定義即可。當然你也可以使用快捷鍵F12

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            15.以文本形式插入外部文本


             

            菜單->編輯->將文件作為文本插入

            好處是,你不需要打開文件去復制粘貼。

            提高你開發效率的十五個 Visual Studio 使用技巧

             

            或許這些小技巧你早就知道了。亦或是,你覺得這些技巧根本沒啥用。當然,我們最主要的任務還是去編碼而已。沒有必要將心思花在這上面。但是,當你習慣使用這些小技巧的時候,這為你帶來的收益覺得不會讓你有所失望的。有些技巧,個人認為還是很有必要掌握的。

            總之,撿你想撿的吧,讓其他人折騰去吧。

            posted @ 2012-03-05 21:25 多彩人生 閱讀(390) | 評論 (0)編輯 收藏

            服務端 點滴

            @飯中淹
            多謝樓主解答,不過還是有一些疑問,我說一下自己對這個架構的理解。
            從用戶登錄開始,用戶登錄連接網關,發數據到loginserver校驗賬戶密碼,如果areaDB中沒有賬戶信息,向數據中心要賬戶密碼,插入areaDB,以后校驗賬戶就可以直接在區域DB中做了,如果賬戶密碼校驗成功,發送本區的組列表給客戶端,玩家選擇某個服務器,login獲取生成一個key發給userserver,同時把key發給客戶端以及一個網關的ip端口,客戶端使用其連接,發送key到Userserver,比對key,非法踢掉客戶端連接,合法userserver向數據中心獲取賬戶詳細信息,比如賬戶上剩余點卡等,同時向GroupDB獲取本組中的自己的角色,得到這些信息都發給客戶端,客戶端選擇一個角色進入游戲,userserver從數據庫讀取該角色的完全信息,根據玩家之前的位置確定進入那個gameserver(我認為游戲服務器是根據地圖劃分的,不知道對不對?),角色數據傳到gameserver,同時客戶端根據這些數據進入場景,gameserver得到信息同時告知publicserver(publicserver連DB做什么?我猜測可以獲取工會以及工會成員信息,對否?)

            http://www.shnenglu.com/johndragon/archive/2008/04/10/46768.html

            posted @ 2012-03-05 21:02 多彩人生 閱讀(174) | 評論 (0)編輯 收藏

            補習 explicit

              C++提供了關鍵字explicit,可以阻止不應該允許的經過轉換構造函數進行的隱式轉換的發生。聲明為explicit的構造函數不能在隱式轉換中使用。
              C++中, 一個參數的構造函數(或者除了第一個參數外其余參數都有默認值的多參構造函數), 承擔了兩個角色。 1 是個構造器 2 是個默認且隱含的類型轉換操作符。
              所以, 有時候在我們寫下如 AAA = XXX, 這樣的代碼, 且恰好XXX的類型正好是AAA單參數構造器的參數類型, 這時候編譯器就自動調用這個構造器, 創建一個AAA的對象。
              這樣看起來好象很酷, 很方便。 但在某些情況下(見下面權威的例子), 卻違背了我們(程序員)的本意。 這時候就要在這個構造器前面加上explicit修飾, 指定這個構造器只能被明確的調用,使用, 不能作為類型轉換操作符被隱含的使用。 呵呵, 看來還是光明正大些比較好。
              explicit構造函數的作用
              解析:
              explicit構造函數是用來防止隱式轉換的。請看下面的代碼:
              class Test1
              {
              public:
              Test1(int n) { num = n; } //普通構造函數
              private:
              int num;
              };
              class Test2
              {
              public:
              explicit Test2(int n) { num = n; } //explicit(顯式)構造函數
              private:
              int num;
              };
              int main()
              {
              Test1 t1 = 12; //隱式調用其構造函數, 成功
              Test2 t2 = 12; //編譯錯誤,不能隱式調用其構造函數
              Test2 t3(12); //顯示調用成功
              return 0;
              }
              Test1的構造函數帶一個int型的參數,代碼19行會隱式轉換成調用Test1的這個構造函數。而Test2的構造函數被聲明為explicit(顯式),這表示不能通過隱式轉換來調用這個構造函數,因此代碼20行會出現編譯錯誤。
              普通構造函數能夠被隱式調用。而explicit構造函數只能被顯示調用。

            posted @ 2012-03-04 16:26 多彩人生 閱讀(193) | 評論 (0)編輯 收藏

            protobuf

            接下來是定義message屬性,一個message是包含了各種類型字段的聚集。有很多標準的變量類型可以使用,包 括:bool,int32,float,double和string。你也可以使用其他的message作為字段類型。正像例子中的Person包含了 PhoneNumber,而AddressBook包含了Persion。甚至可以在message內部定義message,例 如:PhoneNumber就是在Persion里面定義的。你還可以定義enum類型,正像指定電話號碼類型的MOBILE、HOME、WORK。

            其中“=1”,“=2”表示每個元素的標識號,它會用在二進制編碼中對域的標識。標識號1-15由于使用時會比那些高的標識號少一個字節,從最優化 角度考慮,可以將其使用在一些較常用的或repeated元素上,對于16以上的則使用在不常用的或optional的元素上。對于repeated的每 個元素都需要重復編碼該標識號,所以repeated的域進行優化來說是最顯示的。

            每個字段必須提供一個修飾詞:

            Ø  required:表示字段必須提供,不能為空。否則message會被認為是未初始化的,試圖build未初始化的message會拋出 RuntimeException。解析未初始化的message會拋出IOException。除此之外,一個required字段與optional 字段完全相同。

            Ø  optional:可選字段,可以設置也可以不設置。如果沒有設置,會設置一個缺省值。可以指定一個缺省值,正像電話號碼的type字段。否則,使用系統 的缺省值:數字類型缺省為0;字符類型缺省為空串;邏輯類型缺省為false;對于嵌入的message,缺省值通常是message的實例或原型。

            Ø  repeated:字段可以被重復(包括0),可等同于動態數組或列表。其中存儲的值列表的順序是被保留的。

            Required修飾的字段是永久性的,在使用該修飾符時一定要特別小心。如果在以后想要修改required域為optional域時會出現問 題。對于訪問舊接口的用戶來說沒有該字段時,將會認為是不合法的訪問,將會被拒絕或丟棄。其中google的一些工程師給出的建議是如果不是必須,就盡量 少用required修飾符。

            posted @ 2012-03-04 15:30 多彩人生 閱讀(570) | 評論 (0)編輯 收藏

            字節對齊

            一、什么是對齊,以及為什么要對齊:
            1. 現代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定變量的時候經常在特定的內存地址訪問,這就需要各類型數據按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
            2. 對齊的作用和原因:各個硬件平臺對存儲空間的處理上有很大的不同。一些平臺對某些特定類型的數據只能從某些特定地址開始存取。其他平臺可能沒有這種情況, 但是最常見的是如果不按照適合其平臺的要求對數據存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為 32位)如果存放在偶地址開始的地方,那么一個讀周期就可以讀出,而如果存放在奇地址開始的地方,就可能會需要2個讀周期,并對兩次讀出的結果的高低 字節進行拼湊才能得到該int數據。顯然在讀取效率上下降很多。這也是空間和時間的博弈。
            二、對齊的實現
            通常,我們寫程序的時候,不需要考慮對齊問題。編譯器會替我們選擇適合目標平臺的對齊策略。當然,我們也可以通知給編譯器傳遞預編譯指令而改變對指定數據的對齊方法。
            但是,正因為我們一般不需要關心這個問題,所以因為編輯器對數據存放做了對齊,而我們不了解的話,常常會對一些問題感到迷惑。最常見的就是struct數據結構的sizeof結果,出乎意料。為此,我們需要對對齊算法所了解。
            對齊的算法:
            由于各個平臺和編譯器的不同,現以本人使用的gcc version 3.2.2編譯器(32位x86平臺)為例子,來討論編譯器對struct數據結構中的各成員如何進行對齊的。
            設結構體如下定義:
            struct A {
                int a;
                char b;
                short c;
            };
            結構體A中包含了4字節長度的int一個,1字節長度的char一個和2字節長度的short型數據一個。所以A用到的空間應該是7字節。但是因為編譯器要對數據成員在空間上進行對齊。
            所以使用sizeof(strcut A)值為8。
            現在把該結構體調整成員變量的順序。
            struct B {
                char b;
                int a;
                short c;
            };
            這時候同樣是總共7個字節的變量,但是sizeof(struct B)的值卻是12。
            下面我們使用預編譯指令#pragma pack (value)來告訴編譯器,使用我們指定的對齊值來取代缺省的。
            #pragma pack (2) /*指定按2字節對齊*/
            struct C {
                char b;
                int a;
                short c;
            };
            #pragma pack () /*取消指定對齊,恢復缺省對齊*/
            sizeof(struct C)值是8。

            修改對齊值為1:
            #pragma pack (1) /*指定按1字節對齊*/
            struct D {
                char b;
                int a;
                short c;
            };
            #pragma pack () /*取消指定對齊,恢復缺省對齊*/
            sizeof(struct D)值為7。

            對于char型數據,其自身對齊值為1,對于short型為2,對于int,float,double類型,其自身對齊值為4,單位字節。
            這里面有四個概念值:
            1)數據類型自身的對齊值:就是上面交代的基本數據類型的自身對齊值。
            2)指定對齊值:#pragma pack (value)時的指定對齊值value。
            3)結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值。
            4)數據成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中較小的那個值。
            有了這些值,我們就可以很方便的來討論具體數據結構的成員和其自身的對齊方式。有效對齊值N是最終用來決定數據存放地址方式的值,最重要。有效對齊N,就 是表示“對齊在N上”,也就是說該數據的"存放起始地址%N=0".而數據結構中的數據變量都是按定義的先后順序來排放的。第一個數據變量的起始地址就是 數據結構的起始地址。結構體的成員變量要對齊排放,結構體本身也要根據自身的有效對齊值圓整(就是結構體成員變量占用總長度需要是對結構體有效對齊值的整 數倍,結合下面例子理解)。這樣就不難理解上面的幾個例子的值了。
            例子分析:
            分析例子B;
            struct B {
                char b;
                int a;
                short c;
            };
            假設B從地址空間0x0000開始排放。該例子中沒有定義指定對齊值,在筆者環境下,該值默認為4。第一個成員變量b的自身對齊值是1,比指定或者默認指 定對齊值4小,所以其有效對齊值為1,所以其存放地址0x0000符合0x0000%1=0.第二個成員變量a,其自身對齊值為4,所以有效對齊值也為 4,所以只能存放在起始地址為0x0004到0x0007這四個連續的字節空間中,復核0x0004%4=0,且緊靠第一個變量。第三個變量c,自身對齊 值為2,所以有效對齊值也是2,可以存放在0x0008到0x0009這兩個字節空間中,符合0x0008%2=0。所以從0x0000到0x0009存 放的都是B內容。再看數據結構B的自身對齊值為其變量中最大對齊值(這里是b)所以就是4,所以結構體的有效對齊值也是4。根據結構體圓整的要求, 0x0009到0x0000=10字節,(10+2)%4=0。所以0x0000A到0x000B也為結構體B所占用。故B從0x0000到0x000B 共有12個字節,sizeof(struct B)=12;

            同理,分析上面例子C:
            #pragma pack (2) /*指定按2字節對齊*/
            struct C {
                char b;
                int a;
                short c;
            };
            #pragma pack () /*取消指定對齊,恢復缺省對齊*/
            第一個變量b的自身對齊值為1,指定對齊值為2,所以,其有效對齊值為1,假設C從0x0000開始,那么b存放在0x0000,符合0x0000%1= 0;第二個變量,自身對齊值為4,指定對齊值為2,所以有效對齊值為2,所以順序存放在0x0002、0x0003、0x0004、0x0005四個連續 字節中,符合0x0002%2=0。第三個變量c的自身對齊值為2,所以有效對齊值為2,順序存放
            在0x0006、0x0007中,符合0x0006%2=0。所以從0x0000到0x00007共八字節存放的是C的變量。又C的自身對齊值為4,所以 C的有效對齊值為2。又8%2=0,C只占用0x0000到0x0007的八個字節。所以sizeof(struct C)=8.

            有 了以上的解釋,相信你對C語言的字節對齊概念應該有了清楚的認識了吧。在網絡程序中,掌握這個概念可是很重要的喔,在不同平臺之間(比如在Windows 和Linux之間)傳遞2進制流(比如結構體),那么在這兩個平臺間必須要定義相同的對齊方式,不然莫名其妙的出了一些錯,可是很難排查的哦^_^。

            posted @ 2012-03-04 14:26 多彩人生 閱讀(311) | 評論 (0)編輯 收藏

            ProtocolBuffers2.4.1應用說明(一)

            ProtocolBuffers2.4.1應用說明(一)
            2012-02-03 12:07

             

            客方的ProtocolBuffers 詳細說明,可以下載最新版的ProtocolBuffers包。

            我所下載的包是:protobuf-2.4.1.tar.bz2 、 protoc-2.4.1-win32.zip

             

            ProtocolBuffers 首頁:http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/overview.html

            protobuf-2.4.1.tar.bz2  是源碼包

             protoc-2.4.1-win32.zip 是編譯 .proto 文件的編譯器

            本文使用 ProtocolBuffers 的環境

            操作系統: windows 7 64位

            開發工具:Visual studio 2008

            開發語言:C++、MFC類庫

            第一步: 編譯protobuf-2.4.1工程

            說明:編譯protobuf-2.4.1工程后生成 libprotobuf.lib, libprotobuf.lib 會在自已的工程文件中用到這個庫文件

            1)當前的目錄結構為:

            E:\ProtocolBuffers\ 此目錄結構下有兩個包 protobuf-2.4.1.tar.bz2 、protoc-2.4.1-win32.zip

            2)解壓 protobuf-2.4.1.tar.bz2 包

            會生 E:\ProtocolBuffers\protobuf-2.4.1\protobuf-2.4.1目錄結構

            調整后的目錄結構為:E:\ProtocolBuffers\protobuf-2.4.1目錄結構,便于應用。

            3)VS2008編譯工程

            找到 E:\ProtocolBuffers\protobuf-2.4.1\vsprojects\protobuf.sln文件。

            用VS2008 打開,然后編譯整個功程,很順利的編譯完整個功程。

            如圖所示:

                

            編譯完成后會在E:\ProtocolBuffers\protobuf-2.4.1\vsprojects\Debug 目錄結構中生成libprotobuf.lib庫文件。

            4) 如果出現問題:

            可以閱讀 vsprojects\readme.txt 說明文檔。  

             

            第二步:編寫 .proto 文件

             

            1)在目錄 E:\ProtocolBuffers\protobuf-2.4.1\examples 中有個示例

            可以先按官方的文檔來熟悉一下。

            2) 編寫 .proto 文件

            自已編寫的 shapeobject.proto 文件

             

            package candee;

            option java_package = "com.example.candee";

            option java_outer_classname = "ShapeObjectProto";

            message DrawInfoPB {


            message ColorVal {

            required int32r = 1;//int32  unsigned short

            required int32g = 2;

            required int32b = 3;

            }

             

            required int32toolbarState = 1;// TOOLBAR_STATE

            required ColorValpenColor = 2;// 筆的顏色

            required int32penLineWidth = 3;// 用戶設置畫筆的寬度

            required ColorValwordColor = 4;// 字的顏色

            required int32wordLineWidth = 5;// 用戶設置字的寬度

            required ColorValgraphColor = 6;// 圖形的顏色

            required int32graph = 7;// 圖形

            required int32graphLineWidth = 8;// 繪制圖形的線寬

            }

             

            message ShapeObjectPB {

             

            required DrawInfoPB drawInfoPB = 1;// 繪畫信息

             

            message DrawPointPB {

            required int32 x1 = 1;

            required int32 y1 = 2;

            required int32 X2 = 3;

            required int32 y2 = 4;

            }

            repeated DrawPointPB drawPointPB = 2;// 繪畫坐標

             

            optional string textPB = 3;// 編輯框文字信息

            }

             

            message DataPB {

            repeated ShapeObjectPB shapeObjectPB = 1;

            }


            第三步 編譯 shapeobject.proto 文件,生成C++源文件

             

            1)解壓 E:\ProtocolBuffers\protoc-2.4.1-win32.zip

               會生成 E:\ProtocolBuffers\protoc-2.4.1-win32\protoc.exe 編譯文件。

            2) 將 protoc.exe 考貝到 shapeobject.proto文件同一級目錄中。

            本目錄為 E:\ProtocolBuffers\protobuf-2.4.1\examples

            3)命令執行protoc 文件

            在\examples\ 新建一個目錄為 1\用來保存生成的C++源文件

            在命令行下,執行protoc --cpp_out=1 shapeobject.proto

            如圖所示:

             4)生成的C++源文件

            在E:\ProtocolBuffers\protobuf-2.4.1\examples\1 目錄中保存生成的文件

            生成的文件:shapeobject.pb.cc shapeobject.pb.h

            將這兩個文件添加到你的功程中,就可以用戶ProtocolBuffer了

            posted @ 2012-03-02 22:04 多彩人生 閱讀(1085) | 評論 (0)編輯 收藏

            僅列出標題
            共25頁: First 17 18 19 20 21 22 23 24 25 

            導航

            統計

            常用鏈接

            留言簿(3)

            隨筆分類

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲中文字幕无码久久2017| 国产福利电影一区二区三区久久老子无码午夜伦不 | 亚洲国产精品无码久久一线| 久久精品国产男包| 国产亚洲综合久久系列| 一本伊大人香蕉久久网手机| 日韩欧美亚洲国产精品字幕久久久 | 久久国产免费直播| 国产精品99久久久精品无码| 国产精品视频久久久| 无码8090精品久久一区| 日产精品99久久久久久| 久久久久久无码国产精品中文字幕 | 久久国产热这里只有精品| 亚洲午夜久久久影院| 久久久久国产精品嫩草影院| 久久久久亚洲av无码专区喷水| 久久精品国产一区二区三区| AAA级久久久精品无码片| 欧美一区二区久久精品| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 中文字幕乱码久久午夜| 国产精品免费久久久久久久久| 亚洲∧v久久久无码精品| 欧美精品一区二区久久| 91久久精品国产91性色也| 国产精品一区二区久久国产| 久久精品中文字幕大胸| 久久久久无码精品国产app| 久久99国产精品二区不卡| 中文字幕久久精品无码| 欧美亚洲国产精品久久高清| 久久久久无码精品国产app| 91精品国产91热久久久久福利| 精品国产VA久久久久久久冰 | 精品国产乱码久久久久久人妻| 久久影院久久香蕉国产线看观看| 99久久免费只有精品国产| 国产成人久久精品区一区二区| 精品乱码久久久久久久| 国产精品久久久亚洲|