Wednesday, November 5, 2014

Unix Prog: Record locking(3)

1. Implied Inheritance and release of locks
1) Locks are associated with a process and a file. When a process terminates, all its locks are released, whenever a descriptor is closed, any locks on the file referenced by that descriptor for that process are released. If one process has 2 file descriptors pointing to the same file, then close one file descriptor will release locks on both file descriptors. Because: locks are associated with process and file itself instead of file descriptors.

2) Locks are never inherited by the child across a fork. Since child process is a different process, which should not have the lock any more.

3) Locks are inherited by a new program across exec, since exec still happens on the same process. But if close-on-exec flag is set for a file descriptor, all locks for the underlying file are released when the descriptor is closed as part of an exec.

2. Free BSD Implementation
lock.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<fcntl.h>  
 #include<unistd.h>  
   
 #define read_lock(fd, offset, whence, len) \  
  lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))  
 #define write_lock(fd, offset, whence, len) \  
  lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))  
   
 int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)  
 {  
  struct flock lock;  
   
  lock.l_type = type;  
  lock.l_start = offset;  
  lock.l_whence = whence;  
  lock.l_len = len;  
   
  return(fcntl(fd, cmd, &lock));  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int fd1, fd2, fd3;  
  pid_t pid;  
   
  fd1 = open("test.txt", O_RDWR);  
  write_lock(fd1, 0, SEEK_SET, 1);  
  if((pid = fork()) > 0) { // Parent process  
   fd2 = dup(fd1);  
   fd3 = open("test.txt", O_RDWR);  
  } else if(pid == 0) {  
   read_lock(fd1, 1, SEEK_SET, 1);  
  }  
   
  exit(0);  
 }  

In above example, fd1, fd2 in parent process point to the same entry in the "kernel file table", fd3 point to another entry in "kernel file table", both entries in "kernel file table" point to the same v-node. After fork, the v-node contains two locks, write lock for parent process and read lock for child process.
Closing either one (fd1, fd2, fd3) in parent process will release the write lock for the parent process(OS will go through the lock list remove the lock entry for that process, since the lock is just related with process and file itself, not file descriptor.

3. Example:
lock.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<fcntl.h>  
   
 int lockfile(int fd)  
 {  
  struct flock fl;  
   
  fl.l_type = F_WRLCK;  
  fl.l_start = 0;  
  fl.l_whence = SEEK_SET;  
  fl.l_len = 0;  
   
  return (fcntl(fd, F_SETLK, &fl));  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int fd;  
  pid_t pid;  
   
  fd = open("test.txt", O_RDWR);  
  lockfile(fd);  
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  } else if(pid == 0) { // child process  
   if(write(fd, "ab", 2) != 2) {  
    printf("file locked. write failed.\n");  
   }  
  }  
   
  exit(0);  
 }  

shell:
Above program just lock the file at the parent process, then at child process, it can not write to that file any more.
 ubuntu@ip-172-31-23-227:~$ ./lock.out  
 ubuntu@ip-172-31-23-227:~$ file locked. write failed.  
   
   

No comments:

Post a Comment