A second basic communication technique, similar in spirit to using lock files, can be implemented by using some of the standard file protection routines found in UNIX. UNIX allows the locking of records. As there is no real record structure imposed on a file, a record (which is sometimes called a segment or section) is considered to be a specified number of contiguous bytes of storage starting at an indicated location. If the starting location for the record is the beginning of a file, and the number of bytes equals the number found in the file, then the entire file is considered to be the record in question. Locking routines can be used to impose advisory or mandatory locking. In advisory locking the operating system keeps track of which processes have locked files. The processes that are using these files cooperate and access the record/file only when they determine the lock is in the appropriate state. When advisory locking is used, outlaw processes can still ignore the lock, and if permissions permit, modify the record/file. In mandatory locking the operating system will check lock information with every read and write call. The operating system will ensure that the proper lock protocol is being followed. While mandatory locking offers added security, it is at the expense of additional system overhead. Locks become mandatory if the file being locked is a plain file (not executable) and the set-group-ID is on and the group execution bit is off.
At a system level the chmod command can be used to specify a file support mandatory locking. For example, in Figure 4.5, the permissions on the data file x.dat are set to support mandatory file locking. The ls command will display the letter S in the group execution bit field of a file that supports a mandatory lock. Notice that in the example absolute mode was used with the chmod command to establish locking. The first digit of the mode value should be a 2 and the third digit a 6, 4, 2, or 0 (but not a 1).
Figure 4.5 Specifying mandatory locking with chmod .
linux$ echo hello > x.dat <-- 1 linux$ ls -l x.dat -rw-r--r-- 1 gray faculty 6 Jan 30 12:06 x.dat <-- 2 linux$ chmod 2644 x.dat <-- 3 $ ls -l x.dat -rw-r-Sr-- 1 gray faculty 6 Jan 30 12:06 x.dat
(1) Create a small text file.
(2) Default protections .
(3) Set the execution bit for the group.
The topic of record locking is expansive. We focus on one small aspect of it. We use file locking routines to place and remove an advisory lock on an entire file as a communication technique with cooperating processes.
There are several ways to set a lock. The two most common approaches are presented: the fcntl system call and the lockf library function. We begin with fcntl (Table 4.6).
Table 4.6. Summary of the fcntl System Call.
Include File(s) |
|
Manual Section |
2 |
|
Summary |
int fcntl(int fd, int cmd /* , struct flock *lock */); |
|||
Return |
Success |
Failure |
Sets errno |
|
Value returned depends upon the cmd argument passed. |
-1 |
Yes |
As its first argument the fcntl system call is passed a valid integer file descriptor of an open file. The second argument, cmd , is an integer command value that specifies the action that fcntl should take. The command values for locking are specified as defined constants in the header file that is included by the header file. The lock specific constants are shown in Table 4.7.
Table 4.7. Lock-Specific Defined Constants Used with the fcntl System Call.
Defined Constant |
Action Taken by fcntl |
---|---|
F_SETLK |
Set or remove a lock. Specific action is based on the contents of the flock structure that is passed as a third argument to fcntl . |
F_SETLKW |
Same as F_SETLK, but block (wait) if the indicated record/segment is not availablethe default is not to block. |
F_GETLK |
Return lock status information via the flock structure that is passed as the third argument to fcntl . |
The third argument for fcntl is optional for some invocations (as indicated by it being gcommented out in the function prototype). However, when working with locks, the third argument is specified and references a flock structure, which is defined as
struct flock { short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */ short int l_whence; /* Where 'l_start' is relative to. */ #ifndef __USE_FILE_OFFSET64 __off_t l_start; /* Offset where the lock begins. */ __off_t l_len; /* Size of the locked area; (0 == EOF). */ #else __off64_t l_start; /* For systems with 64 bit offset. */ __off64_t l_len; #endif __pid_t l_pid; /* PID of process holding the lock. */ };
The flock structure is used to pass information to and return information from the fcntl call. The type of lock, l_type , is indicated by using one of the defined constants shown in Table 4.8.
The l_whence , l_start , and l_len flock members are used to indicate the starting location (0, the beginning of the file; 1, the current location; and 2, the end of the file), relative offset, and size of the record (segment). If these values are set to 0, the entire file will be operated upon. The l_pid member is used to return the PID of the process that placed the lock.
Table 4.8. Defined Constants Used in the flock l_type Member.
Defined Constant |
Lock Specification |
---|---|
F_RDLCK |
Read lock |
F_WRLCK |
Write lock |
F_UNLCK |
Remove lock |
When dealing with locks, if fcntl fails to carry out an indicated command, it will return a value of -1 and set errno . Error messages associated with locking are shown in Table 4.9.
Table 4.9. fcntl Error Messages Relating to Locking.
# |
Constant |
perror Message |
Explanation |
---|---|---|---|
4 |
EINTR |
Interrupted system call |
A signal was caught during the system call. |
9 |
EBADF |
Bad file number |
fd does not reference a valid open file descriptor. |
11 |
EAGAIN |
Resource temporarily unavailable |
Lock operation is prohibited , as the file has been memory mapped by another process. |
13 |
EACCES |
Permission denied |
Lock operation prohibited by a lock held by another process. |
14 |
EFAULT |
Bad address |
*lock references an illegal address space. |
22 |
EINVAL |
Invalid argument |
|
35 |
EDEADLK |
Resource deadlock avoided |
cmd is F_SETLKW and requested lock is blocked by a lock from another process. If fcntl blocks the calling process waiting for lock to be free, deadlock would occur. |
37 |
ENOLCK |
No locks available |
System has reached the maximum number of record locks. |
Program 4.2 demonstrates the use of file locking.
Program 4.2 Using fcntl to lock a file.
File : p4.2.cxx /* Locking a file with fcntl */ #include + #include #include #include #include using namespace std; const int MAX = 5; 10 int main(int argc, char *argv[ ]) { int f_des, pass = 0; pid_t pid = getpid(); struct flock lock; // for fcntl info + if (argc < 2) { // name of file to lock missing cerr << "Usage " << *argv << " lock_file_name" << endl; return 1; } sleep(1); // don't start immediately 20 if ((f_des = open(argv[1], O_RDWR)) < 0){ perror(argv[1]); // could not access file return 2; } lock.l_type = F_WRLCK; // set a write lock + lock.l_whence = 0; // start at beginning lock.l_start = 0; // with a 0 offset lock.l_len = 0; // whole file while (fcntl(f_des, F_SETLK, &lock) < 0) { switch (errno) { 30 case EAGAIN: case EACCES: if (++pass < MAX) sleep(1); else { // run out of tries + fcntl(f_des, F_GETLK, &lock); cerr << "Process " << pid << " found file " << argv[1] << " locked by " << lock.l_pid << endl; return 3; } 40 continue; } perror("fcntl"); return 4; } + cerr << endl << "Process " << pid << " has the file" << endl; sleep(3); // fake processing cerr << "Process " << pid << " is done with the file" << endl; return 0; }
In this program the name of the file to be locked is passed on the command line. A call to sleep is placed at the start of the program to slow down the processing (for demonstration purposes only). The designated file is opened for reading and writing. In lines 24 through 27 the lock structure is assigned values that indicate a write lock is to be applied to the entire file. In the while loop that follows , a call to fcntl requests the lock be placed. If fcntl fails and errno is set to either EAGAIN or EACCES (values that indicate the lock could not be applied), the process will sleep for one second and try to apply the lock again. To be safe, the EACCES constant is grouped with EAGAIN, as in some versions of UNIX this is the value that is returned when a lock cannot be applied. If the MAX number of tries ( passes ) has been exceeded, another call to fcntl (line 35) is made to obtain information about the process that has locked the file. In this call the address of the lock structure is passed to fcntl . The PID of the locking process is displayed, and the program exits. If an error other than EAGAIN or EACCES is encountered when attempting to set the lock, perror is called, a message is displayed, and the program exits. If the process successfully obtains the lock, the process prints an informational message, sleeps three seconds (to simulate some sort of processing), and prints a second message as it terminates. When the process terminates, the system automatically removes the lock on the file. If the process were not to terminate, the process would need to set the l_type member to F_UNLCK and reissue the fcntl call to clear the lock.
If we run three copies of Program 4.2 in rapid succession, using the file x.dat as the lock file, their output will be similar to that shown in Figure 4.6.
Figure 4.6 Running multiple copies of Program 4.2locking a file.
linux$ p4.2 x.dat & p4.2 x.dat & p4.2 x.dat & <-- 1 [1] 28392 [2] 28393 [3] 28394 $ Process 28392 has the file Process 28392 is done with the file Process 28393 has the file Process 28394 found file x.dat locked by 28393 Process 28393 is done with the file [3] Exit 3 p4.2 x.dat [2] Done p4.2 x.dat [1] + Done p4.2 x.dat
(1) All three processes will use the same file :
Notice that the last process, PID 28394 in this example, is unable to place a lock on the file and returns the process ID of the process that currently has the lock on the file. The second process, PID 28393, through repeated retries (with intervening calls to sleep ) is able to lock the file once the first process is finished with it.
EXERCISEChange the F_SETLK constant in Program 4.2 to F_SETLKW. Recompile the program and rerun it as shown in Figure 4.6. What sequence of messages are produced now? Why? |
The lockf library function may also be used to apply, test, or remove a lock on an open file. Beneath the covers this library function is an alternate interface for the fcntl system call. The lockf library function is summarized in Table 4.10.
Table 4.10. Summary of the lockf Library Call
Include File(s) |
|
Manual Section |
3 |
|
Summary |
int lockf(int fd, int cmd, off_t len); |
|||
Return |
Success |
Failure |
Sets errno |
|
-1 |
Yes |
The fd argument is a file descriptor of a file that has been opened for either writing (O_WRONLY) or for reading and writing (O_RDWR). The cmd argument for lockf is similar to the cmd argument used with fcntl . The cmd value indicates the action to be taken. The action that lockf will take for each cmd value (as specified in the include file ) is summarized in Table 4.11.
Table 4.11. Defined cmd Constants.
Defined Constant |
Lock Specification |
---|---|
F_ULOCK |
Unlock a previously locked file. |
F_LOCK |
Lock a file (or a section of a file) for exclusive use if it is available. If unavailable, the lockf function will block. |
F_TLOCK |
Test and, if successful, lock a file (or section of a file) for exclusive use. An error is returned if no lock can be applied; with this option the lockf function will not block if the lock cannot be applied. |
F_TEST |
Test a file for the presence of a lock. A 0 is returned if the file is unlocked or locked by the current process. If locked by another process, -1 is returned and errno is set to EACCES. |
The len argument of lockf indicates the number of contiguous bytes to lock or unlock. A value of zero indicates the section should be from the present location to the end of the file.
If the lockf call is successful, it returns a value of 0. If the call fails, it sets errno and returns the value -1 (Table 4.12).
Table 4.12. lockf error messages.
# |
Constant |
perror Message |
Explanation |
---|---|---|---|
9 |
EBADF |
Bad file number |
fd is not a valid open file descriptor. |
11 |
EAGAIN |
Resource temporarily unavailable |
|
13 |
EACCES |
Permission denied |
Lock operation prohibited by a lock held by another process. |
22 |
EINVAL |
Invalid argument |
Invalid operation specified for fd . |
35 |
EDEADLK |
File locking deadlock |
Requested lock operation would cause a deadlock. |
37 |
ENLOCK |
No locks available |
Maximum number of system locks has been reached. |
Of the two techniques, lockf is simpler but less flexible than using fcntl . Note that when using the lockf call, the user must issue a separate lseek system call to position the file pointer to the proper location in the file prior to the call. Also, when generating parent/child process pairs, each shares the same file pointer. If locks are to be used in both processes, it is sometimes best to close and reopen the file in question so that each process has its own separate file pointer.
A final noteLinux supports a shlock command that can be used in shell scripts. The shlock command creates a lock file that contains an identifying PID.
EXERCISEWrite Exercise 4.3 using the lockf system call. Verify that your solution works. |
Programs and Processes
Processing Environment
Using Processes
Primitive Communications
Pipes
Message Queues
Semaphores
Shared Memory
Remote Procedure Calls
Sockets
Threads
Appendix A. Using Linux Manual Pages
Appendix B. UNIX Error Messages
Appendix C. RPC Syntax Diagrams
Appendix D. Profiling Programs