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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...