记录一次linux信号量sem_t使用bug
linux提供了互斥量pthread_mutex_t(pthread庫) 用于線程間同步,進程間同步提供了信號量sem_t。如果把pthread_mutex_t放到共享內存中,并將其屬性設置為PTHREAD_PROCESS_SHARED,則也能實現進程間的同步,相對而言比較麻煩。一般直接使用信號量更加方便。
這里討論使用具名信號量以便在無血緣關系的進程之間做同步,主要涉及下面5個函數:
sem_open();//打開或者創建信號量 sem_wait();//獲取信號量 sem_post();//掛出信號量 sem_close();//關閉信號量 sem_unlink();//銷毀信號量其中sem_unlink()的行為比較讓人迷惑,linux內核文檔描述為:
DESCRIPTIONsem_unlink() removes the named semaphore referred to by name. The semaphore name is removed immediately. Thesemaphore is destroyed once all other processes that have the semaphore open close it.意思是執行該函數,會立即移除信號量的id名,并嘗試銷毀對應的信號量實體,只有所有打開了該信號量的進程都執行了sem_close(),執行sem_unlink()時才會真正銷毀這個信號量。問題就出現了,id名與信號量實體可能出現分離,因為當某個進程A關閉了信號量(也就是執行了sem_close)并嘗試執行sem_unlink的時候,并不能確保其他進程關閉了信號量,結果是信號量實體無法被真正銷毀,它依然在內核中存在,且其他使用該信號量實體的進程依然可以工作正常。問題是,當進程A再次打開這個信號量時(使用sem_open(),id名不變),已經無法通過id名找到之前那個信號量實體了,打開的是一個全新的信號量,雖然id名與之前一樣,內容卻已經沒有關聯了。
??????? 這樣一來,進程A就無法與其他進程同步了。只有其他進程也執行一次sem_close,并重新打開這個id名(sem_open()),才能重新連接到進程A打開的那個信號量實體,繼續同步。因此“The semaphore name is removed immediately”,應該理解為"立即解除name 與信號量實體的綁定關系",且解除之后無法重新綁定。
??????? 在使用共享內存的過程中,shmctl(id,IPC_RMID,NULL)移除共享內存,好像也有類似的問題,沒有實測過。
總結:
????????通常當一個進程退出時,需要關閉已經打開的信號量(sem_close),如果信號量屬于這個進程(在這個進程里面create),還應該執行sem_unlink;如果信號量的歸屬權不是該進程,則只應該執行關閉。
??????? 現實使用的時候,進程可能異常掛掉退出,沒有執行到sem_close這一步,甚至還沒有來得及掛出信號量(sem_post)程序就掛了,問題比較復雜,不僅可能存在數據安全,還很有可能造成死鎖。所以最好是當某個進程重啟了,其他相關進程也重啟一下,確保所有進程打開的是同一個信號量實體,可以一定程度避免信號量和共享內存連接異常的問題。
??????? 當然,最好是不要讓程序有異常掛掉的情況,應該做好異常捕獲和處理,減少程序未定義行為。
總結
以上是生活随笔為你收集整理的记录一次linux信号量sem_t使用bug的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android项目-IPTV经验总结
- 下一篇: 基于(LinuxC语言)的UDP局域网聊