fcntl() 针对(文件)描述符提供控制。
#include
#include
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
参数 fd 是被参数 cmd 操作的描述符。针对 cmd 的值,fcntl 能够接受第三个参数 int arg。
返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD、F_GETFD、F_GETFL 以及 F_GETOWN。
F_DUPFD 返回新的文件描述符
F_GETFD 返回相应标志
F_GETFL、F_GETOWN 返回一个正的进程 ID 或负的进程组 ID 。
1、cmd 值的 F_DUPFD
F_DUPFD:返回一个如下描述的(文件)描述符:
实际上调用 dup(oldfd),等效于
fcntl(oldfd, F_DUPFD, 0);
而调用 dup2(oldfd, newfd),等效于
close(oldfd);
fcntl(oldfd, F_DUPFD, newfd);
2、cmd 值的 F_GETLK、F_SETLK 或 F_SETLKW
(1)F_GETLK
从内核获取文件锁的信息,将其保存到第三个参数,此时第三个参数为 struct flock *flockptr。我们这里是要设置文件锁,而不是获取已有文件锁的信息,我们这里用不到这个宏。
(2)F_SETLK
设置第三个参数所代表的文件锁,而且设置的是非阻塞文件锁,也就是如果加锁失败不会阻塞。也就是说加锁失败后如果不想阻塞的话,就是由 F_SETLK 宏来决定的。
此时需要用到第三个参数,struct flock *flockptr。
使用举例:
第一步:定义一个 struct flock flockptr 结构体变量(这个结构体变量就是文件锁)。
第二步:设置 flockptr 的成员,表示你想设置什么样的文件锁。
第三步:通过第三个参数,将设置好的 flockptr 的地址传递给 fcntl,设置你要的文件锁
(3)F_SETLKW
与 F_SETLK 一样,只不过设置的是阻塞文件锁,也就说加锁不成功的话就阻塞,是由F_SETLKW宏来决定的。
第三个参数:
第三个参数设置为什么视情况而定,如果 fcntl 用于实现文件锁的话,第三个参数为 struct flock *flockptr,flockptr 代表的就是文件锁。对 flockptr 的成员设置为特定的值,就可以将文件锁设置为你想要的锁。
struct flock结构体如下:
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) }
成员说明
l_type:锁类型
l_whence:加锁位置粗定位,设置同 lseek 的 whence
l_whence 这个与 lseek 函数的 whence 是一个含义,off_t lseek(int fd, off_t offset, int whence);
l_start:精定位,相对 l_whence 的偏移,与 lseek 的 offset 的含义完全一致
通过 l_whence 和 l_start 的值,就可以用来指定从文件的什么位置开始加锁,不过一般来说,我们会将 l_whence 指定为SEEK_SET,l_start 指定为 0,表示从整个文件头上开始加锁。
l_len:从 l_whence 和 l_start 所指定的起始地点算起,对文件多长的内容加锁。
如果 l_len 被设置 0,表示一直加锁到文件的末尾,如果文件长度是变化的,将自动调整加锁的末尾位置。
将 l_whence 和 l_start 设置为 SEEK_SET 和 0,然后再将 l_len 设置为 0,就表示从文件头加锁到文件末尾,其实就是对整个文件加锁。
flockptr.l_whence=SEEK_SET;
flockptr.l_start=0;
flockptr.l_len=0;
就就表示对整个文件加锁。
如果只是对文件中间的某段加锁,这只是区域加锁,加区域锁时可以给文件 n 多个的独立区域加锁。
l_pid:当前正加着锁的那个进程的 PID 。
只有当我们获取一个已存在锁的信息时,才会使用这个成员,这个成员的值不是我们设置的,是由文件锁自己设置的,我们只是获取以查看当前那个进程正加着锁。对于我们目前设置文件锁来说,这个成员用不到。
栗子
使用文件锁的互斥操作,解决父子进程向同一文件写“hello ”,“world\n”时,hello hello world相连的问题。
【file_lock.h】
#ifndef H_FILELOCK_H
#define H_FILELOCK_H
#include
#include
//非阻塞设置写锁
#define SET_WRFLCK(fd, l_whence, l_offset, l_len) \
set_filelock(fd, F_SETLK, F_WRLCK, l_whence, l_offset, l_len)
//阻塞设置写锁
#define SET_WRFLCK_W(fd, l_whence, l_offset, l_len) \
set_filelock(fd, F_SETLKW, F_WRLCK, l_whence, l_offset, l_len)
//非阻塞设置读锁
#define SET_RDFLCK(fd, l_whence, l_offset, l_len) \
set_filelock(fd, F_SETLK, F_RDLCK, l_whence, l_offset, l_len)
//阻塞设置读锁
#define SET_RDFLCK_W(fd, l_whence, l_offset, l_len) \
set_filelock(fd, F_SETLKW, F_RDLCK, l_whence, l_offset, l_len)
//解锁
#define UNLCK(fd, l_whence, l_offset, l_len) \
set_filelock(fd, F_SETLK, F_UNLCK, l_whence, l_offset, l_len)
/* 调用这个函数,即可实现阻塞加读锁/阻塞加写锁, 非阻塞加读锁/非阻塞加写锁/解锁 */
static void set_filelock(int fd, int ifwait, int l_type, int l_whence,
int l_offset, int l_len)
{
int ret = 0;
struct flock flck;
flck.l_type = l_type;
flck.l_whence = l_whence;
flck.l_start = l_offset;
flck.l_len = l_len;
ret = fcntl(fd, ifwait, &flck);
if (ret == -1) {
perror("fcntl fail");
exit(-1);
}
}
#endif
【main.c】
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "file_lock.h"
#define PARENT_CONTENT "parent process\n"
#define SUN_CONTENT "sun process\n"
void print_err(char *str, int line, int err_no)
{
printf("%d, %s: %s\n", line, str, strerror(err_no));
exit(-1);
}
int main(void)
{
int fd = 0;
int ret = 0;
fd = open("./file", O_RDWR | O_CREAT | O_TRUNC, 0664);
if (fd == -1)
print_err("./file", __LINE__, errno);
ret = fork();
if (ret > 0) {
while (1) {
SET_WRFLCK_W(fd, SEEK_SET, 0, 0);
write(fd, PARENT_CONTENT, strlen(PARENT_CONTENT));
UNLCK(fd, SEEK_SET, 0, 0);
sleep(1);
}
} else if (ret == 0) {
while (1) {
SET_WRFLCK_W(fd, SEEK_SET, 0, 0);
write(fd, SUN_CONTENT, strlen(SUN_CONTENT));
UNLCK(fd, SEEK_SET, 0, 0);
sleep(1);
}
}
return 0;
}
gcc -o main main.c
结果:
(SAW:Game Over!)