MP(多CPU)同步原語代碼示例
----引自《現(xiàn)代體系結(jié)構(gòu)上的UNIX系統(tǒng)》
為了便于對(duì)示例的展開,我們首先假定一個(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操作的相對(duì)時(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ò)誤寫入磁盤
} | }