Perl IO:文件鎖

来源:https://www.cnblogs.com/f-ck-need-u/archive/2019/02/27/10447881.html
-Advertisement-
Play Games

文件鎖 當多個進程或多個程式都想要修同一個文件的時候,如果不加控制,多進程或多程式將可能導致文件更新的丟失。 例如進程1和進程2都要寫入數據到a.txt中,進程1獲取到了文件句柄,進程2也獲取到了文件句柄,然後進程1寫入一段數據,進程2寫入一段數據,進程1關閉文件句柄,會將數據flush到文件中,進 ...


文件鎖

當多個進程或多個程式都想要修同一個文件的時候,如果不加控制,多進程或多程式將可能導致文件更新的丟失。

例如進程1和進程2都要寫入數據到a.txt中,進程1獲取到了文件句柄,進程2也獲取到了文件句柄,然後進程1寫入一段數據,進程2寫入一段數據,進程1關閉文件句柄,會將數據flush到文件中,進程2也關閉文件句柄,也將flush到文件中,於是進程1的數據被進程2保存的數據覆蓋了。

所以,多進程修改同一文件的時候,需要協調每個進程:

  • 保證文件在同一時間只能被一個進程修改,只有進程1修改完成之後,進程2才能獲得修改權
  • 進程1獲得了修改權,就不允許進程2去讀取這個文件的數據,因為進程2可能讀取出來的數據是進程1修改前的過期數據

這種協調方式可以通過文件鎖來實現。文件鎖分兩種,獨占鎖(寫鎖)和共用鎖(讀鎖)。當進程想要修改文件的時候,申請獨占鎖(寫鎖),當進程想要讀取文件數據的時候,申請共用鎖(讀鎖)。

獨占鎖和獨占鎖、獨占鎖和共用鎖都是互斥的。只要進程1持有了獨占鎖,進程2想要申請獨占鎖或共用鎖都將失敗(阻塞),也就保證了這一時刻只有進程1能修改文件,只有當進程1釋放了獨占鎖,進程2才能繼續申請到獨占鎖或共用鎖。但是共用鎖和共用鎖是可以共存的,這代表的是兩個進程都只是要去讀取數據,並不互相衝突。

        獨占鎖       共用鎖
獨占鎖     ×           ×
共用鎖     ×           √

文件鎖:flock和lockf

Linux上的文件鎖類型主要有兩種:flock和lockf。後者是fcntl系統調用的一個封裝。它們之間有些區別:

  • flock來自BSD,而fcntl或lockf來自POSIX,所以lockf或fcntl實現的鎖也稱為POSIX鎖
  • flock只能對整個文件加鎖,而fcntl或lockf可以對文件中的部分加鎖,即粒度更細的記錄鎖
  • flock的鎖是勸告鎖,lockf或fcntl可以實現強制鎖。所謂勸告鎖,是指只有多進程雙方都遵紀守法地使用flock鎖才有意義,某進程使用flock,但另一進程不使用flock,則flock鎖對另一進程完全無限制
  • flock鎖是附加在(關聯在)文件描述符上的,而lockf是關聯在文件實體上的。本文後面將詳細分析flock鎖在文件描述符上的現象

Perl中主要使用flock來實現文件鎖,也是本文的主要內容。

Perl的flock

flock FILEHANDLE, flags;

flock兩個參數,第一個是文件句柄,第二個是鎖標誌。

鎖標誌有4種,有數值格式的1、2、8、4,在導入Fcntl模塊的:flock後,也支持字元格式的LOCK_SHLOCK_EXLOCK_UNLOCK_NB

字元格式      數值格式      意義
-----------------------------------
LOCK_SH        1        申請共用鎖
LOCK_EX        2        申請獨占鎖
LOCK_UN        8        釋放鎖
LOCK_NB        4        非阻塞模式

獨占鎖和獨占鎖、獨占鎖和共用鎖是衝突的。所以,當進程1持有獨占鎖時,進程2想要申請獨占鎖或共用鎖預設將被阻塞。如果使用了非阻塞模式,那麼本該阻塞的過程將立即返回,而不是阻塞等待其它進程釋放鎖。非阻塞模式可以結合共用鎖或獨占鎖使用。所以,有下麵幾種方式:

use Fcntl qw(:flock);

flock $fh, LOCK_SH;    # 申請共用鎖
flock $fh, LOCK_EX;    # 申請獨占鎖
flock $fh, LOCK_UN;    # 釋放鎖
flock $fh, LOCK_SH | LOCK_NB;  # 以非阻塞的方式申請共用鎖
flock $fh, LOCK_EX | LOCK_NB;  # 以非阻塞的方式申請獨占鎖

flock在操作成功時返回true,否則返回false。例如,在申請鎖的時候,無論是否使用了非阻塞模式,只要沒申請到鎖就返回false,否則返回true,而在釋放鎖的時候,成功釋放則返回true。

例如,兩個程式(不是單程式內的兩個進程,這種情況後面分析)同時運行,其中一個程式寫a.txt文件,另一個程式讀a.txt文件,但要保證先寫完再讀。

