c++ 宏 win linux_服务器端开发经验总结 Linux C语言
簡介
在進行服務(wù)器端開發(fā)的時候需要考慮一些算法和性能問題,經(jīng)過了幾年的開發(fā),對這方面有了一些經(jīng)驗,現(xiàn)在寫下來跟大家分享和討論。
我主要是在Linux下進行C語言的開發(fā),所以后面的實現(xiàn)都是基于Linux操作系統(tǒng)并用C語言來講解。其它平臺和語言需要考慮的問題是類似的只不過可能是實現(xiàn)細節(jié)上有一些差異,我盡量減少這些差異吧。注意一下講解的所有內(nèi)容都是基于32位系統(tǒng)的開發(fā)!
服務(wù)器程序開發(fā)核心是穩(wěn)定,在穩(wěn)定的前提下需要考慮效率。其中主要的公共模塊是內(nèi)存池和線程池。因為服務(wù)器程序一般都會長時間的運行,而且頻繁的進行創(chuàng)建和釋放內(nèi)存的操作,這時如果使用系統(tǒng)的malloc和free方法,則會使系統(tǒng)中產(chǎn)生很多內(nèi)存碎片,從而影響效率和穩(wěn)定性。內(nèi)存池的主要思想是先調(diào)用系統(tǒng)的malloc開辟一個很大的內(nèi)存,然后對這個大內(nèi)存進行管理,程序中要使用內(nèi)存池,內(nèi)存池分配內(nèi)存,程序要釋放內(nèi)存時,只是通知內(nèi)存池,而不真正釋放內(nèi)存。線程池,原理類似,并發(fā)處理一些任務(wù)的時候,需要使用很多線程,我們可以先創(chuàng)建很多線程,然后每次有任務(wù)需要處理,則找到一個空閑的線程讓它來處理任務(wù),處理完成后線程掛起。這樣省去了每次一個任務(wù)都創(chuàng)建線程的時間開銷。
預(yù)備知識
下面介紹一下在進行庫的封裝時用到的一些技巧。因為我們是使用C語言進行開發(fā),所以為了使結(jié)構(gòu)和效率得到很好的平衡,需要一些技巧來進行封裝,從而使程序在保證C語言效率的前提下足夠的模塊化。
container_of 宏
首先介紹一下這個宏,這個宏是我在linux內(nèi)核中看到的,它用于通過一個結(jié)構(gòu)中的某個成員的指針,推算出整個結(jié)構(gòu)的指針,先舉一個例子吧。例如有如下幾個結(jié)構(gòu):
1: struct my_struct 2: { 3: int a; 4: int b; 5: char c; 6: short d; 7: };看看下面的代碼:
1: int main () 2: { 3: struct my_struct mys; // 創(chuàng)建結(jié)構(gòu)對象 4: struct my_struct* pmys; // 聲明一個指向我們的結(jié)構(gòu)的指針 5: char* pc = &(mys.c); // pc指向結(jié)構(gòu)中c成員 6: int* pb = &(mys.b); // pb指向結(jié)構(gòu)中b成員 7: pmys = container_of (pc, struct my_struct, c); // pmys實際上指向mys結(jié)構(gòu) 8: pmys = container_of (pb, struct my_struct, b); // pmys還是指向mys結(jié)構(gòu) 9: // 上面兩行代碼與 pmys = &mys; 的功能一樣 10: ... 11: }Linuxc/c++服務(wù)器開發(fā)高階視頻學(xué)習(xí)資料+主頁qun獲取
上面的代碼說明,通過調(diào)用container_of可以通過某個結(jié)構(gòu)中某個成員的指針(如pc或者pb),獲得整個結(jié)構(gòu)的指針。
這個宏需要三個參數(shù),第一個參數(shù)是結(jié)構(gòu)中某個成員的指針,第二個參數(shù)是結(jié)構(gòu)的類型,第三個參數(shù)是,第一個參數(shù)中的成員指針指向的成員在結(jié)構(gòu)的聲明中的成名名稱。這個宏定義如下:
在上面的實現(xiàn)中,首先定義了一個offsetof宏,這個宏用于計算結(jié)構(gòu)中的某個成員的地址相對于這個結(jié)構(gòu)的起始地址的偏移。WIN32下可以使用系統(tǒng)自帶offset宏。
container_of宏實際上是用結(jié)構(gòu)中某個成員的地址減去這個成員相對于結(jié)構(gòu)起始的偏移,就得到了結(jié)構(gòu)的地址也就是指向這個結(jié)構(gòu)的指針。linux下實現(xiàn)的時候:const typeof( ((type *)0)->member)* _mptr = (ptr);這句實際上是在做類型安全檢查。保證給宏的第一個參數(shù)成員指針的類型就是指向這個成員類型的指針。typeof是gcc擴展,用于獲得某個表達式的類型。在WIN32下我沒有找到typeof的代替品,所以沒有做類型安全檢查。
有了container_of宏,我們可以做些什么工作呢?舉個例子,如果我們寫一個鏈表,教科書上一般將鏈表中的數(shù)據(jù)區(qū)域當(dāng)作一個int。實際上可能是一個復(fù)雜的結(jié)構(gòu),而我作為編寫鏈表的人,不知道使用者會在鏈表中存儲什么數(shù)據(jù)。通常的做法是,數(shù)據(jù)區(qū)域就是一個void*,這樣使用者可以用這個指針存儲任何對象了。如果使用者要存儲自己的數(shù)據(jù),可以創(chuàng)建自定義結(jié)構(gòu)的對象,然后用這個指針指向這個結(jié)構(gòu)。鏈表中的每一個元素除了數(shù)據(jù)區(qū)占用的內(nèi)存外還占用了一個void*.在服務(wù)器端開發(fā)時,數(shù)據(jù)量是很大的,這樣就浪費了很多的void*。我們可以通過如下的方法來解決這個問題。
定義鏈表中的元素結(jié)構(gòu):
1: struct link_item 2: { 3: struct link_item* next; 4: };而提供的操作鏈表的方法都是基于struct link_item* 指針的方法。用戶在使用的時候聲明自己的鏈表元素結(jié)構(gòu):
1: struct user_link_item 2: { 3: struct link_item lk_item; 4: int my_data1; 5: short my_data2; 6: // ... 7: };這個結(jié)構(gòu)中擁有struct link_item類型的一個成員lk_item。我們在使用鏈表的時候,總是將lk_item的地址傳遞給鏈表的方法。獲取數(shù)據(jù)的時候獲得的也是指向struct link_item的指針,但是我們可以使用container_of宏用鏈表方法返回的struct link_item的指針推算出struct user_link_item的指針,這樣作為用戶就可以獲得存儲在鏈表中的數(shù)據(jù)了。這樣的實現(xiàn)中具體元素的內(nèi)存也是外部使用者來分配的。
這種方法有些類似c++的繼承機制。遇到繼承的問題可以考慮使用這種方法。
引用計數(shù)器
在開發(fā)服務(wù)器程序的時候,我們經(jīng)常是多線程的并行處理。對于要處理的數(shù)據(jù)就有一個線程安全的問題。最簡單的線程安全處理是原子操作。每一次處理都是使用一條指令完成。其次是使用線程鎖,進行線程同步。對于一個存儲在某個數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)來說,可能會在某個線程進行讀取操作,而在另外的一個線程這個數(shù)據(jù)被刪除了。我們可以模仿WIN32的COM處理這個問題時使用的方法。增加一個引用計數(shù)器,每次要對數(shù)據(jù)進行訪問的時候都要遞增這個數(shù)據(jù)的引用計數(shù)器,當(dāng)使用完成的時候,遞減這個數(shù)據(jù)的引用計數(shù)器。當(dāng)引用計數(shù)器的值被遞減為0的時候,這個數(shù)據(jù)就會真正的被刪除。當(dāng)然,引用計數(shù)器的遞增和遞減操作需要是線程安全的,完全可以使用原子操作來實現(xiàn)。
所有需要保證線程安全的數(shù)據(jù)對象,我們都可以通過上面講到的使用container_of宏的實現(xiàn)的繼承方式來實現(xiàn)。
通訊協(xié)議中變長字段的結(jié)構(gòu)化處理
服務(wù)器端通訊通常會設(shè)計一套協(xié)議。在協(xié)議中通常是如下形式:
|4字節(jié)表示后面的字節(jié)數(shù)|1字節(jié)表示某個標(biāo)志|1字節(jié)表示某個標(biāo)志|2字節(jié)表示某個標(biāo)志|n個字節(jié)表示內(nèi)容|上面的這種協(xié)議,我們?nèi)绾蝸韺⑺D(zhuǎn)換為一個結(jié)構(gòu)呢?看看如下定義的結(jié)構(gòu):
1: // 錯誤的結(jié)構(gòu) 2: struct wrong_struct 3: { 4: long size; 5: char f1; 6: char f2; 7: short f3; 8: char* content; 9: };上面的結(jié)構(gòu)明顯是錯誤的,主要是content成員。content是一個指向字符串的指針?biāo)采w了協(xié)議中“n個字節(jié)表示內(nèi)容”中的最前面的4個字節(jié)。如何解決這個問題呢?很簡單:
1: struct right_struct 2: { 3: long size; 4: char f1; 5: char f2; 6: short f3; 7: char content[1]; 8: }; 8: };我們將content聲明為只有一個元素的字符串?dāng)?shù)組。字符串?dāng)?shù)組又是字符串指針,我們可以使用content成員來引用“n個字節(jié)表示內(nèi)容”了。
總結(jié)
以上是生活随笔為你收集整理的c++ 宏 win linux_服务器端开发经验总结 Linux C语言的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python的二维数组操作
- 下一篇: 城市轨道交通运营票务管理论文_城市轨道交