函数原型:
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
1、fcntl的五种功能
根据操作类型cmd的不同fcntl有不同的功能:
F_DUPFD 返回新的文件描述符
F_GETFD 返回文件描述符标志
F_GETFL 返回文件状态标志
F_GETOWN 进程ID或进程组ID
All other commands 返回0
记录锁功能:
int fcntl(int fd, int cmd, struct flock *lock);
cmd = F_GETLK,测试能否建立一把锁
cmd = F_SETLK,设置锁
cmd = F_SETLKW, 阻塞设置一把锁
struct flock {
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /* 加锁的起始位置:SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 加锁的起始偏移,相对于l_whence */
off_t l_len; /* 上锁的字节数,如果为0,表示从偏移处一直到文件的末尾*/
pid_t l_pid; /* 已经占用锁的PID(只对F_GETLK 命令有效) */
/*...*/
};
2、记录锁的使用
记录锁和读写锁一样也有两种锁:共享读锁(F_RDLCK)和独占写锁(F_WRLCK)。
- 文件给定字节区间,多个进程可以有一把共享读锁,即允许多个进程以读模式访问该字节区;
- 文件给定字节区间,只能有一个进程有一把独占写锁,即只允许有一个进程已写模式访问该字节区;
- 文件给定字节区间,如果有一把或多把读锁,不能在该字节区再加写锁,同样,如果有一把写锁,不能再该字节区再加任何读写锁。
- 加锁时,该进程必须对该文件有相应的文件访问权限,即加读锁,该文件必须是读打开,加写锁时,该文件必须是写打开。
3、记录锁的继承和释放规则
- 当一个进程终止时,它所建立的记录锁将全部释放;
- 当关闭一个文件描述符时,则进程通过该文件描述符引用的该文件上的任何一把锁都将被释放。
- 由fork产生的子进程不继承父进程所设置的锁。
- 同一个进程可以重复对同一个文件区间加锁,后加的锁将覆盖前面加的锁。
- 执行exec后,新程序可以继承原执行程序的锁。
4、程序
//初始化flock
void lock_init(flock *lock, short type, short whence, off_t start, off_t len)
{
if (lock == NULL)
return;
lock->l_type = type;
lock->l_whence = whence;
lock->l_start = start;
lock->l_len = len;
}
//加读锁
int readw_lock(int fd)
{
if (fd < 0)
{
return -1;
}
struct flock lock;
lock_init(&lock, F_RDLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLKW, &lock) != 0)
{
return -1;
}
return 0;
}
//加写锁
int writew_lock(int fd)
{
if (fd < 0)
{
return -1;
}
struct flock lock;
lock_init(&lock, F_WRLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLKW, &lock) != 0)
{
return -1;
}
return 0;
}
//解锁
int unlock(int fd)
{
if (fd < 0)
{
return -1;
}
struct flock lock;
lock_init(&lock, F_UNLCK, SEEK_SET, 0, 0);
if (fcntl(fd, F_SETLKW, &lock) != 0)
{
return -1;
}
return 0;
}
//测试锁能否建立,不存在返回0,返回存在锁的进程ID。
pid_t lock_test(int fd, short type, short whence, off_t start, off_t len)
{
flock lock;
lock_init(&lock, type, whence, start, len);
if (fcntl(fd, F_GETLK, &lock) == -1)
{
return -1;
}
if(lock.l_type == F_UNLCK)
return 0;
return lock.l_pid;
}
int main()
{
int fd = open(FILE_PATH, O_RDWR | O_CREAT, FILE_MODE); //以读写的方式打开一个文件
if (fork() == 0)
{
int fd_1 = open(FILE_PATH, O_RDWR | O_CREAT, FILE_MODE);
readw_lock(fd_1); //子进程加读锁
cout<<"child get read lock..."<<endl;
sleep(3);
close(fd_1);
cout<<"close the file descriptor..."<<endl;
pause();
}
sleep(1);
writew_lock(fd); //父进程加写锁
cout<<"parent get write lock..."<<endl;
unlock(fd);
return 0;
}