程式1的代碼內容:

#!/usr/bin/perl

use strict;
use warnings;
use Fcntl qw(:flock);

open my $fh, '>', "a.txt"
    or die "open failed: $!";

flock $fh, LOCK_EX;
print $fh, "Hello World1\n";
print $fh, "Hello World2\n";
print $fh, "Hello World3\n";

flock $fh, LOCK_UN;

程式2的代碼內容:

#!/usr/bin/perl

use strict;
use warnings;
use Fcntl qw(:flock);

open my $fh, '<', "a.txt"
    or die "open failed: $!";

# 非阻塞的方式每秒申請一次共用鎖
# 只要沒申請成功就返回false
until(flock $fh, LOCK_SH | LOCK_NB){
    print "waiting for lock released\n";
    sleep 1;
}
while(<$fh>){
    print "readed: $_";
}

flock $fh, LOCK_UN;

fork、文件句柄、文件描述符和鎖的關係

在開始之前,先看看在Perl中的fork、文件句柄、文件描述符、flock之間的結論。

  • 文件句柄是指向文件描述符的,文件描述符是指向實體文件的(假如是實體文件的描述符的話)
  • fork只會複製文件句柄,不會複製文件描述符,而是通過複製的不同文件句柄指向同一個文件描述符而實現文件描述符共用
  • 通過引用計數的方式來計算某個文件描述符上文件句柄的數量
  • close()一次表示引用數減1,直到所有文件句柄都關閉了即引用數為0時,文件描述符才被關閉
  • flock是附在文件描述符上的,不是文件句柄也不是實體文件上的
  • flock是進程級別的,不適用於在多線程中使用它來鎖互斥
  • 所以fork後的父子進程在共用文件描述符的同時也會共用flock鎖
  • flock $fh, LOCK_UN會直接釋放文件描述符上的鎖
  • 當文件描述符被關閉時,文件描述符上的鎖也會自動釋放。所以使用close()去釋放鎖的時候,必須要保證所有文件句柄都被關閉才能關閉文件描述符從而釋放鎖
  • flock(包括加鎖和解鎖)或close()都會自動flush IO Buffer,保證多進程間獲取鎖時數據同步
  • 只要持有了某個文件描述符上的鎖,在這把鎖釋放之前,自己可以隨意更換鎖的類型,例如多次flock從EX鎖變成SH鎖

下麵是正式介紹和解釋。

在C或操作系統上的fork會複製(dup)文件描述符,使得父子進程對同一文件使用不同文件描述符。但Perl的fork只會複製文件句柄而不會複製文件描述符,父子進程的不同文件句柄會共用同一個文件描述符,並使用引用計數的方式來統計有多少個文件句柄在使用這個文件描述符

之所以複製文件句柄是因為文件句柄在Perl中是一種變數類型,在不同作用域內是互相獨立的。而文件描述符對Perl來說相對更底層一些,屬於操作系統的數據資源,對Perl來說是屬於可以共用的數據。

也就是說,如果只fork了一次,那麼父子進程的兩個文件句柄都共用同一個文件描述符,都指向這個文件描述符,這個文件描述符上的引用計數為2。當父進程close關閉了該文件描述符上的一個文件句柄,子進程需要也關閉一次才是真的關閉這個文件描述符。

不僅如此,由於文件描述符是共用的,導致加在文件描述符上的鎖(比如flock鎖)在父子進程上看上去也是共用的。儘管只在父子某一個進程上加一把鎖,但這兩個進程都將持有這把鎖。如果想要釋放這個文件描述符上的鎖,直接unlock(flock $fh, LOCK_UN)或關閉文件描述符即可

但是註意,close()關閉的只是文件描述符上的一個文件句柄引用,在文件描述符真的被關閉之前(即所有文件句柄都被關掉),鎖會一直存在於描述符上。所以,很多時候使用close去釋放時的操作(之所以使用close而非unlock類操作,是因為unlock存在race condition,多個進程可能會在釋放鎖的同時搶到那個文件的鎖),可能需要在多個進程中都執行,而使用unlock類的操作只需在父子中的任何一進程中即可釋放鎖。

例如,分析下麵的代碼中父進程三處加獨占鎖位置(1)、(2)、(3)對子進程中加共用鎖的影響。

use Fcntl qw(:flock);

open my $fh, ">", "a.log";
# (1) flock $fh, LOCK_EX;

# 這裡開始fork子進程
my $pid = fork;
# (3) flock $fh, LOCK_EX;

unless($pid){
    # 子進程
    # flock $fh, LOCK_SH;
}

# 父進程
# (2) flock $fh, LOCK_EX;

首先分析父進程在(3)處加鎖對子進程的影響。(3)是在fork後且進入子進程代碼段之前運行的,也就是說父子進程都執行了一次flock加獨占鎖,顯然只有一個進程能夠加鎖。但無論是誰加鎖了,這個描述符上的鎖對另一個進程都是共用的,也就是兩個進程都持有EX鎖,這似乎違背了我們對獨占鎖的獨占性常識,但並沒有,因為實際上文件描述符上只有一個鎖,只不過這個鎖被兩個進程中的文件句柄持有了。因為子進程也持有EX鎖,自己可以直接申請SH鎖實現自己的鎖切換,如果父進程這時還沒有關閉文件句柄或解鎖,它也將持有SH鎖。

