一、fcntl函數
功能:操縱文件描述符,改變已打開的文件的屬性
int fcntl(int fd, int cmd, ... /* arg */ );
cmd的取值可以如下:
複製文件描述符
F_DUPFD (long)
設置/獲取文件描述符標誌
F_GETFD (void)
F_SETFD (long)
設置/獲取文件狀態標誌
F_GETFL (void)
F_SETFL (long)
獲取/設置文件鎖
F_GETLK
F_SETLK,F_SETLKW
F_SETFL:
On Linux this command can change only the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
示例程序如下:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void set_flag(int, int);
void clr_flag(int, int);
int main(int argc, char *argv[])
{
char buf[1024] = {0};
int ret;
/*
int flags;
flags = fcntl(0, F_GETFL, 0);
if (flags == -1)
ERR_EXIT("fcntl get flag error");
ret = fcntl(0, SETFL, flags | O_NONBLOCK); //設置爲非阻塞,但不更改其他狀態
if (ret == -1)
ERR_EXIT("fcntl set flag error");
*/
set_flag(0, O_NONBLOCK);
ret = read(0, buf, 1024);
if (ret == -1)
ERR_EXIT("read error");
printf("buf=%s\n", buf);
return 0;
}
void set_flag(int fd, int flags)
{
int val;
val = fcntl(fd, F_GETFL, 0);
if (val == -1)
ERR_EXIT("fcntl get flag error");
val |= flags;
if (fcntl(fd, F_SETFL, val) < 0)
ERR_EXIT("fcntl set flag error");
}
void clr_flag(int fd, int flags)
{
int val;
val = fcntl(fd, F_GETFL, 0);
if (val == -1)
ERR_EXIT("fcntl get flag error");
val &= ~flags;
if (fcntl(fd, F_SETFL, val) < 0)
ERR_EXIT("fcntl set flag error");
}
測試如下:
二、文件鎖結構體
Unix/Linux下的文件鎖結構爲:struct flock;
#include <fcntl.h> 或 #include <sys/fcntl.h>
typedef struct flock
{
short l_type; /* lock operation type 鎖操作類型: F_RDLCK、F_WRLCK、F_UNLCK */
short l_whence; /* lock base indicator 鎖的基本指示器 */
/* SEEK_SET、SEEK_CUR、SEEK_END、SEEK_DATA、SEEK_HOLE */
off_t l_start; /* starting offset from base 鎖的起始位置 */
off_t l_len; /* lock length; len == 0 means until end of file */
int l_sysid; /* system ID running process holding lock 持有該鎖的進程的系統ID*/
pid_t l_pid; /* process ID of process holding lock 持有該鎖的進程的進程ID*/
long l_pad[4]; /* reserve area */
} flock_t;
文件鎖的類型只有兩種,一種是寫鎖也叫排他鎖,一種是讀鎖也就共享鎖,可以有多個進程各持有一個讀鎖,但只能有一個進程持有寫鎖,只有對文件有對應的讀寫權限才能施加對應的鎖類型。中間三個參數
l_whence, l_start, l_len 決定了被鎖定的文件範圍。當fcntl 函數的cmd爲F_GETLK時,flock 結構體的 l_pid 參數會返回持有寫鎖的進程id。進程退出或者文件描述符被關閉時,會釋放所有的鎖。
示例程序:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
int fd;
fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, 0664);
if (fd == -1)
ERR_EXIT("open error");
/* 只有對文件有相應的讀寫權限才能施加對應的文件鎖 */
struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK; // 排他鎖,即不允許其他進程再對其加任何類型的鎖,但讀鎖(共享鎖)允許
lock.l_whence = SEEK_SET;
lock.l_start = 0; //從文件開頭開始鎖定
lock.l_len = 0; // 文件全部內容鎖住
if (fcntl(fd, F_SETLK, &lock) == 0)
{
/* 若爲F_SETLKW,這時如果鎖已經被其他進程佔用,則此進程會阻塞直到其他進程釋放鎖*/
printf("lock success\n");
printf("press any key to unlock\n");
getchar();
lock.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &lock) == 0)
printf("unlock success\n");
else
ERR_EXIT("unlock fail");
}
else
ERR_EXIT("lock fail");
return 0; //進程退出會對所有文件解鎖
}
現在其中一個終端執行:
現在文件已經被鎖住了,而且沒有按下任何按鍵,所以卡在這裏,也還沒解鎖,接着在另一個終端再次執行同個程序:
會立即返回錯誤,因爲我們希望施加的是排他鎖,而現在前面一個進程正在佔用寫鎖還沒釋放,所以嘗試施加鎖失敗,而如果fcntl
函數的cmd 設置爲 F_SETLKW,即帶w的版本,則此進程會一直阻塞直到前面一個進程釋放了鎖。