参考文章:
http://blog.mcuol.com/User/zhl168/Article/11232_1.htm
http://blog.csdn.net/bical/archive/2008/10/03/3014731.aspx
fcntl()用来操作文件描述词的一些特性。参数fd代表欲设置的文件描述词,参数cmd代表欲操作的指令。
有以下几种情况:
F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述词,并且复制参数fd的文件描述词。执行成功则返回新复制的文件描述词。请参考dup2()。F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。
F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。
F_GETFL 取得文件描述词状态旗标,此旗标为open()的参数flags。
F_SETFL 设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
F_GETLK 取得文件锁定的状态。
F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。
F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。参数lock指针为flock 结构指针,定义如下
struct flcok
{
short int l_type; /* 锁定的状态*/
short int l_whence;/*决定l_start位置*/
off_t l_start; /*锁定区域的开头位置*/
off_t l_len; /*锁定区域的大小*/
pid_t l_pid; /*锁定动作的进程*/
};
l_type 有三种状态:
F_RDLCK 建立一个供读取用的锁定
F_WRLCK 建立一个供写入用的锁定
F_UNLCK 删除之前建立的锁定
l_whence 也有三种方式:
SEEK_SET 以文件开头为锁定的起始位置。
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置。
返回值 成功则返回0,若有错误则返回-1,错误原因存于errno.
示例:
锁设置子函数:
/*
* 设置记录锁子函数lock_set.c
*
* 记录锁分为读取锁和写入锁,其中读取锁又称为共享锁,可以使多个
* 进程都能够在文件的同一部分建立读取锁。而写入锁又称为互斥锁,
* 在任何时刻只能有一个进程在文件的某个部分建立写入锁。当然,在
* 文件的同一部分不能同时建立读取锁和写入锁。
*
* fcntl的lock结构如下所示:
* struct flock {
* short l_type;
* off_t l_start;
* short l_whence;
* off_t l_len;
* pid_t l_pid;
* }
*
* 技巧:为加锁整个文件,通常的方法是将l_start说明为0,l_whence
* 说明为SEEK_SET,l_len说明为0。
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
void print_lock(struct flock lock)
{
printf(" -----------------------------/n");
if (lock.l_type == F_RDLCK) {
printf("/tl_type: F_RDLCK/n");
}
else if (lock.l_type == F_WRLCK) {
printf("/tl_type: F_WRLCK/n");
}
else if (lock.l_type == F_UNLCK) {
printf("/tl_type: F_UNLCK/n");
}
printf("/tl_start: %d/n", (int)lock.l_start);
if (lock.l_whence == SEEK_SET) {
printf("/tl_whence: SEEK_SET/n");
}
else if (lock.l_whence == SEEK_CUR) {
printf("/tl_whence: SEEK_CUR/n");
}
else if (lock.l_whence == SEEK_END) {
printf("/tl_whence: SEEK_END/n");
}
printf("/tl_len: %d/n", (int)lock.l_len);
printf(" -----------------------------/n");
}
void lock_set(int fd, int type)
{
struct flock lock;
/*赋值lock结构体,加锁整个文件*/
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
while (1) {
lock.l_type = type;
/*
* 根据不同的type来给文件加锁或解锁,
* 如果成功,则返回0,失败则返回-1。
* 举例:如果一个文件原来已经建立了互斥锁,那么再调用fcntl
* 建立锁就会失败,返回-1。
*/
if ((fcntl(fd, F_SETLK, &lock)) == 0) {
/*如果是共享锁*/
if (lock.l_type == F_RDLCK) {
printf("read only set by %d/n", getpid());
}
/*如果是互斥锁*/
else if (lock.l_type == F_WRLCK) {
printf("write lock set by %d/n", getpid());
}
else if (lock.l_type == F_UNLCK) {
printf("release lock by %d/n", getpid());
}
print_lock(lock);
return;
}
else {
/*
* 获得lock的描述,也就是将文件fd的加锁信息存入到lock结构中
* 如果成功则返回0
* 如果失败则返回-1
*/
if ((fcntl(fd, F_GETLK, &lock)) == 0) {
if (lock.l_type != F_UNLCK) {
if (lock.l_type == F_RDLCK) {
printf("read lock already set by %d/n", lock.l_pid);
}
else if (lock.l_type == F_WRLCK) {
printf("write lock already set by %d/n", lock.l_pid);
}
getchar();
}
}
else {
printf("cannot get the description of struck flock./n");
}
}
}
}
读取锁:
/*
* 测试文件读取锁
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void lock_set(int, int);
void die(char *msg)
{
perror(msg);
exit(1);
}
int open_file(void)
{
int fd;
if ((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_RDWR, 0666)) < 0) {
die("open error");
}
else {
printf("Open file: hello.c %d/n", fd);
}
return fd;
}
void close_file(int fd)
{
if (close(fd) < 0) {
die("close error");
}
else {
printf("Close file: hello.c/n");
}
}
int main(void)
{
int fd;
fd = open_file();
/*给文件加写入锁*/
lock_set(fd, F_RDLCK);
/*等待键盘任意键触发*/
getchar();
/*给文件解锁*/
lock_set(fd, F_UNLCK);
getchar();
close_file(fd);
return 0;
}
写入锁:
/*
* 测试文件写入锁
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void lock_set(int, int);
void die(char *msg)
{
perror(msg);
exit(1);
}
int open_file(void)
{
int fd;
if ((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_RDWR, 0666)) < 0) {
die("open error");
}
else {
printf("Open file: hello.c %d/n", fd);
}
return fd;
}
void close_file(int fd)
{
if (close(fd) < 0) {
die("close error");
}
else {
printf("Close file: hello.c/n");
}
}
int main(void)
{
int fd;
fd = open_file();
/*给文件加写入锁*/
lock_set(fd, F_WRLCK);
/*等待键盘任意键触发*/
getchar();
/*给文件解锁*/
lock_set(fd, F_UNLCK);
getchar();
close_file(fd);
return 0;
}
Makefile源文件:
CC = gcc
OBJS = fcntl_write fcntl_read
CFLAGS = -Wall -g
all: $(OBJS)
fcntl_write: fcntl_write.c lock_set.c
$(CC) $(CFLAGS) $^ -o $@
fcntl_read: fcntl_read.c lock_set.c
$(CC) $(CFLAGS) $^ -o $@
.PHONY: clean
clean:
rm -rf $(OBJS)
测试结果:
[armlinux@lqm fcntl]$ ./fcntl_read
Open file: hello.c 3
read only set by 12398
-----------------------------
l_type: F_RDLCK
l_start: 0
l_whence: SEEK_SET
l_len: 0
-----------------------------
release lock by 12398
-----------------------------
l_type: F_UNLCK
l_start: 0
l_whence: SEEK_SET
l_len: 0
-----------------------------
Close file: hello.c
[armlinux@lqm fcntl]$ ./fcntl_write
Open file: hello.c 3
write lock set by 12399
-----------------------------
l_type: F_WRLCK
l_start: 0
l_whence: SEEK_SET
l_len: 0
-----------------------------
release lock by 12399
-----------------------------
l_type: F_UNLCK
l_start: 0
l_whence: SEEK_SET
l_len: 0
-----------------------------
Close file: hello.c