信號量(Semaphore),有時被稱為信號燈,是在多環境下使用的一種設施,是可以用來保證兩個或多個關鍵代碼段不被並發調用。在進入一個關鍵代碼段之前,線程必須獲取一個信號量;一旦該關鍵代碼段完成了,那么該線程必須釋放信號量。其它想進入該關鍵代碼段的線程必須等待直到第一個線程釋放信號量。為了完成這個過程,需要創建一個信號量VI,然后將Acquire Semaphore VI以及Release Semaphore VI分別放置在每個關鍵代碼段的首末端。確認這些信號量VI引用的是初始創建的信號量。
1.信號:(signal)是一種處理異步事件的方式。信號時比較復雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程外,還可以發送信號給進程本身。linux除了支持unix早期的信號語義函數,還支持語義符合posix.1標准的信號函數sigaction。
2.信號量:(Semaphore)進程間通信處理同步互斥的機制。是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們能夠正確、合理的使用公共資源。
競態條件(race condition)是一個在設備或者系統試圖同時執行兩個操作的時候出現的不希望的狀況,但是由於設備和系統的自然特性,為了正確地執行,操作必須按照合適順序進行。 在計算機內存或者存儲里,如果同時發出讀寫大量數據的指令的時候競態條件可能發生,機器試圖覆蓋相同的或者就的數據,而此時舊的數據仍然在被讀取。結果可能是下面一個或者多個情況:計算機死機,出現非法操作提示並結束程序,錯誤的讀取舊的數據,或者錯誤的寫入新數據。在串行的內存和存儲訪問能防止這種情況,當讀寫命令同時發生的時候,默認是先執行讀操作的。 如果兩個或兩個以上的線程同時訪問相同的對象,或者訪問不同步的共享狀態.就會出現競態條件。
轉自:http://blog.csdn.net/yuzhoudiwang/article/details/4368279
現在最常用的進程間通信的方式有:信號,信號量,消息隊列,共享內存。
所謂進程通信,就是不同進程之間進行一些"接觸",這種接觸有簡單,也有復雜。機制不同,復雜度也不一樣。通信是一個廣義上的意義,不僅僅指傳遞一些massege。他們的使用方法是基本相同的,所以只要掌握了一種的使用方法,然后記住其他的使用方法就可以了。
1. 信號
在我學習的內容中,主要接觸了信號來實現同步的機制,據說信號也可以用來做其它的事
情,但是我還不知道做什么。
信號和信號量是不同的,他們雖然都可用來實現同步和互斥,但前者是使用信號處理器來
進行的,后者是使用P,V*作來實現的。
使用信號要先知道有哪些信號,在Linux下有31個需要記住的通用信號,據說也是system
V中最常用的那些。這里略。
1. 1信號相關函數:
#include
int sigaction(int signo, const struct sigaction *act, struct sigaction
*oact);
該函數用來為進程安裝信號處理器,struct sigaction數據是用來保存信號處理器的相
關信息。
#include
int sigemptyset(sigset_t *set);
將信號集合清空。
int sigfillset(sigset_t *set);
將信號集合設置成包含所有的信號。在對信號進行*作以前一定要對信號集進行初始化。
int sigaddset(sigset_t *set, int signo);
向信號集中加入signo對應的新信號。
int sigdelset(sigset_t *set, int signo);
從信號集中刪除signo對應的一個信號。
int sigismember(const sigset_t *set, int signo);
判斷某個信號是否在信號集中。返回1則在,0則不在。
#include
int sigprocmask(int how,const sigset_t *set, sigset_t *oset);
用來設置進程的信號屏蔽碼。信號屏蔽碼可以用來在某段時間內阻塞一些信號集中的信
號,如果信號不在信號集中,就不必討論它,因為肯定不響應,是否能生成也不肯定,我
沒有做過試驗。
1.2我所理解的使用信號機制的方法:
使用信號,主要做的事情就是信號處理器的工作,這里面是你想做的事情。就像中斷處理
函數一樣。
在使用信號以前,首先要初始化信號集,只有在信號集里面的信號才會被考慮。
有兩種方法可以初始化信號集,一種是設置空信號集,一種是將所有的信號都加到信號集
中。如果你自己想要的信號集不是這兩種,可以在初始化了以后通過添加和刪除信號進行
定制。
如果在進程執行的一段時間內不想對某些信號進行響應,則可以使用sigprocmask對當前
的信號集中的一些信號進行阻塞,稍后再執行。
當你將信號集設置完畢后,在讓他工作之前需要安裝信號處理器。安裝信號處理器可以實
現這幾個功能:
指定信號處理函數的入口;指定信號屏蔽集合;指定信號處理器的一些標志。所謂信號處
理器,就是指定了一些處理方法,關鍵在於安裝信號處理器,這是使正確的信號進行正確
的處理關鍵。在安裝的時候,一定要對特定的信號賦予正確的信號處理函數。
我不知道不同進程之間的信號處理器能否混用,但是像一個特定的進程中有多少個信號處
理器這樣的問題是不能提的。因為信號處理器是一個概念,他針對的是信號,就是說如果
你指定了一個數據結構,用它來存儲針對某個信號的處理信息,那么安裝信號處理器就是
賦予這個數據結構一些相關信息,使用信號處理器就是用這個數據結構存儲的信息來組織
一種機制當發生這個信號的時候會做一些你實現設置好的處理。但是如果區分不同進程中
對同一個信號的不同處理器?我想處理器可能只對核它所屬的進程有關的信號進行響應,
但是如果是這樣的話,那這是怎么實現的呢?
不過有一點是可以知道的,那就是每一個信號都有一個信號處理器(確定的),可以動過
安裝信號處理器來指定她的行為。信號處理器由他自己的信息存儲區域(我不知道在什么
地方),但是可以通過向sigaction類型的數據結構向信號處理器的信息存儲區域中傳遞
信息。這個數據結構由一個就可以了,因為它只是臨時傳遞數據的載體。
但是sigpromask和信號處理器里面的sigmask是不一樣的,前者是在進程當前流程設置信
號屏蔽,后者是指定在信號處理器作用時需要屏蔽掉的信號。例如,在設置某個特定信號
的信號處理器時,我們當然不能讓它的信號處理器工作了,因為還沒有設置完嗎,這是我
們可以使用sigprocmask來讓當前的流程開始阻塞該信號,當設置完信號處理器以后,再
用sigprocmask恢復被阻塞的信號。而以后再接收到該信號時,信號處理器就可以工作了。
我的想法是,同一個信號在不同的進程里可以有不同的信號處理器(一般應該有一個缺省
處理),當系統中發生一個信號時,所有能接受到的進程都可以接收到這個信號,並用他
們自己的信號處理器對這個信號做出各自的響應。
1.3如何用信號來進行進程間的同步
同步的實現主要是通過在接受信號之前掛起進程,等待相關信號。所以涉及到異步信號安
全函數的概念。
不過信號如何來實現進程間的互斥,我理解不是很多,我想信號的主要用處還是在軟中斷
處理和進程同步。
2.信號量
信號量和信號是不同的東西,仔細想想就可以理解:信號是實現約定的固定的值,而信號
量是一個變量記錄着某些特定信息。
信號量這種東西我們在*作系統課程中就已經接觸過了,這里只是再草草說幾句。信號量
分為有名和無名兩種。進程間通信用有名信號量,同一進程內部通信一般用無名信號量。
這個我不再多說。
2.1信號量相關函數
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
創建一個新的信號量組或獲取一個已經存在的信號量組。
#include
#include
#include
int semop(int semid, struct sembuf *sop, int nsops);
semop函數可以一次對一個或多個信號量進行*作。
Int semctl(int sem_id, int semnum, int cmd,/*union semun arg*/…);
該函數可以用來獲取一些信號量的使用信息或者是來對信號量進行控制。
2.2我對信號量機制的理解
對信號量的*作只有兩個:P, V。
為了在邏輯上便於組織信號量,信號量機制中有一個概念是信號量組。我們可以把一個信
號量組中創建相關的信號量,這樣邏輯上清晰也便於管理。在使用之前你同樣需要對他們
進行初始化:生成或打開信號量組,向其中生成或刪除你指定的信號量。
對信號量的*作只用兩種,他都是通過semop函數中的sops參數來指定的,如果這個參數
是一個數組的話,那么就是對多個信號量進行*作。Sops參數中的sem_op字段指明了對信
號量進行的是P*作還是V*作。你只要指定就行了,具體的*作不需要你去實現,函數中
都已經提供了。使用信號量,你得清楚信號量組id和信號量在信號量組中的位置(其實也
就是另一個id)。一個信號量必須屬於一個信號量組,否則不能被系統所使用。切記!
信號量和信號量組是不會被系統所自動清理的,所以當你的進程退出前,千萬別忘了清理
你生成的那些信號量們。
信號量既可以實現互斥,也可以實現同步,這里就不說了,*作系統課程中是有介紹的。
3.消息隊列
消息隊列是比較高級的一種進程間通信方法,因為它真的可以在進程間傳送massege,你
傳送一個"I seek you"都可以。
一個消息隊列可以被多個進程所共享(IPC就是在這個基礎上進行的);如果一個進程的
消息太多一個消息隊列放不下,也可以用多於一個的消息隊列(不過可能管理會比較復
雜)。共享消息隊列的進程所發送的消息中除了massege本身外還有一個標志,這個標志
可以指明該消息將由哪個進程或者是哪類進程接受。每一個共享消息隊列的進程針對這個
隊列也有自己的標志,可以用來聲明自己的身份。
對於系統中的每一個消息隊列,都有一個數據結構來代表它,這個數據結構是msqid_ds,
這里略去不講,在中可以看到它的原型。
3.1消息隊列相關函數
使用消息隊列之前,你要么獲得這個消息隊列,要么自己建立一個,否則是不能使用消息
隊列的(我覺得這都像是多余的話,請見諒)。當這個消息隊列不再使用時,也一定要有
一個進程來刪除消息隊列,系統是不會自動的清理消息隊列和msgid_ds的。
Int msgget(key_t key, int msgflg);
獲取一個存在的消息隊列的ID,或者是根據跟定的權限創建一個消息隊列。但是怎么樣去
刪除這個消息隊列,我還不十分清楚。
Int msgctl(int msqid, int cmd, struct msqid_ds *buf);
用來從msqid_ds中獲取很多消息隊列本身的信息。
Int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg);
用於向隊列發送消息。
Int msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int
msgflg);
從隊列中接收消息。
我這個文檔里面對消息隊列中的一些臨界情況所述不多,因為這是我的小結,而非介紹。
在GNU C庫技術中可以看到它的詳細介紹。