php多程序讀寫同乙個檔案鎖的問題及flock詳解

2021-07-16 17:36:09 字數 4351 閱讀 6081

php是原生支援多程序程式設計的,可以利用pcntl_fork()在當前位置產生乙個子程序,那麼就可能存在多個程序讀寫同乙個檔案的問題,比如多程序程式讀寫同乙個日誌檔案,這樣就有必要解決讀寫同乙個檔案時加鎖的問題,php已經內建了乙個讀寫的檔案鎖方法flock,,官方的解釋是輕便的檔案諮詢鎖定,這很官方。

還是先看乙個栗子吧:

<?php 

if (flock($fp, lock_ex)) else

fclose($fp);

?>

flock的第乙個引數是乙個檔案控制代碼,第二個引數可以設定鎖定方式,有幾個常量可以設定,下面一一介紹。

lock_sh : 取得共享鎖定(讀取的程式)也就是常說的共享鎖,該程序只能讀不能寫,其他程序還是能讀取該檔案的。

lock_ex:取得獨佔鎖定(寫入的程式)常說的獨佔鎖,該程序能讀寫該檔案,其他程序則不能讀寫。

lock_un:釋放鎖定(無論共享或獨佔)也就是釋放上述兩種鎖。

lock_nb:配合lock_sh和lock_ex使用,使得在加鎖時程式非阻塞。

下面用幾個示例簡單說明下:

2018-01-29更新

php文件對於flock函式的中文翻譯很不專業,這是flock英文原文的解釋

(php 4, php 5, php 7)

flock — portable advisory file locking

翻譯過來應該是可移植的協同鎖,而中文文件中的翻譯 

輕便的檔案諮詢鎖定略顯業餘

一、協同鎖(advisory lock) 和 強制鎖 (mandatory lock)

1、協同鎖

協同鎖要求參與操作的程序之間協同合作。假設程序「a」獲得乙個write鎖,並開始向檔案中寫入內容;此時,程序「b」並沒有試圖獲取乙個鎖,它仍然可以開啟檔案並向檔案中寫入內容。在此過程中,程序「b」就是乙個非合作程序。如果程序「b」試圖獲取乙個鎖,那麼整個過程就是乙個合作的過程,從而可以保證操作的「序列化」。

只有當參與操作的程序是協同合作的時候,協同鎖才能發揮作用。協同鎖有時也被稱為「非強制」鎖。

2、強制鎖

強制鎖不需要參與操作的程序之間保持協同合作。它利用核心來查檢每個開啟、讀取、寫入操作,從而保證在呼叫這些操作時不違反檔案上的鎖規則。

而flock使用協同鎖,它要求程序都要遵守先拿鎖,後操作的約定,這樣才能實現檔案鎖的功能。

二、在介紹後續的內容之前,首先我們還要了解一下linux核心對於開啟檔案的處理機制,以下摘自《linux/unix系統程式設計手冊》一書第5.4節

從上面的介紹可以知道,複製檔案描述符(通過fork建立子程序或者dup系統呼叫)之後這些檔案描述符指向核心中的同乙個開啟檔案控制代碼,而程序每次呼叫fopen開啟乙個檔案都會在核心中維護乙個新的開啟檔案控制代碼

例如:

<?php 

//例程

$fp = fopen("demo.log", "a");

$pid = pcntl_fork();

if ($pid == 0)

else

上面例程一中先開啟乙個檔案,然後fork,相當於是複製了檔案描述符,父子程序中的檔案控制代碼指向核心中同乙個開啟檔案控制代碼。

<?php 

//例程二

$pid = pcntl_fork();

$fp = fopen("demo.log", "a");

if ($pid == 0)

else

而這個例程二是先fork,然後父子程序分別呼叫了一次fopen,這時父子程序的檔案控制代碼指向核心中的不同的開啟檔案控制代碼,雖然它們開啟的是同乙個檔案。

三、flock鎖是基於核心中開啟檔案控制代碼的

前面之所以大費周章的介紹核心開啟檔案的資料結構,正是由於flock施加的鎖是基於核心中開啟的檔案控制代碼,也就是說指向核心中同乙個開啟檔案控制代碼的檔案描述符(或檔案控制代碼)是共享乙個檔案鎖的,對其中任何乙個檔案控制代碼的加鎖操作都會反映到其他的檔案控制代碼。對於乙個已經獲得鎖的核心開啟檔案控制代碼,再次加鎖會先釋放之前的鎖,然後再次加新鎖,可以理解是更新了 一次鎖

<?php 

$fp = fopen("demo.log", "a");

