標籤:
進程間通訊介紹
1、幾種進程間的通訊方式
# 管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的進程間使用。進程的親緣關係通常是指父子進程關係。
# 有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係進程間的通訊。
# 訊號量( semophore ) : 訊號量是一個計數器,可以用來控制多個進程對共用資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共用資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
# 訊息佇列( message queue ) : 訊息佇列是由訊息的鏈表,存放在核心中並由訊息佇列標識符標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
# 訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。
# 共用記憶體( shared memory ) :共用記憶體就是映射一段能被其他進程所訪問的記憶體,這段共用記憶體由一個進程建立,但多個進程都可以訪問。共用記憶體是最快的 IPC 方式,它是針對其他處理序間通訊方式運行效率低而專門設計的。它往往與其他通訊機制,如訊號兩,配合使用,來實現進程間的同步和通訊。
# 通訊端( socket ) : 套解口也是一種處理序間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的進程通訊。
2、互斥鎖,是一種訊號量,常用來防止兩個進程或線程在同一時刻訪問相同的共用資源
原子性:把一個互斥量鎖定為一個原子操作,這意味著作業系統(或pthread函數庫)保證了如果一個線程鎖定了一個互斥量,沒有其他線程在同一時間可以成功鎖定這個互斥量。
唯一性:如果一個線程鎖定了一個互斥量,在它解除鎖定之前,沒有其他線程可以鎖定這個互斥量。
非繁忙等待:如果一個線程已經鎖定了一個互斥量,第二個線程又試圖去鎖定這個互斥量,則第二個線程將被掛起(不佔用任何cpu資源),直到第一個線程解除對這個互斥量的鎖定為止,第二個線程則被喚醒並繼續執行,同時鎖定這個互斥量。
2.函數說明:
需要的標頭檔:pthread.h
1)初始化互斥鎖
函數原型:int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t*mattr)
參數說明:mp 互斥鎖地址 mattr 屬性 通常預設 null
初始化互斥鎖之前,必須將其所在的記憶體清零。
如果互斥鎖已初始化,則它會處於未鎖定狀態。互斥鎖可以位於進程之間共用的記憶體中或者某個進程的專用記憶體中。
2)鎖定互斥鎖
函數原型:
intpthread_mutex_lock(pthread_mutex_t *mutex); #include pthread_mutex_t mutex; intret; ret = pthread_ mutex_lock(&mp); /* acquire the mutex */
函數說明:
當 pthread_mutex_lock() 返回時,該互斥鎖已被鎖定。調用線程是該互斥鎖的屬主。如果該互斥鎖已被另一個線程鎖定和擁有,則調用線程將阻塞,直到該互斥鎖變為可用為止。
如果互斥鎖類型為 PTHREAD_MUTEX_NORMAL,則不提供死結檢測。嘗試重新鎖定互斥鎖會導致死結。如果某個線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或未鎖定,則將產生不確定的行為。
如果互斥鎖類型為 PTHREAD_MUTEX_ERRORCHECK,則會提供錯誤檢查。如果某個線程嘗試重新鎖定的互斥鎖已經由該線程鎖定,則將返回錯誤。如果某個線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯誤。
如果互斥鎖類型為 PTHREAD_MUTEX_RECURSIVE,則該互斥鎖會保留鎖定計數這一概念。線程首次成功擷取互斥鎖時,鎖定計數會設定為 1。線程每重新鎖定該互斥鎖一次,鎖定計數就增加 1。線程每解除鎖定該互斥鎖一次,鎖定計數就減小 1。 鎖定計數達到 0 時,該互斥鎖即可供其他線程擷取。如果某個線程嘗試解除鎖定的互斥鎖不是由該線程鎖定或者未鎖定,則將返回錯誤。
如果互斥鎖類型是 PTHREAD_MUTEX_DEFAULT,則嘗試以遞迴方式鎖定該互斥鎖將產生不確定的行為。對於不是由調用線程鎖定的互斥鎖,如果嘗試解除對它的鎖定,則會產生不確定的行為。如果嘗試解除鎖定尚未鎖定的互斥鎖,則會產生不確定的行為。
傳回值:
pthread_mutex_lock() 在成功完成之後會返回零。其他任何傳回值都表示出現了錯誤。如果出現以下任一情況,該函數將失敗並返回對應的值。
EAGAIN:由於已超出了互斥鎖遞迴鎖定的最大次數,因此無法擷取該互斥鎖。
EDEADLK:當前線程已經擁有互斥鎖。
3)解除鎖定互斥鎖
函數原型:
intpthread_mutex_unlock(pthread_mutex_t *mutex); #include pthread_mutex_t mutex;int ret; ret = pthread_mutex_unlock(&mutex); /* release the mutex */
函數說明:pthread_mutex_unlock() 可釋放 mutex 引用的互斥鎖對象。互斥鎖的釋放方式取決於互斥鎖的類型屬性。如果調用 pthread_mutex_unlock() 時有多個線程被 mutex 對象阻塞,則互斥鎖變為可用時調度策略可確定擷取該互斥鎖的線程。對於 PTHREAD_MUTEX_RECURSIVE 類型的互斥鎖,當計數達到零並且調用線程不再對該互斥鎖進行任何鎖定時,該互斥鎖將變為可用。
傳回值:pthread_mutex_unlock() 在成功完成之後會返回零。
其他任何傳回值都表示出現了錯誤。如果出現以下情況,該函數將失敗並返回對應的值。
EPERM :當前線程不擁有互斥鎖。
4)使用非阻塞互斥鎖鎖定
函數原型:
intpthread_mutex_trylock(pthread_mutex_t *mutex); #include pthread_mutex_t mutex;int ret; ret = pthread_mutex_trylock(&mutex); /* try to lock the mutex */
函數說明:pthread_mutex_trylock() 是 pthread_mutex_lock() 的非阻塞版本。如果 mutex 所引用的互斥對象當前被任何線程(包括當前線程)鎖定,則將立即返回該調用。否則,該互斥鎖將處於鎖定狀態,調用線程是其屬主。
傳回值:pthread_mutex_trylock() 在成功完成之後會返回零。其他任何傳回值都表示出現了錯誤。如果出現以下任一情況,該函數將失敗並返回對應的值。
EBUSY :
由於 mutex 所指向的互斥鎖鎖定,因此無法擷取該互斥鎖。
EAGAIN:描述:
由於已超出了 mutex 的遞迴鎖定最大次數,因此無法擷取該互斥鎖。
5)銷毀互斥鎖
函數原型:
intpthread_mutex_destroy(pthread_mutex_t *mp); #include pthread_mutex_t mp; intret; ret = pthread_mutex_destroy(&mp); /* mutex is destroyed */請注意,沒有釋放用來儲存互斥鎖的空間。
傳回值:
pthread_mutex_destroy() 在成功完成之後會返回零。其他任何傳回值都表示出現了錯誤。如果出現以下任一情況,該函數將失敗並返回對應的值。
EINVAL: mp 指定的值不會引用已初始化的互斥鎖對象
4.饑餓和死結的情形
當一個互斥量已經被別的線程鎖定後,另一個線程調用pthread_mutex_lock()函數去鎖定它時,會掛起自己的線程等待這個互斥量被解鎖。可能出現以下兩種情況:
“饑餓狀態”:這個互斥量一直沒有被解鎖,等待鎖定它的線程將一直被掛著,即它請求某個資源,但永遠得不到它。使用者必須在程式中努力避免這種“饑餓”狀態出現。Pthread函數庫不會自動處理這種情況。
“死結”:一組線程中的所有線程都在等待被同組中另外一些線程佔用的資源,這時,所有線程都因等待互斥量而被掛起,它們中的任何一個都不可能恢複運行,程式無法繼續運行下去。這時就產生了死結。Pthread函數庫可以跟蹤這種情形,最後一個線程試圖調用pthread_mutex_lock()時會失敗,並傳回型別為EDEADLK的錯誤。
現實中我們有多個線程操作(讀寫)同一個檔案,訊息鏈表的增刪改查等操作,必須要通過鎖機制來實現同步
線程同步機制之互斥鎖