fcntl函數(shù)的說(shuō)明與實(shí)例 (文件加鎖)

對(duì)文件加鎖是原子性的,可以用于進(jìn)程間文件操作的同步。在linux下,有三個(gè)函數(shù)可以對(duì)文件進(jìn)程加鎖,分別是fcntl、flock、lockf。這里只說(shuō)fcntl,它的用法也是最復(fù)雜的。
fcntl是file control的縮寫(xiě)。在linux下大部分設(shè)備都是文件,所以fcntl的功能也比較多,包括:
•Duplicating a file descriptor(復(fù)制文件描述符)
•File descriptor flags(操作close-on-exec標(biāo)志)
•File status flags(操作文件O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC標(biāo)識(shí))
•Advisory locking(建議性鎖)
•Mandatory locking(強(qiáng)制性鎖)
•Managing signals(管理信號(hào))
•Leases(租借鎖)
•File and directory change notification (dnotify)(文件和目錄更改消息)
•Changing the capacity of a pipe(改變管道大小)
這里只說(shuō)一下Advisory locking和Mandatory locking。建議性鎖是指給文件上鎖后,只在文件上設(shè)置了一個(gè)鎖的標(biāo)識(shí)。其他進(jìn)程在對(duì)這個(gè)文件進(jìn)程操作時(shí),可以檢測(cè)到鎖的存在,但這個(gè)鎖并不能阻止它對(duì)這個(gè)文件進(jìn)行操作。這就好比紅綠燈,當(dāng)亮紅燈時(shí),告訴你不要過(guò)馬路,但如果你一定要過(guò),也攔不住你。強(qiáng)制性鎖則是當(dāng)給文件上鎖后,當(dāng)其他進(jìn)程要對(duì)這個(gè)文件進(jìn)程不兼容的操作(如上了讀鎖,另一個(gè)進(jìn)程要寫(xiě)),則系統(tǒng)內(nèi)核將阻塞后來(lái)的進(jìn)程直到第一個(gè)進(jìn)程將鎖解開(kāi)。在該功能下,fcntl的函數(shù)原型為:
#include <unistd.h>
#include <fcntl.h></p> <p>int fcntl(int fd, int cmd,struct flock *plock );</p> <p>struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(F_GETLK only) */
...
};
Advisory locking共有三個(gè)操作,分別是F_GETLK、F_SETLK、F_SETLKW。其中F_GETLK用來(lái)測(cè)試鎖,注意是測(cè)試而不是獲取鎖;F_SETLK用來(lái)加鎖、解鎖;F_SETLKW功能同F(xiàn)_SETLK,只是操作變成阻塞式的。而fcntl可以用過(guò)l_whence、l_start、l_len來(lái)控制文件上鎖的區(qū)間。下面分別是上鎖、測(cè)試鎖的代碼。
/* slock.c */</p> <p>#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h></p> <p>int main()
{
struct flock _lock;</p> <p> _lock.l_type = F_WRLCK;
_lock.l_whence = SEEK_SET;
_lock.l_start = 0;
_lock.l_len = 0;</p> <p> int fd = open( "/dev/shm/test",O_CREAT|O_RDWR,S_IRWXU|S_IRGRP|S_IWGRP|S_IRWXO );
if ( fd < 0 )
{
puts( "open error" );
return 0;
}</p> <p> int ret = fcntl( fd,F_SETLK,&_lock );
if ( ret < 0 )
{
puts( "fcntl error" );
close( fd );
return 0;
}</p> <p> puts( "sleep now ..." );
sleep( 100 );
puts( "exit..." );
_lock.l_type = F_UNLCK;
_lock.l_whence = SEEK_SET;
_lock.l_start = 0;
_lock.l_len = 0;</p> <p> ret = fcntl( fd,F_SETLK,&_lock );
if ( ret < 0 )
{
puts( "unlock error" );
}</p> <p> close( fd );
}
/* glock.c */</p> <p>#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h></p> <p>int main()
{
struct flock _lock;</p> <p> _lock.l_type = F_RDLCK;
_lock.l_whence = SEEK_SET;
_lock.l_start = 0;
_lock.l_len = 0;</p> <p> int fd = open( "/dev/shm/test",O_RDWR );
if ( fd < 0 )
{
perror( "open error" );
return 0;
}</p> <p> int ret = fcntl( fd,F_GETLK,&_lock );
if ( ret < 0 )
{
perror( "fcntl error:" );
close( fd );
return 0;
}</p> <p> printf( "lock is %d\n",_lock.l_type );</p> <p> close( fd );
}
在上面的代碼中,"_lock.l_type = F_RDLCK;"表示給文件上讀共享鎖,"_lock.l_whence = SEEK_SET;"表示從文件開(kāi)頭開(kāi)始加鎖,"_lock.l_start = 0;"表示偏移l_whence多少字節(jié)開(kāi)始加鎖,"_lock.l_len = 0;"表示加鎖的字節(jié)數(shù),即長(zhǎng)度(Specifying 0 for l_len has the special meaning: lock all bytes starting at the location specified by l_whence and l_start through to the end of file, no matter how large the file grows.)。
在上面的代碼中,分別編譯為slock、glock。先運(yùn)行slock再運(yùn)行g(shù)lock:
./slock
sleep now ...
./glock
lock is 1
exit...
slock先給文件上寫(xiě)鎖,然后glock測(cè)試讀共享鎖是否能加上,測(cè)試結(jié)果是已存在一個(gè)寫(xiě)鎖(F_WRLCK,debian下定義為1)。這里需要注意的是F_GETLK是測(cè)試鎖是否能加上,如果可以,則struct flock中的l_type為F_UNLCK;如果不行,則l_type為文件當(dāng)前鎖的類(lèi)型,而l_pid為上鎖的進(jìn)程pid。故如果slock上的鎖是F_RDLCK,glock測(cè)試的鎖也是F_RDLCK,這兩個(gè)鎖是兼容的,返回的l_type類(lèi)型為F_UNLCK。即你不能通過(guò)F_GETLK來(lái)判斷文件是否上鎖,只能測(cè)試某個(gè)鎖是否能加上。
上面的是建議性鎖,如果要實(shí)現(xiàn)強(qiáng)制性鎖,則:
To make use of mandatory locks, mandatory locking must be enabled both on the filesystem that contains the file to be locked, and on the file itself. Mandatory locking is enabled on a filesystem using the "-o
mand" option to mount(8), or the MS_MANDLOCK flag for mount(2). Mandatory locking is enabled on a file by disabling group execute permission
on the file and enabling the set-group-ID permission bit (see chmod(1) and chmod(2)).
這是說(shuō),要實(shí)現(xiàn)強(qiáng)制性鎖則須將文件所在的文件系統(tǒng)用"-o mand"參數(shù)來(lái)掛載,并且使用chmod函數(shù)將文件用戶(hù)組的x權(quán)限去掉。然后用上面同樣的代碼就可以了。我第一次見(jiàn)這么奇特的函數(shù),實(shí)現(xiàn)一個(gè)功能并不是通過(guò)本身的參數(shù)控制,而是系統(tǒng)設(shè)置.....幸好我也不用強(qiáng)制性鎖。
以上是fcntl加文件鎖的簡(jiǎn)單例子。需要注意的是不同系統(tǒng)的實(shí)現(xiàn)并不一樣,宏定義也不一樣。如:
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/fcntl.h
/* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
#define F_RDLCK 1 /* shared or read lock */
#define F_UNLCK 2 /* unlock */
#define F_WRLCK 3 /* exclusive or write lock */
而在debian中,/usr/include/bits/fcntl.h
/* For posix fcntl() and `l_type' field of a `struct flock' for lockf(). */
#define F_RDLCK 0 /* Read lock. */
#define F_WRLCK 1 /* Write lock. */
#define F_UNLCK 2 /* Remove lock. */
相關(guān)文章
Fedora Linux 42 穩(wěn)定版發(fā)布: 帶來(lái)大量新功能和軟件更新
Fedora 42昨日發(fā)布,這是 Red Hat 贊助開(kāi)發(fā)的杰出前沿 Linux 發(fā)行版的最新版,包含大量新功能和軟件更新,使其成為 2025 年上半年發(fā)布的一款出色的 Linux 操作系統(tǒng)之一,內(nèi)2025-04-16如何在Linux查看硬盤(pán)信息? 查看Linux硬盤(pán)大小類(lèi)型和硬件信息的5種方法
使用Linux系統(tǒng)的過(guò)程中,查看和了解硬盤(pán)信息是非常重要的工作,尤其是對(duì)于系統(tǒng)管理員而言,那么在Linux系統(tǒng)中如何查看硬盤(pán)信息?以下是具體內(nèi)容介紹2025-03-12如何在 Linux 中查看 CPU 詳細(xì)信息? 3招輕松查看CPU型號(hào)、核心數(shù)和溫度
在日常運(yùn)維工作中,獲取 CPU 信息是系統(tǒng)運(yùn)維管理員常見(jiàn)的工作內(nèi)容,無(wú)論是為了性能調(diào)優(yōu)、硬件升級(jí)還是僅僅滿(mǎn)足好奇心2025-03-11什么是 Arch Linux? 獨(dú)樹(shù)一幟的Arch Linux發(fā)行版分析
Arch Linux是為簡(jiǎn)化,優(yōu)化,現(xiàn)代化,實(shí)用主義,用戶(hù)中心和多功能性而創(chuàng)建Linux發(fā)行版,究竟是什么讓 Arch 與眾不同?下面我們就來(lái)簡(jiǎn)要解讀2025-02-19如何在Linux環(huán)境下制作 Win11裝機(jī)U盤(pán)?
一直用的linux辦公,想要將筆記本電腦從 Linux 系統(tǒng)切換回 Windows 11,我們可以制作一個(gè)win11裝機(jī)u盤(pán),詳細(xì)如下2025-02-17Rsnapshot怎么用? 基于Rsync的強(qiáng)大Linux備份工具使用指南
Rsnapshot 不僅可以備份本地文件,還能通過(guò) SSH 備份遠(yuǎn)程文件,接下來(lái)詳細(xì)介紹如何安裝、配置和使用 Rsnapshot,包括創(chuàng)建每小時(shí)、每天、每周和每月的本地備份,以及如何進(jìn)2025-02-06Linux Kernel 6.13發(fā)布:附更新內(nèi)容及新特性解讀
Linux 內(nèi)核 6.13 正式發(fā)布,新版本引入了惰性搶占支持,簡(jiǎn)化內(nèi)核搶占邏輯,通過(guò)減少與調(diào)度器相關(guān)的調(diào)用次數(shù),讓內(nèi)核在運(yùn)行時(shí)表現(xiàn)更優(yōu),從而提高效率2025-01-23五大特性引領(lǐng)創(chuàng)新! 深度操作系統(tǒng) deepin 25 Preview預(yù)覽版發(fā)布
今日,深度操作系統(tǒng)正式推出deepin 25 Preview版本,該版本集成了五大核心特性:磐石系統(tǒng)、全新DDE、Treeland窗口合成器、AI For OS以及Distrobox子系統(tǒng)2025-01-18Linux Mint Xia 22.1重磅發(fā)布: 重要更新一覽
Beta 版 Linux Mint“Xia” 22.1 發(fā)布,新版本基于 Ubuntu 24.04,內(nèi)核版本為 Linux 6.8,這次更新帶來(lái)了諸多優(yōu)化和改進(jìn),進(jìn)一步鞏固了 Mint 在 Linux 桌面操作系統(tǒng)領(lǐng)域的2025-01-16LinuxMint怎么安裝? Linux Mint22下載安裝圖文教程
Linux Mint22發(fā)布以后,有很多新功能,很多朋友想要下載并安裝,該怎么操作呢?下面我們就來(lái)看看詳細(xì)安裝指南2025-01-16