MP(多CPU)同步原語代碼示例
----引自《現代體系結構上的UNIX系統》
為了便于對示例的展開,我們首先假定一個場景:
內核中把檢測到的錯誤信息記錄到一個磁盤文件中的情形。出錯信息通過內存中的一個隊列來傳遞給日志進程(logging process)的。
當出現一個錯誤時,就在隊列中加入一項,并且通過調用函數log_error 通知日志進程。出錯日志進程接著把隊列中的項寫到磁盤上。
這就使得碰到錯誤的進程不必等候I/O完成或者或得為了向文件執行I/O而可能需要的任何鎖,并且避免了任何可能的上鎖次序問題。
代碼1, 采用事件計數的出錯日志通知機制
日志進程
log_error(error) |
{ | for(next=1; ; next++) {
lock(&err_queue); | await(&err_event, next);
把出錯信息加入到隊列 | lock(&err_queue);
unlock(&err_queue); | 從隊列中刪除項
advance(&err_event); | unlock(&err_queue);
} | write error to disk
| }
隊列本身由一個自旋鎖來保護。在本例中,事件計數只用于同步的目的,并不提供互斥。
在試用事件計數的時候,advance操作會永久性地改變事件計數的狀態。advance和await操作的相對時序沒有關系。
代碼2, 采用同步變量的出錯日志通知機制
日志進程
log_error(error) |
{ | for(;;){
lock(&err_queue); | lock(&err_queue);
把出錯信息加入到隊列 | if (queue_empty){
SV_SIGNAL(&err_syncvar, 0); | SV_WAIT(&err_syncvar, PRI, &err_queue);
unlock(&err_queue); | lock(&err_queue);
} | }
| 從隊列中刪除項
| unlock(&err_queue);
| 把錯誤寫入磁盤
| }
因為同步變量自身沒有保留狀態,所以當日志進程測試隊列的狀態并決定是等待一項還是從隊列中刪除一項的時候,必須占有自旋鎖。類似地,log_error在
發送信號時也必須占有自旋鎖。注,SV_WAIT將釋放自旋鎖,并且阻塞日志進程,SV_SIGNAL到后從阻塞處繼續執行。
代碼3, 采用管程的出錯日志通知機制
日志進程
log_error(error) | for(;;){
{ | mon_enter(&err_mon);
mon_enter(&err_mon); | if (queue empty)
把出錯信息加入到隊列 | mon_wait(&err_mon, NEWENTRY);
|
mon_signal(&err_mon, NEWENTRY); | 從隊列中刪除項
mon_exit(&err_mon); | mon_exit(&err_mon);
} | 把錯誤寫入磁盤
| }
代碼4, 采用信號量的出錯日志通知機制
日志進程
log_error(error) | for(;;){
{ | P(&err_sema);
lock(&err_queue); | lock(&err_queue);
把出錯信息加入到隊列 | 從隊列中刪除項
unlock(err_queue); | unlock(&err_queue);
V(&err_sema); | 把錯誤寫入磁盤
} | }