if(flock($fp, lock_ex))

if(flock($fp, lock_ex))

上面這個例程雖然第一次加鎖之後沒有釋放鎖,但第二次加鎖還是會成功,這就是更新鎖的情況。

<?php 

$fp1 = fopen("demo.log", "a");

$fp2 = fopen("demo.log", "a");

if(flock($fp1, lock_ex))

if(flock($fp2, lock_ex))

這個例程開啟同乙個檔案兩次,fp1和fp2指向不同的核心開啟檔案控制代碼,fp1獲得鎖後沒有釋放,結果fp2將獲取不到鎖而一直阻塞。

<?php 

//例程四

$fp = fopen("demo.log", "a");

$pid = pcntl_fork();

if ($pid == 0)

}} elseif($pid > 0)

}

上述例程四輸出:

子程序加鎖成功

父程序加鎖成功

由於先開啟檔案然後fork,父子程序的檔案控制代碼指向同乙個核心開啟檔案控制代碼,父子程序每次加鎖都相當於在更新同乙個鎖,所以雖然子程序先拿到了鎖並且沒有釋放鎖,父程序卻仍然可以拿到鎖,這本質上還是一種更新鎖的情況,flock並沒有達到併發控制的目的。

<?php 

//例程五

$pid = pcntl_fork();

$fp = fopen("demo.log", "a");

if ($pid == 0)

}} elseif($pid > 0)

}

上述例程五中,先fork乙個子程序,然後父子程序都用fopen開啟檔案,它們的檔案控制代碼指向不同的核心開啟檔案控制代碼,所以當子程序拿到鎖後,只要不釋放鎖,那麼父程序將永遠拿不到鎖,這才是flock正確的使用場景。

四、flock的使用場景和示例**

<?php 

$pid = pcntl_fork();

$fp = fopen("log.txt", "a");

if ($pid == 0)

}else if ($pid > 0)

}

上面這個例子中建立乙個子程序,然後父子程序以追加的模式分別開啟同乙個檔案,父子程序向日誌檔案中分別迴圈寫一首詩( 這裡使用fflush每寫一句就重新整理檔案緩衝,避免緩衝影響問題的顯現),結束之後檢視日誌檔案:

葡萄美酒夜光杯,欲飲琵琶馬上催。醉臥沙場君莫笑,古來征戰幾人回。

葡萄美酒夜光杯,欲飲琵琶馬上催。醉臥沙場君莫笑,古來征戰幾人回。

葡萄美酒夜光杯,欲飲琵琶馬上催。黃河遠上白雲間,一片孤城萬仞山。羌笛何須怨楊柳,春風不度玉門關。

黃河遠上白雲間,一片孤城萬仞山。羌笛何須怨楊柳,春風不度玉門關。

黃河遠上白雲間,一片孤城萬仞山。羌笛何須怨楊柳,春風不度玉門關。

可以看到上述**的問題是一首詩還沒寫完,另一首詩就開始寫了,結果破壞了詩的完整性,如果不想兩首詩混在一起,那麼就可以使用flock在開始寫入一首詩之前加鎖,寫完之後釋放鎖。

真正的栗子來了

$pid = pcntl_fork();

$fp = fopen("log.txt", "a");

if ($pid == 0)

}}else if ($pid > 0)}}

that's it!

C C 業務 多程序同時讀寫同乙個檔案

include intflock int fd,int operation 引數說明 返回值說明 返回0表示成功,若有錯誤則返回 1,錯誤 存於errno。lock 會依引數operation所指定的方式對引數fd所指的檔案做各種鎖定或解除鎖定的動作。此函式只能鎖定整個檔案,無法鎖定檔案的某一區域。...

解決多程序或多執行緒同時讀寫同乙個檔案的問題

解決多程序或多執行緒同時讀寫同乙個檔案的問題 php是沒有多執行緒概念的,儘管如此我們仍然可以用 不完美 的方法來模擬多執行緒。簡單的說,就是佇列處理。通過對檔案進行 加鎖和解鎖 來實現。當乙個檔案被乙個使用者操作時,該檔案是被鎖定的,其他使用者只能等待,確實不夠完美,但是也可以滿足一些要求不高的應...

讀寫同乙個檔案出問題

在c primer plus 第六版中的第十三章程式設計練習第3題出現了問題。題目 編寫乙個檔案拷貝程式,提示使用者輸入文字檔案名,並以該檔名作為源檔名和輸出檔名。該程式要使用ctype.h中的toupper 函式,在寫入到輸出檔案時把所有文字轉換成大寫。使用標準的i o和文字模式。我一開始就只建立...