1. 平臺差異簡介
Windows 和Unix是當(dāng)前兩大主流操作系統(tǒng)平臺,基于C/C++的開發(fā)人員經(jīng)常會面臨這兩個平臺之間的移植的問題。Unix作為一個開發(fā)式的系統(tǒng),其下有出現(xiàn)了很 多個分支,包括Sun的Solaris、IBM的AIX、HP Unix、SCO Unix、Free BSD、蘋果的MAC OS以及開源的Linux等。對于這些Unix的分支操作系統(tǒng),其實現(xiàn)又有很大的差別,因此開發(fā)人員又要針對這些不同的系統(tǒng)進(jìn)行移植。本文的目的就是介紹 一下Windows平臺和Unix平臺之間的差別,并簡單介紹一下不同Unix分支操作系統(tǒng)之間的差別,在移植開發(fā)過程中的一些注意事項,同時簡要介紹一 下Unix下開發(fā)的一般流程和常用的開發(fā)調(diào)試工具。
關(guān)于平臺之間的差異,主要是Windows平臺和Unix平臺之間的差異,這里著重介紹一下這兩個平臺在C/C++開發(fā)中存在的差異,其間會穿插介紹一些Unix不同分支之間的差異。
1.1語言特性的差異
語言特性的差異,指的是不同操作系統(tǒng)平臺中,實現(xiàn)C++/C時的一些細(xì)微的差異,忽略這些差異可能會帶來一些特別隱蔽的錯誤。而且可能是致命的錯誤。所 以,了解語言特性的差異,對于在Unix移植來說非常重要。如果考慮系統(tǒng)多多個平臺支持,就必須了解在不同平臺下語言特性的差異,從開發(fā)一開始就把這些因 素考慮進(jìn)去,這樣才能最低限度的降低移植的過程中工作量。
1.1.1字節(jié)順序的差異
字節(jié)順序指的主要是整型變量在內(nèi)存中的存儲方式。在計算機(jī)中,數(shù)據(jù)都是以二進(jìn)制方式存儲的,包括在內(nèi)存和硬盤中。而計算機(jī)又以8位二進(jìn)制作為一個存儲單 元。在32位系統(tǒng)中,一個整型的存儲需要四個存儲單元。也就是說要把一個32位的整數(shù)分割成位四段分別進(jìn)行存儲,而每一段的存儲位置就是字節(jié)順序的差異。 為了清楚的表示每段存儲的先后位置,我們用16進(jìn)制來表示一段的值,下表列出了在Unix系統(tǒng)和Windows系統(tǒng)中整數(shù)20000在內(nèi)存中的情況。
十六進(jìn)制表示
0x00004E20
Windows內(nèi)存表示
20 4E 00 00
Unix內(nèi)存表示
00 00 4E 20
如表中所示,Windows中存儲方式和該整數(shù)的16進(jìn)制表示是相反,是一種低位在前高位在后的存儲順序。而Unix下的存儲順序和正常的16進(jìn)制表示的順序相同,稱為高位在前低位在后的順序。這種差異帶來的問題,主要體現(xiàn)在以下幾個方面:
? 網(wǎng)絡(luò)通信時
當(dāng)Windows 和Unix之間發(fā)生網(wǎng)絡(luò)數(shù)據(jù)傳輸,傳輸一個整型數(shù)據(jù)(如一個數(shù)據(jù)包的長度)的時候,如果不經(jīng)處理直接把內(nèi)存中的數(shù)據(jù)傳輸過去,那么在對方看來完全是另一個 數(shù)據(jù),這樣就會造成問題。如Windows下面發(fā)送過去一個20000(0x00004E20),在Unix下面收到的數(shù)據(jù)就會被理解成 541982720(0x204E0000),這簡直是天壤之別。
? 文件存儲和讀取時
跟網(wǎng)絡(luò)傳輸類似,如果在Windows下面把某個整數(shù)寫到了文件中,然后在Unix下面打開這個文件讀取該數(shù)據(jù),就會出現(xiàn)跟上面類似的問題。
這個問題主要體現(xiàn)在不同平臺之間互操作時,在多平臺開發(fā)過程中,尤其時在網(wǎng)絡(luò)應(yīng)用開發(fā)的時候,兩個平臺之間數(shù)據(jù)交互是非常普遍的,所以這個問題也就顯的很 普遍。解決這個問題的方法就是交互的雙方采用一種相同的數(shù)據(jù)編碼標(biāo)準(zhǔn),就是數(shù)據(jù)在傳輸和存儲的時候采用什么方法進(jìn)行編碼,具體的做法有一下幾種:
1. 數(shù)字轉(zhuǎn)換成字符傳進(jìn)行交互
2. 協(xié)商一個同意的字節(jié)順序,根據(jù)自己平臺的字節(jié)順序還原數(shù)據(jù)
3. 采用其他標(biāo)準(zhǔn)的編碼方式,如ASN1編碼
跟 這個問題類似,32位系統(tǒng)和64位系統(tǒng)的差異也會出現(xiàn)這樣的問題,解決方法跟這個問題的解決方法相同。在32位系統(tǒng)和64位系統(tǒng)中,長整型(long)分 別用32位和64位表示,這樣,在不同系統(tǒng)之間交互的時候必然會出現(xiàn)整型數(shù)據(jù)表示方式不同的問題。目前大多數(shù)Windows系統(tǒng)都是32位的系統(tǒng),而 Unix中很多都是64位的,尤其是大型的服務(wù)器,所以這個問題必須引起重視。
1.1.2變量的作用域差異
在不同的系統(tǒng)下,由于編譯器的不同,對變量作用域的實現(xiàn)機(jī)制也有所不同,這里以Windows下的VC和Solaris下的CC這兩個編譯器為例做一個簡單的比較說明。
在C++的開發(fā)過程中,我們經(jīng)常會有這樣的用法:
for(int i=0;i<num;i++)
{
…
}
這 是一種最常用的for循環(huán)的用法,因為其中i主要使用來控制循環(huán),所以一般沒有必要拿出來單獨進(jìn)行聲明,只是放在for語句中一起聲明。這里i、j等簡單 的變量就成了我們常用的變量,一般不按照編程規(guī)范那樣為他們命名。就是這種聲明方法,在Windows下和Solaris下有了不同的理解,i的作用域不 同。我們先把作用域進(jìn)行劃分,如下:
{
…
for(int i=0;i<num;i++)
II
{
I
…
}
…
…
}
我 們劃分出I和II兩個作用域,其中作用域II包含在作用域I當(dāng)中。在Windows下,變量i的作用域是I的整個范圍,而Solaris下的i的作用域只 是II的范圍。其實標(biāo)準(zhǔn)的C++語法應(yīng)該是Solaris的做法,但是微軟在實現(xiàn)的時候沒有按照這個標(biāo)準(zhǔn)實現(xiàn),這就引發(fā)了我們討論的這個問題。由于這個差 異,就引發(fā)了一些微妙而隱蔽的問題。先看一下下面兩端代碼。
A:
for(int i=0;i<num;i++)
{
…
}
…
for(i=0;i<num;i++)
{
…
}
B:
for(int i=0;i<num;i++)
{
…
}
…
for(int i=0;i<num;i++)
{
…
}
代碼A在Windows下面可以正常編譯,而在Solaris下面確編不過去,提示第二個for循環(huán)中變量i沒有定義。相反代碼B在Solaris下可以正常編譯,而在Windows下面編不過去,提示第二個for循環(huán)中變量i重復(fù)定義。
在 通常的情況下,我們會按照B的方法書寫代碼,而在Windows編譯是出現(xiàn)錯誤,然后改成A的那種形式。這樣,在Windows下就沒有問題了,程序也可 以編譯過去了,但是到Solaris下時,有會出現(xiàn)問題,這是就不得不把i的聲明拿到所有for循環(huán)的外面。當(dāng)i的聲明拿到for循環(huán)的外面時,真正的問 題來了。首先提示一下,這樣的一段代碼是沒有問題的:
C:
int i = 0;
if(cond)
{
…
for(int i=0;i<num;i++)
{
…
}
…
}
這是一段正確的代碼,雖然在外面已經(jīng)定義了i,但是在for里面重新定義一個i也沒有問題,這是C++的語法所允許的(java里面不允許這樣做)。但就是因為這種C++語言的靈活機(jī)制,引發(fā)了問題的產(chǎn)生。
問題產(chǎn)生源于程序中出現(xiàn)了A_B那樣的代碼,然后把i的聲明拿到了外面。在后期維護(hù)的過程中,又在后面增加了一個循環(huán),但是卻是按照C的那種方式增加的,這樣就產(chǎn)生了問題。請看如下代碼:
int i=0;
char str1[10];
char str2[10];
strcpy(str1,”hello”);
…
for(i=0;i<20;i++)
{
…
I
}
…
if(cond)
{
for(int i=0;i<10;i++)
III
{
II
if(str1[i]==0) break;
}
memcpy(str2,str1,i);
str2[i]=0;
}
…
在 上述代碼,為了分析方便,我們把整段代碼分成I、II和III三個作用域。其中作用域II就是整個if語句,實現(xiàn)的相當(dāng)于一個strcpy函數(shù)的功能。 II中的內(nèi)容就好是我們上面說的后期維護(hù)中加入的,當(dāng)然,實際情況并不像我們例子中這么明前,i的聲明可能離我們的if語句很遠(yuǎn),所以加入這段代碼是不知 道上面是否聲明了i變量。而且,這段代碼編譯的時候也不回出錯,不管是Windows還是Solaris(單獨的一段II中的代碼在Solaris下面編 不過去)。在Windows下面,這段代碼可以正常的運行,不回出現(xiàn)任何問題,因為II中的代碼完全是根據(jù)Windows下的習(xí)慣編寫的。但是在 Solaris下面,這段代碼就會出現(xiàn)內(nèi)存越界的錯誤,雖然編譯可以正常通過,但是實現(xiàn)的卻不是程序員預(yù)期的目的。在執(zhí)行memcpy的時候,那個i其實 是外層聲明的那個i,值是20,而str2和str1的大小之后10,所以就發(fā)生了讀寫內(nèi)存越界。而程序員預(yù)想的,這個i是for循環(huán)算出來的str1字 符串的長度,應(yīng)該是5。
要解決這類問題,就得加強(qiáng)編程規(guī)范,杜絕這種錯誤代碼的生成。從開始的時候就要意識到可能產(chǎn)生的問題,從而避免問題的發(fā)生。
1.1.3全局對象的初始化
在C++中,初始化對象的時候系統(tǒng)會自動調(diào)用構(gòu)造函數(shù),因此我們習(xí)慣在構(gòu)造函數(shù)中做一些初始化的工作,讓自動自動為我們調(diào)用初始化的操作。其中,有些對象是靜態(tài)分配的全局對象,就是在任何函數(shù)體外聲明的對象,如:
CMyObject g_Object;
通常情況下,程序啟動的時候,系統(tǒng)都會自動調(diào)用這個對象的構(gòu)造函數(shù)對這個全局對象進(jìn)行初始化,但是在某些系統(tǒng)中(SCO Unix),就不想我們期望的那樣,也許這是編譯器實現(xiàn)的一個bug,但是我們也不能忽視這個問題的存在。對于這種問題,我們可以通過顯式創(chuàng)建對象的方法 解決,如下:
CMyObject* g_pObject = new CMyObject;
這樣,系統(tǒng)在啟動的時候,就會執(zhí)行new CMyObject來為對象分配空間,同時執(zhí)行調(diào)用對象的構(gòu)造函數(shù)來初始化對象。如果不想使用指針的方式引用該對象(為了安全因素,不想某個函數(shù)在程序運行期間把這個指針置空),那么我們可以采用另一種方法,如下:
CMyObject& g_Object = *(new CMyObject);
這樣也可以達(dá)到對像創(chuàng)建和初始化的工作。雖然對于我們分配的這個對象沒有進(jìn)行釋放操作,但是全局只有這么一次,所以不用擔(dān)心內(nèi)存泄漏問題。程序運行結(jié)束的時候,操作系統(tǒng)會自動釋放掉程序所申請的所有內(nèi)存,當(dāng)然也包含這個對象。
1.1.4 語法檢查的差異
不同操作系統(tǒng)下面有不同的編譯器的實現(xiàn),不同的編譯器對語法要求的程度不同。在Windows下可以正常編譯的代碼,在Unix下就可能出現(xiàn)語法錯誤。1.1.2中就是一個典型的例子。另外,還有一些其他方面的語法檢查的差異。
C 是一中很靈活的語言,語法很自由,但是不同的平臺下這中自由的程度也不同。Windows VC、Solaris CC和Linux gcc實現(xiàn)的都不錯,但是有些其他的系統(tǒng)實現(xiàn)的就不是這么靈活,很多寫法在他們下面都行不通。具體的記不太清了,在AIX和SCO Unix下面碰到很多這種情況。所以只能在移植的過程中逐漸的發(fā)現(xiàn)和改正。但是只要保證采用標(biāo)準(zhǔn)的書寫規(guī)范,應(yīng)該可以更少的產(chǎn)生這種錯誤。
有這樣一段代碼:
if(NULL == pVar)
{
…
}
這是在大多數(shù)平臺下面很好的一種習(xí)慣,可以避免哪種把“==”寫成“=”的錯誤,在編譯期間就能發(fā)現(xiàn)。但是在SCO Unix下面,這種寫法就會引發(fā)編譯器的一個警告。這個例子能簡單的說明一下不同編譯器之間存在的差別。
1.2 操作系統(tǒng)特性的差異
不 同的操作系統(tǒng)中都存在一些系統(tǒng)的限制,如打開文件句柄數(shù)的限制、Socket等待隊列的限制、進(jìn)程和線程堆棧大小的限制等,因此在開發(fā)的過程中,必須考慮 到這些限制因素對程序的影響。當(dāng)然,有些限制參數(shù)可以適當(dāng)?shù)恼{(diào)整,這就需要在發(fā)布程序的時候加以聲明。另外,操作系統(tǒng)的容錯性也對程序有影響。下面分別進(jìn) 行討論。
1.2.1 文件描述符的限制
文 件描述符最初是Unix下的一個概念,在Unix系統(tǒng)中,用文件描述符來表示文件、打開的socket連接等,跟Windows下HANDLE的概念類 似。文件描述符是一種系統(tǒng)資源,系統(tǒng)對每個進(jìn)程可以分配的文件描述符數(shù)量都有限制。以Solaris為例,默認(rèn)情況下每個進(jìn)程可以打開的文件描述符為 1024個,系統(tǒng)的硬限制是8192(具體的值跟版本有關(guān)),也就是說可以調(diào)整到8192。在Unix系統(tǒng)下使用ulimit命令來獲得系統(tǒng)的這些限制參 數(shù)。一般情況下,這都是夠用的,但是有一個例外,在32為的Solaris程序中,使用標(biāo)準(zhǔn)輸入輸出函數(shù)(stdio)進(jìn)行文件的操作,最大的文件描述符 不能超過256。比如說用fopen打開文件,出去系統(tǒng)占用的3個文件描述符(stdin、stdout和stderr),程序中只能再同時打開253個 文件。如果使用open函數(shù)來打開文件,就沒有這個限制,但是就不能夠使用stdio中的那些函數(shù)進(jìn)行操作了,是程序的通用性和靈活性有所降低。這是因為 在stdio的FILE結(jié)構(gòu)中,用一個unsigned char來表示文件描述符,所以只能表示0~255。
在網(wǎng)絡(luò)程序的開發(fā)中,每一個網(wǎng)絡(luò)連接也都占用一個文件描述符,如果程序打開了很多Socket連接(典型的例子就是使用了連接池技術(shù)),那么程序運行的時候可能用fopen打不開文件。
解決這個問題,可以采用一下幾種方法:
1. 升級為64位系統(tǒng)或采用采用64位方式編譯程序
2. 使用sys/io.h中的函數(shù)操作文件
3. 采用文件池技術(shù),預(yù)留一部分文件描述符(3~255之間的),使用freopen函數(shù)來重用這些描述符。
至于采用哪種方法或者是否考慮系統(tǒng)中處理這個問題,就要視具體的情況而定了,那些不受這個限制影響的程序,可以不考慮這個問題。
1.2.2 進(jìn)程和線程的限制
一般的操作系統(tǒng)對每個進(jìn)程和線程可以使用的資源數(shù)都有限制,比如一個進(jìn)程可以創(chuàng)建的線程數(shù),一個進(jìn)程可以打開的文件描述符的數(shù)量,進(jìn)程和線程棧大小的限制和默認(rèn)值等。
針對這些問題,首先要分析和考慮你的系統(tǒng)是一個什么樣的規(guī)模,會不會收到這些限制的影響,如果需求大于系統(tǒng)的限制,可以通過適當(dāng)?shù)恼{(diào)整系統(tǒng)參數(shù)來解決,如果還不能解決,就得考慮采用多進(jìn)程的方式來解決。
對 于進(jìn)程和線程的??臻g大小的限制,主要是線程??臻g的問題。一般的系統(tǒng)都有默認(rèn)的線程??臻g大小,而且不同超作系統(tǒng)的默認(rèn)值可能不同。在通常情況下,這些 對程序沒有影響,但是當(dāng)程序的層次結(jié)構(gòu)比較復(fù)雜,使用了過多的本地變量,這個限制可能就會對程序產(chǎn)生影響,導(dǎo)致??臻g溢出,這是一個比較嚴(yán)重的問題。不能 通過調(diào)整系統(tǒng)參數(shù)來解決這個問題,但是可以通過相應(yīng)的函數(shù),在程序里面指定創(chuàng)建線程的棧空間的大小。但是具體該調(diào)整的數(shù)值應(yīng)該適可而止,而不是越大越好。 因為線程的??臻g過大的時候,就會影響到可創(chuàng)建線程的數(shù)量,雖然遠(yuǎn)沒有達(dá)到系統(tǒng)多線程數(shù)的限制,但卻可能因為系統(tǒng)資源占用過多導(dǎo)致分配內(nèi)存失敗。
Linux的線程是通過進(jìn)程實現(xiàn)的,實際上是假的線程。如果程序只在Linux下運行,就可以考慮直接使用多進(jìn)程技術(shù)來代替多線程,因為在Linux下多線程并不能帶來多線程相對于多進(jìn)程的優(yōu)勢。
1.2.3 網(wǎng)絡(luò)通信能力的限制
對于網(wǎng)絡(luò)編程來說,性能是最主要的因素。系統(tǒng)為了提高網(wǎng)絡(luò)通信的性能,提供了很多輔助的技術(shù),其中等待隊列就是其中之一。
對 于程序來說,在一個時間點只能處理一個網(wǎng)絡(luò)連接請求,而如果同時來了多個網(wǎng)絡(luò)連接請求的話,就會有很多請求失敗。為了解決這個問題,操作系統(tǒng)提供了等待隊 列技術(shù),就是處理不上的連接請求先放到系統(tǒng)的等待隊列中,這樣就可以提高網(wǎng)絡(luò)連接的成功率。等待隊列的創(chuàng)建也需要消耗系統(tǒng)資源,因此系統(tǒng)對等待隊列的大小 都有限制,程序中可以通過函數(shù)設(shè)定等待隊列的大小,但是不能超過系統(tǒng)的硬性限制。下面列出了幾個操作系統(tǒng)的最大等待隊列的大小:
操作系統(tǒng)
最大等待隊列
Windows 2000 Server
200
Windows XP Home
5
Solaris E250
128
上 表中只簡單列出了幾個操作系統(tǒng)的等待隊列參數(shù),其他系統(tǒng)暫位列出,如果有興趣可以自己作個簡單的程序測試一下。所以這個問題就跟具體的系統(tǒng)環(huán)境有關(guān)了,不 過我們可以在系統(tǒng)連接池的基礎(chǔ)上再做一些工作,采用連接緩沖池技術(shù)。就是接受到網(wǎng)絡(luò)連接請求以后,提交給出去線程處理的之前,先放到一個緩沖池中。這樣可 以接受更多的連接請求等待處理,能一定程度的提高系統(tǒng)的連接成功率。不過,跟系統(tǒng)的等待隊列不同,這是通過軟件方式實現(xiàn)的等待隊列,而系統(tǒng)提供的連接池是 從操作系統(tǒng)級來解決問題的,更接近硬件層次,所以效率肯定會不同。面對這類問題,首先還是得以調(diào)整系統(tǒng)連接池的大小,然后再采用其他輔助手段。
1.2.4 容錯性的影響
采 用C/C++開發(fā)程序,緩沖區(qū)溢出的錯誤非常普遍,但是系統(tǒng)運行程序的時候,對待運行期出現(xiàn)的這些錯誤的處理能力都不相同??偟膩碚f,Windows系統(tǒng) 的容錯性最強(qiáng),尤其是Debug版的程序,系統(tǒng)都加入了一些保護(hù)機(jī)制,能夠保證出現(xiàn)一些小的錯誤以后,程序仍能夠正常運行。Unix平臺下面要求的就嚴(yán)格 一些,有些系統(tǒng)更是容不得一點沙子,有一點錯誤就會出現(xiàn)宕機(jī)現(xiàn)象,這些跟操作系統(tǒng)的內(nèi)存分配機(jī)制有關(guān)。Windows平臺的程序分配內(nèi)存的時候,一般都會 多分出一些字節(jié)用于對齊,如果緩沖區(qū)溢出的不是太多,就不回對內(nèi)存中其他變量的值造成影響,因此程序也能夠正常運行。但是這種保護(hù)機(jī)制會帶來更多的系統(tǒng)開 銷。這就是Windows程序移植到Unix下面穩(wěn)定性降低的主要原因之一,也是為什么Windows系統(tǒng)會消耗那么多系統(tǒng)資源的原因。
要解決這類問題,就要進(jìn)行更嚴(yán)格的測試和代碼檢查。同時,借助相關(guān)的測試工具,找出系統(tǒng)中隱藏的潛在的問題,不能放過任何一個可能產(chǎn)生的錯誤,尤其是編譯過程中發(fā)現(xiàn)的警告信息。當(dāng)然,這些工作都應(yīng)該再移植前做的很充分,在移植后更應(yīng)該加大測試的力度。
1.3 圖形用戶界面
Windows 和Unix 圖形模型差異極大,這點是Unix和Windows程序開發(fā)最大的差別。UNIX 使用X Window 系統(tǒng)GUI,而Windows 使用GDI。雖然在概念上類似,但是X API 和GDI API 之間沒有簡單的對應(yīng)。在Windows下面可以通過MFC等類庫很方便的開發(fā)出圖形用戶界面的程序,而Unix下相對來說就麻煩了些,缺少哪種所見即所得 的好的開發(fā)工具。Unix下的GUI程序開發(fā),是一個比較復(fù)雜的過程,這里就不在詳細(xì)介紹。如果要進(jìn)行Unix下面GUI程序的開發(fā)的話,可以單獨去查找 相關(guān)的文檔。
1.4 并發(fā)處理
并發(fā)處理包括多進(jìn)程和多線程的概念,Windows和Unix的并發(fā)處理差別也比較大,但是基本上都能找到一組對應(yīng)的函數(shù)來實現(xiàn)類似的功能。
在Windows 下,創(chuàng)建進(jìn)程和線程可以通過調(diào)用Windows的API來完成,或者通過調(diào)用MFC提供的并發(fā)處理類庫來實現(xiàn)。在Unix下面創(chuàng)建進(jìn)程通常使用fork函 數(shù),這跟Windows下面的多進(jìn)程概念有所不同,相當(dāng)于在當(dāng)前位置給當(dāng)前進(jìn)程創(chuàng)建一個副本;而Windows下的創(chuàng)建進(jìn)程大都是創(chuàng)建一個新的進(jìn)程。 Unix下的多線程操作,通過一組線程函數(shù)來完成,通常我們使用POSIX 的PTHREAD線程庫來創(chuàng)建線程,但是在不同的Unix分支系統(tǒng)中,都包含又自己的本地線程庫。如在Solaris下面的本地線程庫,是一組以thr_ 開頭的函數(shù),而POSIX的線程函數(shù)一般都已pthread_開頭。雖然有兩種不同的線程庫空我們選擇,但在某一個特定的系統(tǒng)下,他們的實現(xiàn)實質(zhì)都是一樣 的,而且基本上都能夠找到對應(yīng)的函數(shù)。為了程序的可移植性,建議采用POSIX的線程庫。這是大多數(shù)Unix系統(tǒng)都支持的線程庫,但是不同系統(tǒng)下實現(xiàn)的功 能可能有所差別,可能只是實現(xiàn)了這個函數(shù)庫的一個子集。
在 有些Unix系統(tǒng)下,沒有實現(xiàn)線程庫,如SCO Unix,系統(tǒng)只提供多進(jìn)程的開發(fā)方式。但是,如果為了實現(xiàn)程序代碼的統(tǒng)一性,我們可以采用第三方提供的線程庫。這里有一個叫FSU-threads的線 程庫供我們選擇。這個線程庫中實現(xiàn)了POSIX中定義的線程函數(shù),而且是開源的,可以支持SunOS 4.1.x, Solaris 2.x, SCO UNIX, FreeBSD, Linux等系統(tǒng)。除此之外,還有ZThreads線程庫等。
在Windows 的線程庫中,實現(xiàn)了互斥(Mutex)、事件(Event)、信號量(Semaphore)等同步對象,用于實現(xiàn)線程之間的同步。在Unix下面,線程同 步主要使用互斥(mutex)和條件變量(cond),其中條件變量可以實現(xiàn)事件和信號量的功能。另外, POSIX還定義了一套信號量函數(shù),跟線程函數(shù)不同,是一組以sem_開頭的函數(shù)(POSIX 1003.1b semaphores)。但是這套函數(shù)就不想POSIX線程函數(shù)支持的那么廣泛了,比如在AIX上就不支持POSIX的信號量函數(shù),不過AIX系統(tǒng)下有另 一組函數(shù)來實現(xiàn)信號量的功能(SystemV semaphores)。在很多Unix系統(tǒng)中,同時支持POSIX的信號量和SystemV的信號量,在Solaris下面還有一套自己的本地函數(shù)來實 現(xiàn)信號量。下面分別列出Unix系統(tǒng)中的用于并發(fā)處理的主要的函數(shù)。
進(jìn)程
fork 創(chuàng)建進(jìn)程
POSIX線程庫
pthread_create 創(chuàng)建一個信的線程
pthread_attr_init 初始化一個線程屬性對象
pthread_attr_destroy 釋放一個線程屬性對象
pthread_exit 終止執(zhí)行調(diào)用的線程
pthread_join 把當(dāng)前調(diào)用線程掛起,直到目標(biāo)線程結(jié)束
pthread_setschedparam 設(shè)置線程的調(diào)度策略和優(yōu)先級
pthread_getschedparam 獲得線程的調(diào)度策略和優(yōu)先級
pthread_sigmask 改變/檢查調(diào)用線程的信號掩碼
pthread_kill 發(fā)送信號到另一個線程
pthread_self 返回當(dāng)前線程的ID
pthead_mutex_init 初始化一個互斥量
pthread_mutexattr_init 初始化互斥量的屬性對象
pthread_mutex_lock 給一個互斥量加鎖,如果互斥量已經(jīng)被別的線程鎖定,調(diào)用線程掛起,直到別的線程釋放
pthread_mutex_unlock 釋放互斥量(解鎖)
ptherad_mutex_destroy 銷毀一個互斥量
pthread_cond_init 初始化一個條件變量
pthread_condattr_init 初始化一個條件變量的屬性對象
pthread_cond_wait 阻塞在一個條件變量上
pthread_cond_signal 解除下一個線程在條件變量的阻塞
pthread_cond_boradcast 解除所有線程在這個條件變量上的阻塞
pthread_cond_destroy 銷毀一個條件變量
pthread_cancel 請求結(jié)束一個線程
Solaris本地線程庫
thr_create 創(chuàng)建一個新線程
thr_exit 終止調(diào)用線程
thr_join 把當(dāng)前調(diào)用線程掛起,直到目標(biāo)線程結(jié)束
thr_yield 用當(dāng)前線程創(chuàng)建出另一個線程
thr_suspend 掛起一個指定的線程
thr_continue 恢復(fù)一個被掛起的線程
thr_setprio 修改線程的優(yōu)先級
thr_getprio 獲得線程的優(yōu)先級
thr_sigsetmask 改變/檢查調(diào)用線程的信號掩碼
thr_kill 發(fā)送信號到另一個線程
thr_self 返回當(dāng)前線程的ID
thr_main 標(biāo)記為主線程
thr_mutex_init 初始化一個互斥量
thr_mutex_lock 給一個互斥量加鎖,如果互斥量已經(jīng)被別的線程鎖定,調(diào)用線程掛起,直到別的線程釋放
thr_mutex_unlock 釋放互斥量(解鎖)
thr_mutex_destroy 銷毀一個條互斥量
thr_cond_init 初始化一個條件變量
thr_cond_wait 阻塞在一個條件變量上
thr_cond_signal 解除下一個線程在條件變量的阻塞
thr_cond_boradcast 解除所有線程在這個條件變量上的阻塞
thr_cond_destroy 銷毀任何狀態(tài)的條件變量
rwlock_init 初始化一個讀寫鎖
rw_rdlock 獲得一個讀寫鎖的讀鎖定
rw_wrlock 獲得一個讀寫鎖的寫鎖定
rw_unlock 解鎖一個讀寫鎖
POSIX信號量
sem_init 初始化一個信號量
sem_destroy 銷毀一個信號量
sem_wait 等待獲得一個信號量,獲得后信號量的值減1,如果當(dāng)前信號量值位0,當(dāng)前線程阻塞,支持有別的線程釋放信號量
sem_trywait 嘗試獲得一個信號量,獲得后信號量的值減1,如果當(dāng)前信號量值位0,返回失敗
sem_post 釋放一個信號量
sem_getvalue 獲得指定信號量的值
System V信號量
javascript:if(confirm('http://personal.xfol.com/~rezaie/api/ipc/man.cgi@semctl%20%20''n''nThis%20file%20was%20not%20retrieved%20by%20Teleport%20Pro,%20because%20the%20server%20reports%20that%20this%20file%20cannot%20be%20found.%20%20''n''nDo%20you%20want%20to%20open%20it%20from%20the%20server?'))window.location='http://personal.xfol.com/%7Erezaie/api/ipc/man.cgi@semctl'>semctl 對信號量進(jìn)行一系列的控制
javascript:if(confirm('http://personal.xfol.com/~rezaie/api/ipc/man.cgi@semget%20%20''n''nThis%20file%20was%20not%20retrieved%20by%20Teleport%20Pro,%20because%20the%20server%20reports%20that%20this%20file%20cannot%20be%20found.%20%20''n''nDo%20you%20want%20to%20open%20it%20from%20the%20server?'))window.location='http://personal.xfol.com/%7Erezaie/api/ipc/man.cgi@semget'>semget 創(chuàng)建一個信號量,成功時返回信號的ID
javascript:if(confirm('http://personal.xfol.com/~rezaie/api/ipc/man.cgi@semop%20%20''n''nThis%20file%20was%20not%20retrieved%20by%20Teleport%20Pro,%20because%20the%20server%20reports%20that%20this%20file%20cannot%20be%20found.%20%20''n''nDo%20you%20want%20to%20open%20it%20from%20the%20server?'))window.location='http://personal.xfol.com/%7Erezaie/api/ipc/man.cgi@semop'>semop 對信號進(jìn)行操作
Solaris的本地信號量,更接近于操作系統(tǒng)中我們學(xué)到的PV操作的信號燈
sema_init 初始化一個信號燈(信號量)
sema_destroy 銷毀一個信號燈
sema_p 執(zhí)行信號燈的P操作
sema_p_sig 跟sema_p類似,當(dāng)阻塞再這個函數(shù)的時候,如果線程收到一個信號,函數(shù)退出
sema_tryp 嘗試執(zhí)行信號燈的P操作
sema_v 執(zhí)行信號燈的V操作
為了方便使用,我在開發(fā)的過程中已經(jīng)把上面常用的函數(shù)都封裝成了類,兼容Windows和各種常見的Unix系統(tǒng),而且網(wǎng)上還有很多這方面的代碼資源可用。如果感興趣的話可以向我索要。
1.5 網(wǎng)絡(luò)通信
Socket (中文譯名:套接字)最初在Unix上出現(xiàn),并很快成為Unix上最流行的網(wǎng)絡(luò)編程接口之一。后來,微軟將它引入到Windows中并得到實現(xiàn),于是從 Windows 95、WinNT4開始,系統(tǒng)就內(nèi)置了Winsock1.1,后來到了Windows98、Windows2000,它內(nèi)置的Winsock DLL更新為Winsock2.2。
Windows 下的Socket函數(shù)大體上和Unix下的Socket函數(shù)差不多,函數(shù)名稱很參數(shù)用法都類似,只有一些細(xì)微的差別,某些參數(shù)的意義不同,而且對于 Socket的屬性控制也不太一樣。Windows下面還對Socket函數(shù)進(jìn)行了封裝,有一系列相關(guān)類可用使用,簡化網(wǎng)絡(luò)編程的復(fù)雜性。Unix本身沒 有這些類庫,但是我們也已經(jīng)積累了很多這方面的經(jīng)驗和資源。我們有一組現(xiàn)成的類對Windows和Unix下的Socket函數(shù)進(jìn)行了封裝,上層只需要簡 單的調(diào)用即可,不用關(guān)心底層的差別。而且,這套類庫也可以同時支持多種平臺,可移植性非常好。