6. Блокиране на файлове

При интензивен обмен на данни с файлове в многозадачни операционни системи възниква въпросът за синхронизиране на операциите за четене / запис между процесите. Например, да кажем, че имаме няколко „процеса на писане“ и един „процес на четене“. Необходимо е само един процес на писане да има достъп до файла за единица време, а останалите в този момент от време, така да се каже, "висят", чакайки своя ред. Това е необходимо, например, за да не се смесват данни от няколко процеса във файла, а да следват блок след блок. Как можем да постигнем това?

Тук на помощ идва функцията flock(), която установява така нареченото "препоръчително заключване" на файл. Това означава, че блокирането на достъпа се извършва не на ниво системно ядро, а на ниво програма.

Препоръчителното блокиране може да се сравни с кръстовище, което е установило доста натоварен трафик, регулиран от светофари. Когато свети червено, някои коли спират, а други минават. По принцип всяка кола може, така да се каже, да кара срещу правилата за движение, без да чака зелен сигнал, но в този случай са възможни инциденти.

Съветващото заключване работи по абсолютно същия начин. А именно, процесите, които го използват, ще работят със споделения файл коректно, а останалите ... по някакъв начин ще работят, докато не настъпи "сблъсък".

От друга страна, "твърдото блокиране" (което не се поддържа в PHP) е като бариера: никой не може да премине, докато не бъде вдигната.

Единствената функция, която обработва ключалки в PHP, се нарича flock().

bool flock(int $f, int $operation [, int& $wouldblock])

Функцията настройва указания дескриптор на отворен файл $f на желания режим на заключваневземете текущия процес. Този режим се определя от аргумента $operation и може да бъде една от следните константи:

LOCK_SH (или 1) — споделена ключалка;

LOCK_EX (или 2) - изключително заключване;

LOCK_UN (или 3) — отключване;

LOCK_NB (или 4) - тази константа трябва да се добави към една от предишните, ако не искате програмата да "виси" на flock (), докато чака реда си, а незабавно да върне контрола.

Ако не е поискано изчакване и блокът не е бил успешно придобит, незадължителната променлива $wouldblock ще бъде зададена на true true.

При грешка функцията, както винаги, връща false, а при успех връща true.

Нека се върнем към нашия пример с процеси на писане. Всеки такъв процес копнее да бъде единственият, на когото е позволено да пише във файла в даден момент (по-точно, когато е почти готов да започне да пише). Той иска да бъде изключителен.

Оттук и името на ключалката, която процесът трябва да зададе за себе си. Като извика функцията flock($f,LOCK_EX), той може да бъде абсолютно сигурен, че всички други процеси няма да започнат да записват във файла без разрешение, докато не завърши всичките си действия и не извика flock($f, LOCK_UN) или затвори файла.

Факт е, че ако в момента нашият процес не е единственият кандидат за писане, операционната система простоняма да го освободиот "вътрешностите" на функцията flock (), т.е. няма да му позволи да продължи, докато процесът на писане не стане единственият. Моментът, когато процесът, използващ ексклузивното заключване, стане активен, също е важен, тъй като всички други процеси на писане чакат (всички в една и съща функция flock ()), когато най-накрая ще завърши работата си сфайл. След като това се случи, операционната система ще избере следващия изключителен процес и т.н.

Нека да разгледаме как в общия случай трябва да бъде организиран процес на писане, който иска да придобие изключителна ключалка за себе си:

Изключителен модел на процес на заключване

Обърнете внимание, че при отварянето на файла не използвахме деструктивния режим w (който изтрива файла, ако съществуваше), а „по-мекия“ режим a+. Това не е случайно. Съдете сами: изтриването на файл идеологически означава промяна на съдържанието му. Но не трябва да правим това, докато не получим изключителна ключалка (помнете примера със светофара)! Ето защо, ако трябва да сте сигурни, че ще изтриете съдържанието на файла всеки път, в никакъв случай не използвайте отворения режим w - използвайте + и функцията ftruncate (), описана по-горе. Например:

$f=fopen($f,"a+") или die("Не мога да отворя файл за запис!");

стадо ($f,LOCK_EX); // изчакайте, докато останем единствените

ftruncate($f,0); // изчистване на цялото съдържание на файла

Защо използваме fflush() преди да отключим файл? Много е просто: деактивирането на заключването не нулира вътрешния буфер на файла, т.е. някои промени могат да бъдат "избутани" във файла ощеследслед освобождаване на заключването. Ние не искаме това, разбира се, затова принуждаваме PHP да принуди всички промени да бъдат записани на диска.

Решихме точно половината от проблема си. Наистина, сега данните от няколко процеса на писане няма да се смесват, но какво да кажем за читателите? Ами ако процесът на четене иска да чете точно от мястото, където записва процесът на запис? В този случай той очевидно ще получи "половина" данни. Тоест данните са неверни. Как да бъдем?

Вторият (и по-добър) начин е даизползване на споделена ключалка. Процес, който придобива този вид заключване, ще бъде спрян само в един случай: когато е активен друг процес, който е придобил изключително заключване. Процесите на четене ще бъдат "на опашка" само когато процесът на запис е активиран. И е правилно. Преценете сами: защо да включите червената светлина на кръстовището, ако очевидно няма напречно движение?

Сега нека разгледаме заключването на споделен четец от гледна точка на процеса на писане. Какво трябва да направи, ако някой чете от файл, в който той се кани да пише? Очевидно трябва да изчака, докато четецът приключи. С други думи, извикването на flock($f,LOCK_EX) трябва да изчака, докато поне едно споделено заключване стане активно. Това всъщност се случва.

Модел на процеса на споделено заключване

В този пример ще се използва формуляр за поръчка, модифициран от този, който разгледахме в предишната работа.

Новата версия на формуляра е показана на фиг.

файлове

Всяка от входящите поръчки се записва в един файл.

Скриптът processorder.php за обработка на формата е даден по-долу:

Авточасти на Bob - Резултати от поръчката

Резултати от поръчката

echo $tireqty.' гуми ';

ехо $oilqty.' бутилки с масло ';

ехо $sparkqty.' запалителни свещи ';

$total = $tireqty * TIREPRICE + $oilqty * OILPRICE + $sparkqty * SPARKPRICE;

Общо за поръчката: '.$total.'

Адрес за доставка: '.$address.' ';

$outputstring = $date."\t".$tireqty." гуми \t".$oilqty." масла\t"

// Отваряне на файл за добавяне

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

До настоящетов момента заявката ви не може да бъде обработена. '