再看父進程中加在(1)或(2)處的獨占鎖,他們其實是等價的,因為在有了子進程後,無論在哪裡加鎖,鎖(文件描述符)都是共用的,引用計數都會是2。這時子進程要獲取共用鎖是完全無需阻塞的,因為它自己就持有了獨占鎖。

也就是說,上面無論是在(1)、(2)還是(3)處加鎖,在子進程中都能隨意無阻塞換鎖,因為子進程在換鎖前已經持有了這個文件描述符上的鎖。

那麼上面的示例中,如何讓子進程申請互斥鎖的時候被阻塞?只需在子進程中打開這個文件的新文件句柄即可,它會創建一個新的文件描述符,在兩個文件描述符上申請鎖時會檢查鎖的互斥性。但是必須記住,要讓子進程能成功申請到互斥鎖,必須在父進程中unlock或者在父子進程中都close(),往往我們會忘記在子進程中也關閉文件句柄而導致文件描述符繼續存在,其上的鎖也繼續保留,從而導致子進程在該文件描述符上持有的鎖阻塞了自己去申請其它描述符的鎖

例如,下麵在子進程中打開了新的$fh1,且父子進程都使用close()來保證文件描述符的關閉、鎖的釋放。當然,也可以直接在父或子進程中使用一次flock $fh, LOCK_UN來直接釋放鎖。

use Fcntl qw(:flock);

open my $fh, ">", "a.log";
# (1) flock $fh, LOCK_EX;

# 這裡開始fork子進程
my $pid = fork;
# (3) flock $fh, LOCK_EX;

unless($pid){
    # 子進程
    open $fh1, ">", "a.log";
    close $fh;     # close(1)
    # flock $fh1, LOCK_SH;
}

# 父進程
# (2) flock $fh, LOCK_EX;
close $fh;         # close(2)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. Python爬蟲入門教程 爬取背景 2019年1月10日深夜,打開了百思不得姐APP,想了一下是否可以爬呢?不自覺的安裝到了夜神模擬器裡面。這個APP還是比較有名和有意思的。 下麵是百思不得姐的簡介 年度超好玩的搞笑內容平臺,整個互聯網能嗨翻宇宙的神級腦洞大神段子手們都在這.. 新鮮的視頻,爆 ...
  • 題意 "題目鏈接" Sol 好像搞出了一個和題解不一樣的做法(然而我考場上沒寫出來還是爆零0) 一個很顯然的思路是考慮每個最小值的貢獻。 預處理出每個數左邊第一個比他小的數,右邊第一個比他大的數。 那麼$[L_i + 1, i]$對$[i, R_i]$中的每個數都會有$a[i]$的貢獻。 我們可以抽 ...
  • pip如今已經成為了Python的一大特色,可以很方便得協助Python開發者進行包管理。本文詳細介紹了pip命令的使用方法。 ...
  • __getattribute__ 官方文檔中描述如下: 該方法可以攔截對對象屬性的所有訪問企圖,當屬性被訪問時,自動調用該方法(只適用於新式類)。因此常用於實現一些訪問某屬性時執行一段代碼的特性。 需要註意的是,正式由於它攔截對所有屬性的訪問(包括對__dict__的訪問),在使用中要十分小心地避開 ...
  • IT是全國平均薪資最高的行業,2017年全國最高,人均13點4萬每年. 但技術固然好,創業拼的還是世界觀下的創意. 蘑菇街,並夕夕,TikTok,頭條,哪個不是創意用IT技術的現實化?? 未來,大平臺是Dio絲(歐拉木大)用的,中等偏上的人還是會用個人定製...上等人我不知道 加油吧,未來引擎是服務 ...
  • 發個致富腦洞:我就在想本人雖然單身,但本人戀愛經歷很多,追女生技術十足,女朋友漂亮又賢惠.如果本人開個平臺幫人誠心介紹女朋友,男女成男女朋友經男方同意我收2.5萬(IT界平均月收入的1.5倍不到),雙方結婚我再收2.5萬,總共5萬.如果沒結婚隨時可退錢,房產證做抵押,不知道行不行啊...本著給極少數 ...
  • 觀察者模式 嗨,大家好,我們又見面了,不知道上次的籃球比賽大家是不是還意猶未盡呢,沒看到的同學可以前往https://www.cnblogs.com/MrJR/p/10441166.html觀摩哦。 今天呢,我們來看另一個Java設計模式——觀察者模式。 顧名思義,觀察者模式,那肯定要有觀察者,既然 ...
  • 定義: CDN 即內容分佈網路,(Content Delivery Netwrok) ,是構築在現有Internet上的一種先進的流量分配網路,其目的是通過在現有的Internet中增加一層新的網路架構,將網站的內容發佈到最接近用戶的網路“邊緣”,使用戶可以就近取得所需的內容,提高用戶訪問網站的相應 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...