標籤:
原文地址:http://blog.csdn.net/linlinlinxi007/article/details/5086806
在通訊程式中,經常使用環形緩衝區作為資料結構來存放通訊中發送和接收的資料。環形緩衝區是一個先進先出的迴圈緩衝區,可以向通訊程式提供對緩衝區的互斥訪問。
1、環形緩衝區的實現原理
環形緩衝區通常有一個讀指標和一個寫指標。讀指標指向環形緩衝區中可讀的資料,寫指標指向環形緩衝區中可寫的緩衝區。通過移動讀指標和寫指標就可以實現緩衝區的資料讀取和寫人。在通常情況下,環形緩衝區的讀使用者僅僅會影響讀指標,而寫使用者僅僅會影響寫指標。如果僅僅有一個讀使用者和一個寫使用者,那麼不需要添加互斥保護機制就可以保證資料的正確性。如果有多個讀寫使用者訪問環形緩衝區,那麼必須添加互斥保護機制來確保多個使用者互斥訪問環形緩衝區。
圖1、圖2和圖3是一個環形緩衝區的運行。圖1是環形緩衝區的初始狀態,可以看到讀指標和寫指標都指向第一個緩衝區處;圖2是向環形緩衝區中添加了一個資料後的情況,可以看到寫指標已經移動到資料區塊2的位置,而讀指標沒有移動;圖3是環形緩衝區進行了讀取和添加後的狀態,可以看到環形緩衝區中已經添加了兩個資料,已經讀取了一個資料。
伺服器公用組件實現----環形緩衝區
訊息佇列鎖調用太頻繁的問題算是解決了,另一個讓人有些苦惱的大概是這太多的記憶體配置和釋放操作了。頻繁的記憶體配置不但增加了系統開銷,更使得記憶體片段不斷增多,非常不利於我們的伺服器長期穩定運行。也許我們可以使用記憶體池,比如SGI STL中附帶的小記憶體 Clerk。但是對於這種按照嚴格的先進先出順序處理的,塊大小並不算小的,而且塊大小也並不統一的記憶體配置情況來說,更多使用的是一種叫做環形緩衝區的方案,mangos的網路代碼中也有這麼一個東西,其原理也是比較簡單的。
就好比兩個人圍著一張圓形的桌子在追逐,跑的人被網路IO線程所控制,當寫入資料時,這個人就往前跑;追的人就是邏輯線程,會一直往前追直到追上跑的人。如果追上了怎麼辦?那就是沒有資料可讀了,先等會兒唄,等跑的人向前跑幾步了再追,總不能讓遊戲沒得玩了吧。那要是追的人跑的太慢,跑的人轉了一圈過來反追上追的人了呢?那您也先歇會兒吧。要是一直這麼反著追,估計您就只能換一個跑的更快的追逐者了,要不這遊戲還真沒法玩下去。
前面我們特彆強調了,按照嚴格的先進先出順序進行處理,這是環形緩衝區的使用必須遵守的一項要求。也就是,大家都得遵守規定,追的人不能從桌子上跨過去,跑的人當然也不允許反過來跑。至於為什麼,不需要多做解釋了吧。
環形緩衝區是一項很好的技術,不用頻繁的分配記憶體,而且在大多數情況下,記憶體的反覆使用也使得我們能用更少的記憶體塊做更多的事。
在網路IO線程中,我們會為每一個串連都準備一個環形緩衝區,用於臨時存放接收到的資料,以應付半包及粘包(即發送方發送的若干包資料到接收方接收時粘成一包)的情況。在解包及解密完成後,我們會將這個資料包複製到邏輯線程訊息佇列中,如果我們只使用一個隊列,那這裡也將會是個環形緩衝區,IO線程往裡寫,邏輯線程在後面讀,互相追逐。可要是我們使用了前面介紹的最佳化方案後,可能這裡便不再需要環形緩衝區了,至少我們並不再需要他們是環形的了。因為我們對同一個隊列不再會出現同時讀和寫的情況,每個隊列在寫滿後交給邏輯線程去讀,邏輯線程讀完後清空隊列再交給IO線程去寫,一段固定大小的緩衝區即可。沒關係,這麼好的技術,在別的地方一定也會用到的
2、執行個體:環形緩衝區的實現
環形緩衝區是資料通訊程式中使用最為廣泛的資料結構之一,下面的代碼,實現了一個環形緩衝區:
1 /*ringbuf .c*/ 2 #include<stdio. h> 3 #include<ctype. h> 4 5 #define NMAX 8 6 7 int iput = 0; /* 環形緩衝區的當前放入位置 */ 8 int iget = 0; /* 緩衝區的當前取出位置 */ 9 int n = 0; /* 環形緩衝區中的元素總數量 */10 double buffer[NMAX];11 12 /* 環形緩衝區的地址編號計算函數,如果到達喚醒緩衝區的尾部,將繞回到頭部。環形緩衝區的有效地址編號為:0到(NMAX-1)*/13 int addring (int i)14 {15 return (i+1) == NMAX ? 0 : i+1;16 }17 18 /* 從環形緩衝區中取一個元素 */19 double get(void)20 {21 int pos;22 if (n>0)23 {24 Pos = iget;25 iget = addring(iget);26 n--;27 return buffer[pos];28 }29 else 30 {31 printf(“Buffer is empty/n”);32 return 0.0;33 } 34 }35 36 /* 向環形緩衝區中放入一個元素*/37 void put(double z)38 {39 if (n<NMAX)40 {41 buffer[iput]=z;42 iput = addring(iput);43 n++;44 }45 else46 printf(“Buffer is full/n”);47 }48 49 50 51 int main{void)52 {53 chat opera[5];54 double z;55 do 56 {57 printf(“Please input p|g|e?”);58 scanf(“%s”, &opera);59 switch(tolower(opera[0]))60 {61 case ‘p’: /* put */62 printf(“Please input a float number?”);63 scanf(“%lf”, &z);64 put(z);65 break;66 case ‘g’: /* get */67 z = get();68 printf(“%8.2f from Buffer/n”, z);69 break;70 case ‘e’:71 printf(“End/n”);72 break;73 default:74 printf(“%s - Operation command error! /n”, opera);75 }/* end switch */76 }while(opera[0] != ’e’);77 return 0;78 }
【轉】環形緩衝區