File Locks


http://www.gnu.org/software/libtool/manual/libc/File-Locks.html
File Locks

The remaining fcntl commands are used to support record locking, which permits multiple cooperating programs to prevent each other from simultaneously accessing parts of a file in error-prone ways.

An exclusive or write lock gives a process exclusive access for writing to the specified part of the file. While a write lock is in place, no other process can lock that part of the file.

A shared or read lock prohibits any other process from requesting a write lock on the specified part of the file. However, other processes can request read locks.

The read and write functions do not actually check to see whether there are any locks in place. If you want to implement a locking protocol for a file shared by multiple processes, your application must do explicit fcntl calls to request and clear locks at the appropriate points. (the struct file does not contain the struct flock)

Locks are associated with processes. A process can only have one kind of lock set for each byte of a given file. When any file descriptor for that file is closed by the process, all of the locks that process holds on that file are released, even if the locks were made using other descriptors that remain open. Likewise, locks are released when a process exits, and are not inherited by child processes created using fork (see Creating a Process).

(只要有一个fd close掉了,尽管还有其他引用同一个file struct的fd处于open,所有的锁全部释放!!!)

F_SETLK、F_SETLKW(wait if cannot accquired)
F_GETLK:
On input to this call, lock describes a lock we would like to place  on  the
file.   If the lock could be placed, fcntl() does not actually place it, but
returns F_UNLCK in the l_type field of lock and leaves the other  fields  of
the  structure  unchanged.   If one or more incompatible locks would prevent
this lock being placed, then fcntl() returns  details  about  one  of  these
locks  in  the  l_type, l_whence, l_start, and l_len fields of lock and sets
l_pid to be the PID of the process holding that lock.

F_UNLCK(unlock)


As well as being removed by an explicit F_UNLCK,  record  locks  are  automatically
released  when the process terminates or if it closes any file descriptor referring
to a file on which locks are held.  This is bad: it means that a process  can  lose
the  locks  on  a file like /etc/passwd or /etc/mtab when for some reason a library
function decides to open, read and close it.

Record locks are not inherited by a child created via fork(2),  but  are  preserved
across an execve(2).

Because of the buffering performed by the stdio(3) library, the use of record lock‐
ing with routines in that package should  be  avoided;  use  read(2)  and  write(2)
instead.



As an example of a situation where file locking is useful, consider a program that can be run simultaneously by several different users, that logs status information to a common file. One example of such a program might be a game that uses a file to keep track of high scores. Another example might be a program that records usage or accounting information for billing purposes.

Having multiple copies of the program simultaneously writing to the file could cause the contents of the file to become mixed up. But you can prevent this kind of problem by setting a write lock on the file before actually writing to the file.

If the program also needs to read the file and wants to make sure that the contents of the file are in a consistent state, then it can also use a read lock. While the read lock is set, no other process can lock that part of the file for writing.

Remember that file locks are only a voluntary protocol for controlling access to a file. There is still potential for access to the file by programs that don’t use the lock protocol. (ie, you can still read/write anything you like from/to this file using an editor like vim, it’s up to you to control access using the flock protocol !!!)



example code

// flock.c
// gcc -o flock flock.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
char buf[] = "123456789012345678901234567890";
char buf2[] = "abcdefghijklmnopqrstuvwxyz";
int fd, fd2;
struct flock lock;

lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 2;
lock.l_len = 8;

fd = open("/tmp/lock", O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
if(fd == -1)
    fprintf(stderr, "fail to open filen");
if(-1 == fcntl(fd, F_SETLK, &lock))
    fprintf(stderr, "fail to fcntl filen");
if(-1 == write(fd, buf, sizeof(buf)-1))
    fprintf(stderr, "fail to write filen");

fd2 = dup(fd);
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 1;
lock.l_len = 5;
if(-1 == fcntl(fd2, F_SETLK, &lock)) // no error, fcntl success after dup
        fprintf(stderr, "fail to fcntl file dupn");
printf("Okn");

getchar(); // now run flock2'<br />// 标记(1)<br /> close(fd2); // now all locks are released !!!&#160; 如果这里没有close(fd2),后面的fcntl依然可以成功,可见file lock是针对进程互斥的<br />getchar(); // now runflock2’ again

fd2 = open("/tmp/lock", O_RDWR);
if(fd2 == -1)
    fprintf(stderr, "fail to open file 2n");
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 4;
lock.l_len = 8;
if(-1 == fcntl(fd2, F_SETLK, &lock))     // no error!!! fcntl success after open the same file the second time
        fprintf(stderr, "fail to fcntl file 2n");
    if(-1 == write(fd2, buf2, sizeof(buf2)-1))
        fprintf(stderr, "fail to write file 2n");

//    sleep(1000);
getchar();  // now run flock2 again
close(fd);
close(fd2);
}

// flock2.c
// gcc -o flock2 flock2.c

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
int fd;
struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 2;
lock.l_len = 8;

fd = open("/tmp/lock", O_RDWR);
if(fd == -1)
printf("fail to openn");
if(-1 == fcntl(fd, F_GETLK, &lock))
printf("fail to fcntln");
printf("pid : %dn", lock.l_pid);
printf("l_start:%d, l_len:%dn", lock.l_start, lock.l_len);
}


Output:
jfo@lab:~/test$ ./flock2
pid : 1921
l_start:1, l_len:9

// now flock' enter<br />jfo@lab:~/test$ ./flock2<br />pid : 0<br />l_start:2, l_len:8<br /><br />// nowflock’ enter again
jfo@lab:~/test$ ./flock2
pid : 1921
l_start:4, l_len:8

// now `flock’ enter again and will exit
jfo@lab:~/test$ ./flock2
pid : 0
l_start:2, l_len:8



前面提到,在标记(1)处如果没有close(fd2);则最后一次enter之前的输出如下:Output:
jfo@lab:~/test$ ./flock2
pid : 16530
l_start:1, l_len:11

说明lock合并了。




end