記錄鎖、信號量、互斥量區別與時間性能比較


一、記錄鎖

     1、概念

     我們首先來看記錄鎖,記錄鎖的功能是當一個進程正在讀或者修改文件的某一個部分時,它可以阻止其他進程修改同一文件區。它其實是“字節范圍鎖”,因為它鎖定的是文件中的一個區域,當然,也可能是整個文件。如下圖:

                                                  

    記錄鎖其實是不同進程間進行同步的一種鎖,它主要針對的是兩個不同的進程,而信號量和互斥量的着眼點在於多線程(可以是同一進程的,也可以是不同進程的)。

    2、fcntl記錄鎖

    提到記錄鎖,第一反應就要想到fcntl函數,通過fcntl函數實現記錄鎖的功能,fcntl在APUE的文件IO中首次提及,這里我們回顧一下它的功能:

         int  fcntl(int fd, int cmd, ... /* int arg*/);                                   返回值:若成功,依賴於cmd;若出錯,返回-1

    fcntl的五種功能:

(1).復制一個已有的描述符

(2).獲取/設置文件描述符標志

(3).獲取/設置文件狀態標志

(4).獲取/設置異步IO所有權

(5).獲取/設置記錄鎖

其他功能暫且不提,這里我們剛好用到第五個功能。在cmd參數中,分別為F_GETLK、F_SETLK、F_SETLKW三種,F_SETLKW是F_SETLK的阻塞版本。

記錄鎖中,涉及到共享讀鎖、獨占性寫鎖,這里的概念與多線程同步原語的讀寫鎖情況幾乎相同,可以對比記憶。

注:用F_GETLK測試能否建立一把鎖,然后用F_SETLK或F_SETLKW企圖建立那把鎖,這兩者不是一個原子操作。因此不能保證在這兩次fcntl調用之間不會有另一個進程插入並建立一把相同的鎖。

     3、鎖的隱含繼承和釋放規則

  (1).鎖與進程和文件兩者相關聯

      即 ① 當一個進程終止時,它建立的鎖全部釋放。

          ② 當一個描述符關閉時,則這一描述符引用的文件上的任何一把鎖都會釋放。如果執行下面四步:

fd1 = open(pathname, ...);
read_lock(fd1,...);
fd2 = dup(fd1);
close(fd2);

 

則在close(fd2)后,在fd1上設置的鎖被釋放,如果將dup換成open,以打開另一描述符上的同一文件,其效果也是一樣的。

(2).由fork產生的子進程不繼承父進程所設置的鎖。

(3).在執行exec后,新程序可以繼承原執行程序的鎖。

   4.建議性鎖和強制性鎖

   ①.建議性鎖是這樣規定的:每個使用上鎖文件的進程都要檢查是否有鎖存在,當然還得尊重已有的鎖。內核和系統總體上都堅持不使用建議性鎖,它們依靠程序員遵守這個規定。(Linux默認是采用建議性鎖)

   ②.強制性鎖是由內核執行的。當文件被上鎖來進行寫入操作時,在鎖定該文件的進程釋放該鎖之前,內核會阻止任何對該文件的讀或寫訪問,每次讀或寫訪問都得檢查鎖是否存在。

 

二、信號量和互斥量的區別

   上面我們提到了信號量和互斥量的着眼點都為多線程,那么他們的區別是什么?

1.信號量:是多線程同步用的,一個線程完成了某一動作就通過信號告訴別的線程,別的線程在進行某些動作。是Unix進程間通信的方式之一。

2.互斥量:是多線程互斥用的,比如說,一個線程占用了某一資源,那么別的線程就無法訪問,直到這個線程離開,其他線程才開始可以利用這個資源。是同一進程下的多線程的五種同步方式之一。

3.區別(來自別人的總結)

  ①.互斥量用於線程的互斥,信號量用於線程的同步,這是根本區別。也就是互斥和同步的區別。

互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。
同步:是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源

  ②.互斥量值只能為0/1,信號量值可以為非負整數。
即一個互斥量只能用於一個資源的互斥訪問,它不能實現多個資源的多線程互斥問題。信號量可以實現多個同類資源的多線程互斥和同步。當信號量為單值信號量是,也可以完成一個資源的互斥訪問。 因此,我們也可以把互斥量看成是信號量的一種特殊方式。

  ③. 互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量可以由一個線程釋放,另一個線程得到。

 

●此外,再簡單提一下信號,之前在IPC中已經有所記錄:

   信號量又叫信號燈,也是一種IPC,負責協調各個線程,以保證它們正確合理的使用公共資源。可分為“二進制信號量”、“整形信號量”、“記錄型信號量”,其中,“二進制信號量”就相當於互斥量,而“記錄型信號量”可以看成是一個計數器(之前IPC中所學)。這里,信號量被抽象為5個操作:創建create、等待wait、釋放post、試圖等待TryWait、銷毀destroy

●再提一下條件變量:

  條件變量常與互斥鎖同時使用,達到線程同步的目的。條件變量通過允許線程阻塞和等待另一個線程發送信號的方法,彌補了互斥鎖的不足。所以,條件變量+互斥量 可以實現信號量的功能,但是,信號量有計數值,每次信號量post操作都會被記錄,而條件變量在發送信號時,如果沒有線程在等待該條件變量,那么信號將丟失。此外,信號量比互斥量實現更加復雜,且開銷大,應該謹慎使用。  

 

三、記錄鎖、信號量、互斥量時間性能比較

    如果在多個進程間共享一個資源,則可使用這3種技術中的一種來協調訪問。我們可以使用映射到兩個進程地址空間中的信號量、記錄鎖或者互斥量。對這三種技術兩兩之間在時間上的差別進行比較。

   若使用信號量,則先創建一個包含一個成員的信號量集合,然后將該信號量值初始化為1。為了分配資源,以sem_op為-1調用semop。為了釋放資源,以sem_op為+1調用semop。對每個操作指定SEM_UNDO,以處理在未釋放資源條件下進程終止的情況。

   若使用記錄鎖,則先創建一個空文件,並且用該文件的第一個字節(無需存在)作為鎖字節。為了分配資源,先對該字節獲得一個寫鎖。釋放該資源時,則對該字節解鎖。記錄鎖的性質確保了當一個鎖的持有者進程終止時,內核會自動釋放該鎖。

   若使用互斥量,需要所有的進程將相同的文件映射到它們的地址空間中,並且使用pthread_process_shared開啟互斥量的進程共享屬性。為了分配資源,我們對互斥量加鎖。為了釋放鎖,我們解鎖互斥量。如果一個進程沒有釋放互斥量而終止,恢復將是非常困難的,除非我們使用魯棒互斥量(pthread_mutex_consistent)。

   下圖顯示了在Linux上,使用這3種不同技術進行鎖操作所需的時間。在每一種情況下,資源都被分配、釋放1000000次。這同時由3個不同的進程執行。

                                                    

    在Linux上,記錄鎖比信號量快,但是共享存儲中的互斥量的性能比信號量和記錄鎖都要優越。如果我們能單一資源加鎖,並且不需要信號量的其他功能,則記錄鎖比信號量要好。因為他使用起來更快,當進程終止時系統會管理遺留下來的鎖。盡管對於Linux來說,共享存儲中使用互斥量是個最快的選擇,但是我們仍然應該使用記錄鎖,除非要特別考慮性能。原因是在多個進程間共享的內存中使用互斥量來恢復一個終止的進程更難,其次,進程共享的互斥量屬性並沒有得到所有平台的支持。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM