File locking trong linux
1. Giới thiệu
File locking
(khoá file) là cơ chế để đảm bảo việcđọc/ghi
file giữa nhiềuprocess
cùng lúc một cách an toàn.- Bài này chúng ta sẽ giới thiệu xem cơ chế
Locking
sinh ra giải quyết vấn đề gì và các ví dụ sử dụng của nó trênbash script
vàngôn ngữ C
.
2. Nếu vấn đề theo cách muôn thủa
- Ví dụ mô tả vấn đề
cập nhật xen kẽ
trong bất cứ hệ thống thông tin nào.- Đó là vấn đề cập nhật số dư tài khoản (
balance
) được lưu trong filebalance.dat
thông quan 2process
làA
vàB
, giả sử ban đầu trong tài khoản có100 củ
, thứ tự thực hiện đúng sẽ như sau:- Process A: Đọc giá trị tài khoản hiện tại, trừ đi
20 củ
và lưu kết quả vàosố dư
.
- Process A: Đọc giá trị tài khoản hiện tại, trừ đi
- Process B: Đọc giá trị tài khoản hiện tại, cộng vào
80 củ
và cũng lưu kết quả vàosố dư
.
- Process B: Đọc giá trị tài khoản hiện tại, cộng vào
- Sau khi thực hiện xong, số dư tài khoản phải là :
100 - 20 + 80 = 160 củ
. - Tuy nhiên, nếu xảy ra vấn đề
cập nhật xen kẽ
, thứ tự thực hiện có thể như sau:- Process A: Đọc giá trị
balance
hiện tại và chuẩn bị thực hiện tính toán.
- Process A: Đọc giá trị
- Process B: Đọc được cùng giá trị với Process A và cũng chuẩn bị thực hiện tính toán.
- Process A: Thực hiện trừ
20 củ
và lưu kết quả (80 củ
) vàobalance
- Process A: Thực hiện trừ
- Process B: Do không biết giá trị nó đọc lúc trước đã bị thay đổi (giá trị nó đang giữa là
100 củ
trong khi giá trị củabalance
đã là80 củ
rồi), nên nó vẫn thực hiện cộng80
củ và lưu kết quả100 + 80 = 180 củ
vàobalance
.
- Process B: Do không biết giá trị nó đọc lúc trước đã bị thay đổi (giá trị nó đang giữa là
- Kết quả là
180
là giá trị cuối cùng chứ không phải là160
như mong muốn.
- Đó là vấn đề cập nhật số dư tài khoản (
3. Hai cơ chế Locking
trong ứng dụng Linux
File locking
(ở đây là nói đến đơn vịFile
để phân biệt vớimemory locking
) là cơ chế cho để hạn chế truy cập cùng 1 file của nhiềuprocess
. Có nghĩa là chỉ cho phép mộtprocess
truy cập ở một thời điểm cụ thể nào đó, vì thế sẽ tránh đượccập nhật xen kẽ
.- Ta hẳn đều biết câu lệnh được ví như
ôm boom cảm tử
trong linux:rm -rf /
(Tuyệt đối đừng thử nếu không biết!!!!!). Nếu ta chạy nó, toà bộ hệ thống đang chạy sẽ bị xoá sạch không còn một cái gì, nên nhớ là không còn một cái gì hết. - Tại sao lại kì quặc vậy, hệ thống đang chạy mà lại còn bị xoá đi là sao. Đó chính là vì : Linux thường không tự động thực hiện lock các file đang được mở. Cho nên cho dù hệ thống đang chạy với một tá file đang được mở đi nữa, thì nó vẫn có thể bị xoá sạch sẽ.
- Thực ra, Linux có hỗ trợ 2 cơ chế lock lock (gọi là 2 loại lock cũng được), đó là:
- Cơ chế khoá cộng tác (Advisory Locking)
- Cơ chế khoá độc quyền (Mandatory Locking)
- Chúng ta sẽ xem chi tiết thêm ở bên dưới.
3.1 Cơ chế khoá cộng tác (Advisory Locking)
Nó không phải là một cách thức bắt buộc.
Nó chỉ chạy khi các
process
tham gia tự giác cộng tác bằng cáchchiếm lấy khoá
.Hay nói cách khác,
khoá cộng tác
này sẽ bị làm ngơ nếuprocess
cố tình không chiếm lấy khoá.Tiếp tục lấy ví dụ về trường hợp cập nhật
balance.dat
ở trên.Trường hợp Process B không cộng tác (hoặc không tự giác):
- Gỉa sử giá trị ban đầu của
balance
vẫn là100 củ
.
- Gỉa sử giá trị ban đầu của
- Process A: thực hiện
chiếm lấy khoá
trên filebalance.dat
và thực hiện mở file, đọc được giá trị100 củ
lên.
- Chúng ta phải hiểu rằng,
khoá
mà Process A đã chiếm không được set bởi hệ điều hành hay hệ thống file. - Vì thế, cho dù Process A đã khoá đi nữa thì Process B vẫn thoải mái đọc, ghi, thậm chí là xoá file đó thông qua
system call
. - Nếu Process B thực hiện như thế thật, thì ta nói rằng : Process B không cộng tác với Process A hay đơn giản là không tự giác.
- Process A: thực hiện
Trường hợp Process B có cộng tác (hoặc tự giác):
- Gỉa sử giá trị ban đầu của
balance
vẫn là100 củ
.
- Gỉa sử giá trị ban đầu của
- Process A: Thực hiện
chiếm lấy khoá
trên filebalance.dat
và thực hiện mở file, đọc được giá trị100 củ
lên.
- Process A: Thực hiện
- Process B: Thực hiện việc cố gắng
chiếm lấy khoá
trước đọc file.
- Vì Process A đã
locked
rồi nên Process B sẽ đợi Process A nhả ra.
- Process B: Thực hiện việc cố gắng
- Process A: Thực hiện trừ
20 củ
và lưu kết quả (80 củ
) vàobalance
.
- Rồi nó cũng thực hiện
nhả (release) lock
luôn.
- Process A: Thực hiện trừ
- Process B: Giờ nó chiếm được
lock
rồi, nó sẽ đọc file và được giá tị80 củ
.
- Rồi nó thực hiện logic của nó (cộng thêm
80 củ
) và lưu vào filebalance.dat
. - Cuối cùng nó cũng
nhả lock
để cácprocess
khác có thể dùng.
- Process B: Giờ nó chiếm được
3.2 Cơ chế khoá độc quyền (Mandatory Locking)
- Trước khi nói về
khoá độc quyền
, chúng ta hãy nhớ trong đầu là Khoá độc quyền trong Linux là không đáng tin (unreliable)- Trong tài liệu
man page
, (link) về hàm Cfcntl
có đoạn sau:
Mandatory locking
Warning: the Linux implementation of mandatory locking is unreliable. See BUGS below. Because of these bugs, and the fact that the feature is believed to be little used, since Linux 4.5, mandatory locking has been made an optional feature, governed by a configuration option (CONFIG_MANDATORY_FILE_LOCKING). This is an initial step toward removing this feature completely. Lược dịch: Implement của
mandatory locking
trongLinux
là không đáng tin. Sẽ thêm lý do ở BUGS bên dưới. Chính vì những BUG đó, từ bảnLinux 4.5
, phần implement củamandatory locking
sẽ được coi làtính năng optional
, được quyết định có trong kernel hay không bằng optionCONFIG_MANDATORY_FILE_LOCKING
. Đây là bước đầu của việc loại bỏ hoàn toàn tính năng này trong tương lai. - Trong tài liệu
- Không giống
khoá cộng tác
ở trên,khoá độc quyền
không yêu cầu bất cứ sự cộng tác nào từ phíaprocess
khác - Một khi khoá này được kích hoạt trên file nào đó, hệ điều hành sẽ không cho bất cứ
process
nào khác đọc/ghi file đó nữa. - Để sử dụng được loại khoá này trên Linux, thì hệ thống phải thoả mãn 3 yêu cầu sau:
- Phải
mount
hệ thống file vớimand
option
- Câu lệnh sẽ có dạng: `mount -o mand
- Phải
- Phải bật bit
set-group-ID
và tắt bitgroup-execute
cho những file mà ta muốnlock
.
- Câu lệnh sẽ có dạng: chmod g+s, g-x
- Phải bật bit
Kernel
phải được build với optionCONFIG_MANDATORY_FILE_LOCKING
được bật.
4. Xem danh sách lock đang được sử dụng
Sử dụng
lslock
(có sẵn trong package util-linux- Kết quả:Trong đó cột
M
sẽ cho biếtkhoá cộng tác
(giá trị = 1) haykhoá độc quyền
(giá trị = 0)
- Kết quả:Trong đó cột
Sử dụng
cat /proc/locks
- Kết quả: Ở đây, cột thứ 3 từ trái sang cho ta biết nó là
khoá cộng tác
(ADVISORY) haykhoá độc quyền
(MANDATORY)
- Kết quả: Ở đây, cột thứ 3 từ trái sang cho ta biết nó là
5. Sử dụng Locking
trong script
- Ta sẽ xem các ví dụ cho
khoá cộng tác
là chủ yếu. - Lệnh flock từ util-linux cho phép ta thực hiện
lock
file. - Để
lock
1 file cho một câu lệnh bất kì, ta sử dụng:
flock <file path > <command>
Giả sử ta làm thử với ví dụ, cập nhật số dư tài khoản:
Ta giả sử có 1 file text
balance.dat
chứa số dư hiện tại, ta có 2process
A và B được chạy bởi 2 script để thực hiện việc cập nhật tài khoản.Ta chuẩn bị 1 script là
update_balance.sh
để thực hiện việc update số dư chung cho cả A và Bupdate_balance.sh sẽ chạy script sau:
#!/bin/bash
file="balance.dat"
value=$(cat $file)
echo "Read current balance:$value"
#sleep 10 seconds to simulate business calculation
progress=10
while [[ $progress -lt 101 ]]; do
echo -n -e "\033[77DCalculating new balance..$progress%"
sleep 1
progress=$((10+progress))
done
echo ""
value=$((value+$1))
echo "Write new balance ($value) back to $file."
echo $value > "$file"
echo "Done."
- Tạo file
balance.dat
bằng lệnh:
echo "100" > balance.dat
- Script a.sh mà Process A chạy sẽ như sau:
#!/bin/bash
#-----------------------------------------
# process A: lock the file and subtract 20
# from the current balance
#-----------------------------------------
flock --verbose balance.dat ./update_balance.sh '-20'
Trường hợp 1: Process B không quan tâm đến việc
chiếm khoá
, cứ access như bình thường:- Script b.sh mà Process B chạy sẽ như sau:
#!/bin/bash #---------------------------------------- # process B: add 80 to the current balance in a # non-cooperative way #---------------------------------------- ./update_balance.sh '80'
- Khi chạy sẽ như sau:
Trường hợp 2: Process B
chiếm khoá
rồi mới access file:- Script b.sh mà Process B chạy sẽ như sau:
#!/bin/bash #---------------------------------------- # process B: add 80 to the current balance # in a cooperative way #---------------------------------------- flock --verbose balance.dat ./update_balance.sh '80'
- Khi chạy sẽ như sau: