File and device input/output

Getting lock information

You can determine which processes, if any, are blocking a lock from being set. This can be used as a simple test or as a means to find locks on a file. A lock is set up as in the previous examples and the F_GETLK command is used in the fcntl call. If the lock passed to fcntl would be blocked, the first blocking lock is returned to the process through the structure passed to fcntl. That is, the lock data passed to fcntl is overwritten by blocking lock information. This information includes two pieces of data that have not been discussed yet, l_pid and l_sysid, that are only used by F_GETLK. (For systems that do not support a distributed architecture the value in l_sysid should be ignored.) These fields uniquely identify the process holding the lock.

If a lock passed to fcntl using the F_GETLK command would not be blocked by another process's lock, then the l_type field is changed to F_UNLCK and the remaining fields in the structure are unaffected. Let us use this capability to print all the segments locked by other processes. Note that if there are several read locks over the same segment only one of these will be found.

   struct flock lck;

/* Find and print "write lock" blocked segments of this file. */ (void) printf("sysid pid type start length\n"); lck.l_start = 0L; do { lck.l_whence = 0; lck.l_len = 0L; lck.l_type = F_WRLCK; (void) fcntl(fd, F_GETLK, &lck); if (lck.l_type != F_UNLCK) { (void) printf("%5d %5d %c %8d %8d\n", lck.l_sysid, lck.l_pid, (lck.l_type == F_WRLCK) ? 'W' : 'R', lck.l_start, lck.l_len); /* if this lock goes to the end of the address * space, no need to look further, so break out. */ if (lck.l_len == 0) break; /* otherwise, look for new lock after the one * just found. */ lck.l_start += lck.l_len; } } while (lck.l_type != F_UNLCK);

fcntl with the F_GETLK command will always return correctly (that is, it will not sleep or fail), if the values passed to it as arguments are valid.

Note that the statement:

   lck.l_whence = 0;
is not strictly necessary within the loop, since the F_GETLK command always returns l_whence set to SEEK_SET (defined as 0) for the blocking lock.

The lockf function with the F_TEST command can also be used to test if there is a process blocking a lock. This function does not, however, return the information about where the lock actually is and which process owns the lock. A routine using lockf to test for a lock on a file follows:

   /* find a blocked record. */
   /* seek to beginning of file */
   (void) lseek(fd, 0, 0L);
   /* set the size of the test region to zero (0)
    * to test until the end of the file address space.
   if (lockf(fd, F_TEST, 0L) < 0) {
   	switch (errno) {
   		case EACCES:
   		case EAGAIN:
   		(void) printf("file is locked by another process\n");
   		case EBADF:
   		/* bad argument passed to lockf */
   		(void) printf("lockf: unknown error <%d>\n", errno);

When a process forks, the child receives a copy of the file descriptors that the parent has opened. The parent and child also share a common file pointer for each file. If the parent were to seek to a point in the file, the child's file pointer would also be at that location. This feature has important implications when using record locking. The current value of the file pointer is used as the reference for the offset of the beginning of the lock, as described by l_start, when using a l_whence value of 1. If both the parent and child process set locks on the same file, there is a possibility that a lock will be set using a file pointer that was reset by the other process. This problem appears in the lockf function call as well and is a result of the /usr/group requirements for record locking. If forking is used in a record locking program, the child process should close and reopen the file if either locking method is used. This will result in the creation of a new and separate file pointer that can be manipulated without this problem occurring. Another solution is to use the fcntl system call with a l_whence value of 0 or 2. This makes the locking function atomic, so that even processes sharing file pointers can be locked without difficulty.

Next topic: Deadlock handling
Previous topic: Setting and removing record locks

© 2004 The SCO Group, Inc. All rights reserved.
UnixWare 7 Release 7.1.4 - 27 April 2004