2013年7月8日
#
在Linux系統(tǒng)中,有兩種不同的上下文:進(jìn)程上下文、中斷上下文。
在中斷中沒有進(jìn)程上下文,而具有中斷上下文,因此在中斷上下文中不能發(fā)生睡眠,也就是不能發(fā)生進(jìn)程切換。
這就決定了在在中斷上下文中不能采用同步原語(信號(hào)量,管程、同步變量等)和長期互斥原語(這會(huì)導(dǎo)致進(jìn)程睡眠), 而只能采用短期互斥原語(例如自旋鎖)。
曾經(jīng),中斷處理程序并不具有自己的棧。相反,它們共享所中斷進(jìn)程的內(nèi)核棧。內(nèi)核棧的大小是兩頁,具體地說,在32位體系結(jié)構(gòu)上是8KB,在64位體系結(jié)構(gòu)上是16KB.
現(xiàn)在。中斷處理程序有了自己的棧,每個(gè)處理器一個(gè),大小為一頁。這個(gè)棧就稱為中斷棧,盡管中斷棧的大小是原先共享?xiàng)5囊话耄骄捎脳?臻g大得多,因?yàn)橹袛嗵幚?br />程序把這一整頁占為己有。
UP(單CPU系統(tǒng))上的中斷處理
互斥
如果一個(gè)中斷處理程序的代碼訪問或者更新了由非中斷的代碼(通常稱為基準(zhǔn)代碼)使用的同一數(shù)據(jù)結(jié)構(gòu),那么就會(huì)出現(xiàn)競爭條件。
幸運(yùn)的是,得到允許的以內(nèi)核態(tài)執(zhí)行的進(jìn)程會(huì)臨時(shí)禁止中斷。因此,只要基準(zhǔn)代碼要更新一個(gè)與中斷處理程序共享的數(shù)據(jù)結(jié)構(gòu),那么就
首先禁止中斷,執(zhí)行臨界段,然后再重新允許中斷。禁止和允許中斷的動(dòng)作就實(shí)現(xiàn)了互斥。
在采取中斷互斥時(shí),必須使用函數(shù)顯示地把編碼寫入算法中。
MP(多CPU系統(tǒng))上
在MP系統(tǒng)上,中斷可以在任何處理器上出現(xiàn)。從最低限度上來說,每個(gè)進(jìn)程會(huì)接收時(shí)鐘中斷,但也可能接收I/O中斷。在MP系統(tǒng)上,例程
SPL(禁止中斷)所提供的保護(hù)并不充分,因?yàn)樗鼈儓?zhí)行影響執(zhí)行它們的處理器上的中斷優(yōu)先級(jí)。中斷可能會(huì)在另一個(gè)處理器上出現(xiàn),如果設(shè)備驅(qū)動(dòng)程序
正在別處運(yùn)行,那么會(huì)造成一個(gè)競爭條件。因?yàn)橹袛嗵幚沓绦虼砹硪粋€(gè)進(jìn)入內(nèi)核的入口點(diǎn)。
當(dāng)基準(zhǔn)驅(qū)動(dòng)程序代碼和中斷處理程序之間共享數(shù)據(jù)結(jié)構(gòu)時(shí),UP可以通過屏蔽中斷來防止出現(xiàn)競爭條件的技術(shù),在多線程MP內(nèi)核中還不充分。
臨界段要在一個(gè)處理器上執(zhí)行,執(zhí)行屏蔽中斷例程只會(huì)屏蔽在那個(gè)處理器上出現(xiàn)的中斷。如果在別的處理器上出現(xiàn)中斷,那么立即就會(huì)有
兩個(gè)處理器同時(shí)訪問、而且可能更新臨界資源。既然這些臨界段需要短期互斥,那么可以使用自旋鎖來對數(shù)據(jù)進(jìn)行保護(hù)。
如果不考慮中斷處理程序和基準(zhǔn)代碼之間的互斥,則Linux中的中斷處理程序是無須重入的。當(dāng)一個(gè)給定的中斷處理程序正在執(zhí)行時(shí),相應(yīng)的中斷線
在所有處理器上都會(huì)被屏蔽掉,以防止同一中斷線上接收另一個(gè)新的中斷。通常情況下,所有的其他中斷都是打開的,所以這些不同中斷線上的其他中斷
都能處理,但當(dāng)前中斷線總是被禁止的。由此可以看出,同一個(gè)中斷處理程序絕不會(huì)被同時(shí)調(diào)用以處理嵌套的中斷。這極大地簡化了中斷程序的編寫。
2013年6月20日
#
MP(多CPU)同步原語代碼示例
----引自《現(xiàn)代體系結(jié)構(gòu)上的UNIX系統(tǒng)》
為了便于對示例的展開,我們首先假定一個(gè)場景:
內(nèi)核中把檢測到的錯(cuò)誤信息記錄到一個(gè)磁盤文件中的情形。出錯(cuò)信息通過內(nèi)存中的一個(gè)隊(duì)列來傳遞給日志進(jìn)程(logging process)的。
當(dāng)出現(xiàn)一個(gè)錯(cuò)誤時(shí),就在隊(duì)列中加入一項(xiàng),并且通過調(diào)用函數(shù)log_error 通知日志進(jìn)程。出錯(cuò)日志進(jìn)程接著把隊(duì)列中的項(xiàng)寫到磁盤上。
這就使得碰到錯(cuò)誤的進(jìn)程不必等候I/O完成或者或得為了向文件執(zhí)行I/O而可能需要的任何鎖,并且避免了任何可能的上鎖次序問題。
代碼1, 采用事件計(jì)數(shù)的出錯(cuò)日志通知機(jī)制
日志進(jìn)程
log_error(error) |
{ | for(next=1; ; next++) {
lock(&err_queue); | await(&err_event, next);
把出錯(cuò)信息加入到隊(duì)列 | lock(&err_queue);
unlock(&err_queue); | 從隊(duì)列中刪除項(xiàng)
advance(&err_event); | unlock(&err_queue);
} | write error to disk
| }
隊(duì)列本身由一個(gè)自旋鎖來保護(hù)。在本例中,事件計(jì)數(shù)只用于同步的目的,并不提供互斥。
在試用事件計(jì)數(shù)的時(shí)候,advance操作會(huì)永久性地改變事件計(jì)數(shù)的狀態(tài)。advance和await操作的相對時(shí)序沒有關(guān)系。
代碼2, 采用同步變量的出錯(cuò)日志通知機(jī)制
日志進(jìn)程
log_error(error) |
{ | for(;;){
lock(&err_queue); | lock(&err_queue);
把出錯(cuò)信息加入到隊(duì)列 | if (queue_empty){
SV_SIGNAL(&err_syncvar, 0); | SV_WAIT(&err_syncvar, PRI, &err_queue);
unlock(&err_queue); | lock(&err_queue);
} | }
| 從隊(duì)列中刪除項(xiàng)
| unlock(&err_queue);
| 把錯(cuò)誤寫入磁盤
| }
因?yàn)橥阶兞孔陨頉]有保留狀態(tài),所以當(dāng)日志進(jìn)程測試隊(duì)列的狀態(tài)并決定是等待一項(xiàng)還是從隊(duì)列中刪除一項(xiàng)的時(shí)候,必須占有自旋鎖。類似地,log_error在
發(fā)送信號(hào)時(shí)也必須占有自旋鎖。注,SV_WAIT將釋放自旋鎖,并且阻塞日志進(jìn)程,SV_SIGNAL到后從阻塞處繼續(xù)執(zhí)行。
代碼3, 采用管程的出錯(cuò)日志通知機(jī)制
日志進(jìn)程
log_error(error) | for(;;){
{ | mon_enter(&err_mon);
mon_enter(&err_mon); | if (queue empty)
把出錯(cuò)信息加入到隊(duì)列 | mon_wait(&err_mon, NEWENTRY);
|
mon_signal(&err_mon, NEWENTRY); | 從隊(duì)列中刪除項(xiàng)
mon_exit(&err_mon); | mon_exit(&err_mon);
} | 把錯(cuò)誤寫入磁盤
| }
代碼4, 采用信號(hào)量的出錯(cuò)日志通知機(jī)制
日志進(jìn)程
log_error(error) | for(;;){
{ | P(&err_sema);
lock(&err_queue); | lock(&err_queue);
把出錯(cuò)信息加入到隊(duì)列 | 從隊(duì)列中刪除項(xiàng)
unlock(err_queue); | unlock(&err_queue);
V(&err_sema); | 把錯(cuò)誤寫入磁盤
} | }
我們把異步環(huán)境下的一組并發(fā)進(jìn)程因直接制約而互相發(fā)送消息、進(jìn)行互相合作、互相等待,使得各進(jìn)程按一定的速度執(zhí)行的過程稱為進(jìn)程間的同步。
具有同步關(guān)系的一組并發(fā)進(jìn)程稱為合作進(jìn)程,合作進(jìn)程間互相發(fā)送的信號(hào)稱為消息或事件。 如果我們對一個(gè)消息或事件賦以唯一的消息名,則我們
可用過程 wait (消息名) 表示進(jìn)程等待合作進(jìn)程發(fā)來的消息,而用過程 signal (消息名) 表示向合作進(jìn)程發(fā)送消息。
(引自百度百科)
進(jìn)程間的同步方式:
為了實(shí)現(xiàn)進(jìn)程互斥地進(jìn)入自己的臨界區(qū),操作系統(tǒng)中設(shè)置專門的同步機(jī)制來協(xié)調(diào)各進(jìn)程間的運(yùn)行。所有的同步機(jī)制都應(yīng)遵循下書四條準(zhǔn)則:
1)空閑讓進(jìn)
2)忙則等待
3)有限等待
4)讓權(quán)原則。當(dāng)進(jìn)程不能進(jìn)入自己的臨界區(qū)時(shí),應(yīng)立即釋放處理機(jī),以免進(jìn)程陷入“忙等”狀態(tài)。
1.單CPU (UP)機(jī)器利用sleep/wakeup函數(shù)對實(shí)現(xiàn)同步機(jī)制。
函數(shù)sleep是一個(gè)內(nèi)部的內(nèi)核例程,它掛起調(diào)用它的進(jìn)程,直到指定的事件發(fā)生為止。這是一個(gè)以內(nèi)核態(tài)運(yùn)行的進(jìn)程自愿出讓控制權(quán),允許自己被搶占。
函數(shù)wakeup用于發(fā)出一個(gè)特定事件已經(jīng)出現(xiàn)的信號(hào),它使得所有等待該事件的進(jìn)程被喚醒,并放回到運(yùn)行隊(duì)列中。事件用一個(gè)整數(shù)值來表示,它往往
是該事件相關(guān)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)的地址。
void lock_object( char *flag_ptr)
{
lock(&object_locking); //自旋鎖
while (*flag_ptr)
sleep(flag_ptr);
*flag_ptr = 1;
unlock(&object_locking);
}
void unlock_object( char *flag_ptr)
{
lock( &object_locking );
*flag_ptr = 0;
wakeup( flag_ptr);
unlock( &object_locking );
}
應(yīng)為wakeup操作沒有記憶,所以wakeup函數(shù)必須喚醒在同一事件上睡眠的所有進(jìn)程。在多CPU系統(tǒng)上,即MP上sleep/wakeup機(jī)制不起作用。
2.SVR4.2 MP 提供了單獨(dú)的執(zhí)行進(jìn)程同步的原語:同步變量。
因?yàn)橥阶兞坎话瑺顟B(tài),所以可以把它們想成是sleep/wakeup的一種MP變形。相反,所需的任何狀態(tài)信息都保存在外部標(biāo)志或者計(jì)數(shù)器中。
同步變量的設(shè)計(jì)要同自旋鎖配合工作。
同步變量被聲明為sv_t類型,采用下面的函數(shù)可以給它分配空間和進(jìn)行初始化:
sv_t *SV_ALLOC( int slpflag);
slpflag指定,如果需要為同步變量分配內(nèi)存,那么是否能阻塞進(jìn)程。
回收同步變量可以調(diào)用
void SV_DEALLOC( sv_t *svp );
內(nèi)核希望單獨(dú)等候的每一個(gè)事件都用一個(gè)不同的同步變量來表示,這就好比配合sleep如何使用唯一的事件參數(shù)。
void SV_WAIT( sv_t *svp, int pri, lock_t *lockp );
要觸發(fā)在同步變量上的事件,可以使用下面的函數(shù):
void SV_SIGNAL( sv_t *svp, int flags);
SV_SIGNAL與wakeup的相似之處在于,如果沒有正在睡眠的進(jìn)程,那么就對過去曾經(jīng)執(zhí)行過的操作沒有記憶,調(diào)用什么也不做。
SV_SIGNAL只喚醒一個(gè)進(jìn)程。如果要喚醒在事件上睡眠的所有進(jìn)程,可以用同步變量的下列函數(shù)來實(shí)現(xiàn):
void SV_BROADCAST( sv_t *svp, int flags);
如果在事件被觸發(fā)之前出現(xiàn)了一個(gè)UNIX信號(hào),那么下面的SV_WAIT變形會(huì)喚醒進(jìn)程:
bool_t SV_WAIT_SIG( sv_t *svp, int pri, lock_t *lkp );
返回的代碼表明發(fā)生了什么樣的事件:如果出現(xiàn)了一個(gè)UNIX信號(hào),那么它返回FALSE,如果出現(xiàn)了SV_SIGNAL或SV_BROADCAST,那么它返回TRUE.
3.采用信號(hào)量的同步
將信號(hào)量的值初始化為0,就可以用于進(jìn)程同步,這樣允許通過使用P操作讓一個(gè)進(jìn)程等待某個(gè)事件發(fā)生。既然信號(hào)量被初始化為0,那么進(jìn)程將立即阻塞。
另一個(gè)進(jìn)程使用V操作能夠發(fā)出信號(hào),表明事件已經(jīng)結(jié)束。V操作導(dǎo)致正等待事件的進(jìn)程被喚醒,并繼續(xù)進(jìn)行。因?yàn)榧词乖谛盘?hào)量上沒有阻塞進(jìn)程,
V操作也會(huì)給信號(hào)量加1,所以在前一個(gè)進(jìn)程能夠執(zhí)行P操作之前出發(fā)事件會(huì)導(dǎo)致進(jìn)程繼續(xù)進(jìn)行,不必等待。這是一種受歡迎的情形,因?yàn)樗恍枰~外的
協(xié)調(diào)工作,就能夠處理在等候事件的進(jìn)程同發(fā)信號(hào)表明該事件完成的進(jìn)程之間本來就有的競爭條件。
進(jìn)程1 進(jìn)程2
p(s) /*等待事件*/ .
.
.
V(s) /*觸發(fā)事件*/
4.利用管程進(jìn)行同步
管程為臨界資源以及訪問或者修改該資源的所有臨界段提供了互斥機(jī)制,它還提供了在使用管程的諸進(jìn)程之間進(jìn)行同步的手段。一個(gè)管程可以想成是一個(gè)裝有
資源的隔間。進(jìn)程要訪問資源,它必須首先進(jìn)入隔間。通過一次只允許一個(gè)進(jìn)程進(jìn)入隔間,就做到了互斥。如果在管程已經(jīng)投入使用的時(shí)候,別的進(jìn)程試圖進(jìn)
入它,那就會(huì)被阻塞,直到使用管程的進(jìn)程退出管程為止,或者在與管程關(guān)聯(lián)的事件上等待。每個(gè)管程都可能有一個(gè)或者更多的事件,若干進(jìn)程能夠在這些事
件上等待。進(jìn)程被阻塞這些事件上,直到在管程內(nèi)執(zhí)行的其他進(jìn)程觸發(fā)事件為止。根據(jù)定義,觸發(fā)操作只能從管程內(nèi)部完成。
5.利用事件計(jì)數(shù)進(jìn)行同步
事件計(jì)數(shù)是一個(gè)非遞減的正整數(shù),在這個(gè)數(shù)值上定義了3種操作。操作advance(E)將事件計(jì)數(shù)E加1,這叫做出發(fā)事件。
操作await(E,V)致使調(diào)用進(jìn)程被阻塞,指導(dǎo)事件計(jì)數(shù)E的值達(dá)到V為止。如果在調(diào)用await的時(shí)候,事件計(jì)數(shù)的值大于或等于V,那么進(jìn)程繼續(xù)執(zhí)行,而不會(huì)阻塞,
因?yàn)槭录且郧坝|發(fā)的。事件計(jì)數(shù)的當(dāng)前值可以用read(E)來讀取。在創(chuàng)建事件計(jì)數(shù)的時(shí)候,它被初始化為0,而且在數(shù)值上永遠(yuǎn)不會(huì)減少。假定保存事件計(jì)數(shù)值
得存儲(chǔ)器位置足夠大,于是事件計(jì)數(shù)在其整個(gè)生命期中,一直都不會(huì)溢出(通常一個(gè)32位的無符號(hào)整數(shù)就夠了)。
有關(guān)代碼示例,請參見后面的隨筆。
2013年6月8日
#
在多CPU系統(tǒng),即MP系統(tǒng)中,存在總線仲裁。
1. 原子操作
從CPU或者I/O設(shè)備到主存儲(chǔ)器的單次讀或者寫操作為原子操作。
這樣的操作一旦開始,就不能被系統(tǒng)上來自CPU或者I/O設(shè)備的任何其他存儲(chǔ)操作所中斷,或者受到他們的干擾。
原子變量
2. 自旋鎖 (自旋鎖用于短期互斥)
自旋鎖得名于這樣一個(gè)事實(shí),一個(gè)進(jìn)程在等候另一個(gè)進(jìn)程正在使用的鎖時(shí)會(huì)處于忙等待(busy-wait,在一個(gè)循環(huán)中自旋)狀態(tài)。
typedef int lock_t;
void initlock( volatile lock_t * lock_status)
{
*lock_status = 0;
}
int
test_and_set(volatile int *addr)
{
int old_value;
old_value = swap_atomic(addr, 1);
if (old_value == 0)
return 0;
return 1;
}
void lock(volatile lock_t *lock_status)
{
while (test_and_set( lock_status) == 1) //被鎖定時(shí),進(jìn)程在此自旋。
;
}
// test_and_set 如果前面的狀態(tài)不為0就返回1,否則返回0.
//如果鎖的狀態(tài)已經(jīng)是1(鎖已經(jīng)被占用),那么test_and_set函數(shù)返回1,并且處理器在循環(huán)中自旋,直到該鎖被釋放為止。只要把鎖的狀態(tài)設(shè)置為0,就可以釋放鎖了。
void
unlock(volatile lock_t * lock_status)
{
*lock_status =0;
}
減少對鎖的爭用可以采用兩種辦法:
第一、內(nèi)核針對不同的臨界資源使用不同的自旋鎖,防止處理器在沒有競爭條件威脅的時(shí)候被另一個(gè)處理器掛起。
第二、增強(qiáng)lock和unlock函數(shù),在上鎖的時(shí)候屏蔽中斷。
在平時(shí)的應(yīng)用設(shè)計(jì)中,由于出現(xiàn)多線程(進(jìn)程)的并發(fā)應(yīng)用,以及分布式應(yīng)用,比較容易出現(xiàn)死鎖現(xiàn)象。
下面來看一個(gè)簡單的實(shí)例:
有兩個(gè)獨(dú)立的鏈表,假定使用兩個(gè)獨(dú)立的鎖來保護(hù)他們,以便能夠獨(dú)立的訪問。再假定某個(gè)操作要求遍歷一個(gè)列表所檢索到的元素必須從兩個(gè)列表中斷開連接,
而且必須以一次
原子操作來完成。
線程1 | 線程2
lock(&lock_a); | lock(&lock_b);
find element to unlink on list a | find element to unlink on list b
lock(&lock_b); | lock(&lock_a);
unlink element from both lists | unlink element from both lists
unlock(&lock_b); | unlock(&lock_a);
unlock(&lock_a); | unlock(&lock_b);
可能出現(xiàn)死鎖的情況。
這要求在消除一個(gè)元素是必須同時(shí)擁有兩個(gè)列表的鎖。對第一個(gè)線程要先或得鎖lock_a, 然后要或得鎖lock_b.
第2個(gè)線程正好相反,它先或得鎖lock_b,然后再獲取鎖lock_a.
如果某一時(shí)刻,第一個(gè)線程執(zhí)行到了 find element to unlink on list a, 而第二個(gè)線程此時(shí)執(zhí)行到了find element to unlink on list b, 則這兩個(gè)線程將發(fā)生死鎖。
這種死鎖稱為AB-BA死鎖。 (注,死鎖的發(fā)生是跟兩個(gè)線程的執(zhí)行時(shí)序相關(guān)的,例如,第一個(gè)線程執(zhí)行完了所有的這部分代碼,線程2才開始執(zhí)行此段代碼,則不會(huì)發(fā)生死鎖。)
如果某個(gè)鎖不是遞歸鎖,例如lock_a, 而線程1在應(yīng)用中對它進(jìn)行多個(gè)調(diào)用,而沒有調(diào)用解鎖操作,也會(huì)發(fā)生死鎖。
代碼示例如下:
lock(&lock_a);
other logic code
lock(&lock_a);
other logic code
unlock(&lock_a);
unlock(&lock_a);
防止死鎖的辦法: 為了防止發(fā)生這類死鎖,所有線程必須以相同的次序獲得嵌套鎖,即以相同的次序獲得且同時(shí)占有鎖。
可以把上面的代碼改成如下代碼來避免死鎖:
lock(&lock_a);
lock(&lock_b);
find element to unlink on list a or b
unlink element from both lists
unlock(&lock_b);
unlock(&lock_a);
當(dāng)涉及到3個(gè)或者更多鎖的時(shí)候也是如此:只要各線程在獲得和釋放鎖的時(shí)候保持相同的次序,那么就不會(huì)出現(xiàn)死鎖。
2009年3月20日
#
實(shí)體(語言內(nèi)建的數(shù)據(jù)類型,開發(fā)者定義的類和方法等)的定義與聲明,實(shí)體和指針跟程序設(shè)計(jì)帶來了不同影響.
對于實(shí)體或定義,編譯器要知道實(shí)體真實(shí)的物理內(nèi)存布局,因此讓編譯器知道這些信息,并且在程序編譯完畢后不能更改.要想更改必須重新編譯程序.因此如果在系統(tǒng)設(shè)計(jì)者程序庫中運(yùn)用了inline函數(shù),并且如果應(yīng)用開發(fā)者在應(yīng)用中用了這個(gè)inline函數(shù),則當(dāng)后來要對inline進(jìn)行修改時(shí),有可能要導(dǎo)致應(yīng)用被重新編譯.
對于指針,它的大小在特定的機(jī)器上是固定的(在32位機(jī)器上,它的大小是32位;在64位機(jī)器上,它的大小是64位).因此可以改變它的值,而不需要重新編譯應(yīng)用,就可以改變應(yīng)用的功能.
在面向?qū)ο笾校梢酝ㄟ^虛函數(shù)指針來延遲特定函數(shù)的決策,即調(diào)用子類的函數(shù).
在C語言中,我們可以通過函數(shù)指針來對函數(shù)的功能進(jìn)行推遲決策.
在C++中,我們也可以通過函數(shù)指針(函數(shù)對象)、對象指針來推遲決策,從而使程序的功能更有彈性。例如,在設(shè)計(jì)模式中的strategy模式中,就是通過在contex中包含一個(gè)指向strategy的指針來實(shí)現(xiàn)的。我們可以定義一個(gè)抽象的strategy接口,然后由各個(gè)具體的strategy實(shí)現(xiàn)這些接口,從而在保證應(yīng)用架構(gòu)幾乎不做任何調(diào)整下,實(shí)現(xiàn)不同的功能。當(dāng)然在這種實(shí)現(xiàn)方式中,我們應(yīng)該加入strategy的決議者,由它來裁決采用哪一種策略方式。決議者可以采用配置文件、應(yīng)用的輸入等作為決議的依據(jù)。
熟悉symbian的人,很快就會(huì)發(fā)現(xiàn):它與symbian中的ECOM架構(gòu)很相似。它要求各種strategy的實(shí)現(xiàn)方式被包含在共享的DLL中,并由RLibrary::Loard()調(diào)用動(dòng)態(tài)載入.
使用定義抽象接口,然后在各DLL中定義具體的實(shí)現(xiàn),并且動(dòng)態(tài)載入,我們可以比較容易地實(shí)現(xiàn)所謂的插件(plugin)。插件的載入取決于配置,或相應(yīng)的輸入檢測。
下面給出在linux和windows上從動(dòng)態(tài)庫中查找和載入的例子:
#ifdef WIN32
HINSTANCE hDll;
if(!(hDll = LoadLibrary(VOCALSIP_DLLPATH)))
adapter.m_initFunc = (INIT_PROTOSTACK_FUNC)GetProcAddress( hDll, "InitVocalSipStack");
adapter.m_createFunc = (CREATE_CHANNEL_FUNC)GetProcAddress( hDll, "CreateVocalSipGCChannel");
adapter.m_cleanupFunc = (CLEANUP_PROTOSTACK_FUNC)GetProcAddress( hDll, "CleanupVocalSipStack");
#else
void* h_dl = dlopen(VOCALSIP_DLLPATH,RTLD_NOW | RTLD_GLOBAL);
adapter.m_initFunc = (INIT_PROTOSTACK_FUNC)dlsym( h_dl, "InitVocalSipStack");
adapter.m_createFunc = (CREATE_CHANNEL_FUNC)dlsym( h_dl, "CreateVocalSipGCChannel");
adapter.m_cleanupFunc = (CLEANUP_PROTOSTACK_FUNC)dlsym( h_dl, "CleanupVocalSipStack");
#endif
2009年3月18日
#
"互斥(mutext)和旗語(semaphore)之間有什么不同?"這樣的問題簡短而有力,但要回答卻相當(dāng)困難.即使有經(jīng)驗(yàn)的實(shí)時(shí)操作系統(tǒng)(RTOS)用戶在區(qū)別如何正確使用mutex和semaphore時(shí)也存在著困難.
但這一點(diǎn)很不幸而且很危險(xiǎn),因?yàn)闊o任這兩種原生RTOS中的哪一種被錯(cuò)誤使用,都會(huì)導(dǎo)致嵌入式系統(tǒng)出現(xiàn)意想不到的錯(cuò)誤,特別是這些系統(tǒng)為有關(guān)生命安全的產(chǎn)品時(shí).
有關(guān)mutex和semaphore的荒誕說法是它們是相似的,甚至是可以互換的.正確的事實(shí)是盡管mutex和semaphore在它們的執(zhí)行上有相似之處,但是我們還是應(yīng)該在使用它們時(shí)加以區(qū)別對待.
最普遍(但也是不正確)的答案是:mutex和semphore非常相似,它們只有一個(gè)區(qū)別,那就是semaphores的計(jì)數(shù)可以超過1. 差不多所有的工程師都能正確的理解:mutex是一個(gè)二進(jìn)制標(biāo)志,可以通過它來確保執(zhí)行流在代碼關(guān)鍵區(qū)(critical section of code)互相排斥,從而對共享資源加一保護(hù).但當(dāng)他們被要求進(jìn)一步回答如何使用"計(jì)算方法semaphore"的方式時(shí),大部分工程師的回答就如同教科書書一般的刻板---semaphore用于保護(hù)多重同類資源.
通過類比辦法,我們很容易解釋為什么"多重資源"場景是有缺陷的.如果你認(rèn)為一個(gè)mutex是由操作系統(tǒng)擁有的關(guān)鍵值的話,我們可以很容易地將個(gè)別的mutex比喻是城市咖啡店中一間浴室的鑰匙.如果你想使用浴室,卻找不到鑰匙,你就必須在一個(gè)隊(duì)列中等候.同樣地,mutex則協(xié)串行化多項(xiàng)任務(wù),以取得全域資源的共享,并且為等待隊(duì)列中的任務(wù)分配一個(gè)靜候其循序漸進(jìn)的位置.
但這種簡單的資源保護(hù)協(xié)議并不使用于兩間相同浴室的情況.如果把一個(gè)semaphore概括為一個(gè)mutex,使其能保護(hù)兩個(gè)或更多相同的資源,那么在我們的比喻中,它就象是放著兩把相同鑰匙的藍(lán)子,你可以用任何一把打開任何一扇浴室的門.
因此,semaphore本身并不能解決多個(gè)相同資源的問題.咖啡店中的客人可能只知道有一把鑰匙,但并不知道哪間浴室可用.如果你試圖以此方式使用semaphore,你將會(huì)發(fā)現(xiàn)需要更多的狀態(tài)信息---它們通常是由不同的mutex所保護(hù)的共享資源.
正確使用semaphore是為了使信號(hào)從一項(xiàng)任務(wù)傳至另一項(xiàng)任務(wù).mutex意味著取得與釋放,使用受保護(hù)共享資源的每一次任務(wù)都是以這樣的順序進(jìn)行.相比之下,使用semaphore的任務(wù)通常不是發(fā)送信號(hào),就是進(jìn)入等待狀態(tài),不可能同時(shí)發(fā)生.
例如,任務(wù)1可能包含程序代碼,當(dāng)按下"電源"(power)按鈕時(shí),即可提出(如發(fā)送信號(hào)或增量)一個(gè)特別的semaphore; 任務(wù)2則依據(jù)相同的semaphore而用于喚醒顯示器. 在這種情況下,其中一項(xiàng)任務(wù)是信號(hào)的生產(chǎn)者,另一項(xiàng)任務(wù)是信號(hào)的消費(fèi)者.
用一個(gè)例子來做總結(jié),首先展示如何使用mutex:
/* Task 1 */
mutexWait(mutex_mens_room);
// Safely use shared resource
mutexRelease(mutex_mens_room);
/* Task 2 */
mutexWait(mutex_mens_room);
// Safely use shared resource
mutexRelease(mutex_mens_room);
相應(yīng)地,你總是采用下列方法使用semaphore:
/* Task 1 - Producer */
semPost(sem_power_btn); // Send the signal
/* Task 2 - Consumer */
semPend(sem_power_btn); // Wait for signal
重要的是,semaphores可以被interrupt service routine(ISR)中斷服務(wù)程序用來向task發(fā)送信號(hào).發(fā)送一個(gè)semaphore是一個(gè)非阻塞的RTOS行為,并且ISR安全.因?yàn)檫@種技術(shù)排除了在task級(jí)別的為了是中斷不使能而引起的錯(cuò)誤的可能性,從ISR中發(fā)出信號(hào)是一種使嵌入式軟件更加可靠的設(shè)計(jì)方式.
http://www.embedded.com/columns/guest/210605040?printable=true
http://www.eettaiwan.com/ART_8800557420_676964_NT_a22f6436.HTM
2009年3月17日
#
摘要: 前一段時(shí)間,讀symbian文檔資料,和Darwin,ICE等開源代碼,經(jīng)常碰到定時(shí)器和定時(shí)器事件.故對定時(shí)器的實(shí)現(xiàn)進(jìn)行了一些整理,作為讀書筆記,以防以后忘記.
閱讀全文
2009年3月11日
#
在以前的multi-process程序中,process之間通過共享內(nèi)存、操作系統(tǒng)提供的消息對列,命名管道等不同方式進(jìn)行數(shù)據(jù)傳遞。為了減少內(nèi)存,以及進(jìn)程切換時(shí)引發(fā)的上下文切換的開銷,現(xiàn)在的系統(tǒng)一般采用multi-thread 工作方式。
各process都有各自獨(dú)立的地址空間,并且是專有的,因此一個(gè)process不能通過指針訪問另一個(gè)process中的地址空間。而屬于同一process的各thread,它們共享同一process的地址空間,因此一個(gè)thread可以通過指針訪問另一個(gè)thread中的地址。這樣我們可以在應(yīng)用中自己定義消息隊(duì)列,并對消息隊(duì)列的訪問進(jìn)行管理,簡化程序并提高性能。
在multi-thread應(yīng)用中,我們可以使用消息對列來在不同thread之間進(jìn)行消息傳遞,并降低各thread之間的藕合。
它們之間的關(guān)系如下:
生產(chǎn)者:向消息隊(duì)列中放數(shù)據(jù)的線程。
消費(fèi)者:從消息隊(duì)列中取出數(shù)據(jù)的線程。
生產(chǎn)者 ----> 消息隊(duì)列 ----->消費(fèi)者
如果應(yīng)用規(guī)定消息隊(duì)列的最大長度。在對隊(duì)列進(jìn)行控制時(shí),應(yīng)該當(dāng)消息隊(duì)列滿時(shí),可能要讓生產(chǎn)者進(jìn)行等待,直到消息隊(duì)列中有新的位置可以放入新的消息。當(dāng)消息隊(duì)列為空時(shí),應(yīng)該讓消費(fèi)者進(jìn)行等待,知道有新的消息被加到消息隊(duì)列中。
在該模型中有兩點(diǎn)需要注意:
1.不同生產(chǎn)者,消費(fèi)者同時(shí)并發(fā)操作消息隊(duì)列時(shí),對各操作的串行化。這個(gè)問題可以通過對隊(duì)列的操作進(jìn)行加鎖來實(shí)現(xiàn)。它的實(shí)現(xiàn)可以參考另一篇隨筆《C++同步鎖管理的一種方法》。
2.在消息隊(duì)列為空或滿時(shí),應(yīng)用程序的處理。
這可以在C++中采用Monitor.在Monitor中對某種條件進(jìn)行監(jiān)控。
Monitor 對象可以采取的動(dòng)作:
(1)、等待操作 wait(long timeout = INFINITE), 缺省情況下無限等待下去。
(2)、發(fā)信號(hào)操作 signal(long count = 1), 缺省情況下激活一個(gè)正在消息隊(duì)列上進(jìn)行等代的線程。
對于Monitor的實(shí)現(xiàn),可以很簡單地用下列兩種方式實(shí)現(xiàn):
1、采用ACE中的ACE_Event, ACE_Thread_Semphore實(shí)現(xiàn)。
2、采用 ICE的Cond實(shí)現(xiàn), 在Linux上,Cond實(shí)際上是通過pthread_cond_t來實(shí)現(xiàn)的。
2009年3月9日
#
前段時(shí)間,寫了一點(diǎn)關(guān)于智能指針的東西,有讀者反映沒有代碼比較難懂.現(xiàn)給出源碼,并稍微加以解釋.
智能指針類用到的基類的定義:
template<typename T>
class HandleBase
{
public:
typedef T element_type;
T* get() const
{
return _ptr;
}
//重載->操作符,返回所指對象的指針.
T* operator->() const
{
if(!_ptr)
{
//
// We don't throw directly NullHandleException here to
// keep the code size of this method to a minimun (the
// assembly code for throwing an exception is much bigger
// than just a function call). This maximises the chances
// of inlining by compiler optimization.
//
throwNullHandleException(__FILE__, __LINE__);
}
return _ptr;
}
// 通過智能指針獲取所指對象的引用.
T& operator*() const
{
if(!_ptr)
{
//
// We don't throw directly NullHandleException here to
// keep the code size of this method to a minimun (the
// assembly code for throwing an exception is much bigger
// than just a function call). This maximises the chances
// of inlining by compiler optimization.
//
throwNullHandleException(__FILE__, __LINE__);
}
return *_ptr;
}
operator bool() const
{
return _ptr ? true : false;
}
void swap(HandleBase& other)
{
std::swap(_ptr, other._ptr);
}
T* _ptr;
private:
void throwNullHandleException(const char *, int) const;
};
......
// 智能指針類定義
template<typename T>
class Handle : public HandleBase<T>
{
public:
Handle(T* p = 0) //智能指針的構(gòu)造函數(shù)
{
this->_ptr = p;
if(this->_ptr)
{
this->_ptr->__incRef(); //在構(gòu)造函數(shù)中增加所指對象的引用計(jì)數(shù)
}
}
template<typename Y> //拷貝構(gòu)造函數(shù)
Handle(const Handle<Y>& r)
{
this->_ptr = r._ptr;
if(this->_ptr)
{
this->_ptr->__incRef(); //在構(gòu)造函數(shù)中增加所指對象的引用計(jì)數(shù)
}
}
Handle(const Handle& r) //拷貝構(gòu)造函數(shù)
{
this->_ptr = r._ptr;
if(this->_ptr)
{
this->_ptr->__incRef(); //在構(gòu)造函數(shù)中增加所指對象的引用計(jì)數(shù)
}
}
~Handle()
{
if(this->_ptr)
{
this->_ptr->__decRef(); //在析構(gòu)函數(shù)中減少所指對象的引用計(jì)數(shù)
}
}
// 重載=操作符, 要注意所有權(quán) (即,對原實(shí)例的處理).
Handle& operator=(T* p)
{
if(this->_ptr != p)
{
if(p)
{
p->__incRef(); //增加新指對象的引用計(jì)數(shù)
}
T* ptr = this->_ptr;
this->_ptr = p;
if(ptr)
{
ptr->__decRef(); //減少原來所指對象的引用計(jì)數(shù)
}
}
return *this;
}
template<typename Y>
Handle& operator=(const Handle<Y>& r)
{
if(this->_ptr != r._ptr)
{
if(r._ptr)
{
r._ptr->__incRef(); //增加新指對象的引用計(jì)數(shù)
}
T* ptr = this->_ptr;
this->_ptr = r._ptr;
if(ptr)
{
ptr->__decRef(); //減少原來所指對象的引用計(jì)數(shù)
}
}
return *this;
}
Handle& operator=(const Handle& r)
{
if(this->_ptr != r._ptr)
{
if(r._ptr)
{
r._ptr->__incRef(); //增加新指對象的引用計(jì)數(shù)
}
T* ptr = this->_ptr;
this->_ptr = r._ptr;
if(ptr)
{
ptr->__decRef(); //減少原來所指對象的引用計(jì)數(shù)
}
}
return *this;
}
跟智能指針配合使用的對象.要能夠跟指針智能配合使用,這些對象應(yīng)該是從下列類的派生類的實(shí)例.
class SimpleShared
{
public:
SimpleShared();
SimpleShared(const SimpleShared&);
virtual ~SimpleShared()
{
}
SimpleShared& operator=(const SimpleShared&)
{
return *this;
}
void __incRef()
{
assert(_ref >= 0);
++_ref;
}
void __decRef()
{
assert(_ref > 0);
if(--_ref == 0) // 如果引用計(jì)數(shù)為0,則摧毀對象本身.
{
if(!_noDelete)
{
_noDelete = true;
delete this;
}
}
}
int __getRef() const
{
return _ref;
}
void __setNoDelete(bool b)
{
_noDelete = b;
}
private:
int _ref;
bool _noDelete;
};
class Shared
{
public:
Shared();
Shared(const Shared&);
virtual ~Shared()
{
}
Shared& operator=(const Shared&)
{
return *this;
}
virtual void __incRef();
virtual void __decRef();
virtual int __getRef() const;
virtual void __setNoDelete(bool);
protected:
#if defined(_WIN32)
LONG _ref;
#elif defined(ICE_HAS_ATOMIC_FUNCTIONS)
volatile int _ref;
#else
int _ref;
Mutex _mutex;
#endif
bool _noDelete;
};