[fcntl.h], [fcntl.c], [fcntlmain.c]
In Stevens "Advanced Programming in the Unix Environment" we see ways to use the Unix service fcntl to lock portions of a file for reading and writing in the manner stated in the Reader and Writer problem [any number of readers at a time, but writers must operate alone]. Here we have three files that adapt and use the code from Stevens:
/* fcntl.h -- Defines mutexes in terms of read/write locks on files.
* filerwlock, filerwlockCreate, filerwlockDelete,
* filerwreadlock, filerwlockUnlock
*/
typedef struct {
int fd;
int n;} filerwlock;
/* Create N read/write locks and returns the id of this cluster of locks. */
filerwlock * filerwlockCreate(char *filename, int n);
/* Delete the cluster of read/write locks associated with fl. */
int filerwlockDelete(filerwlock *fl);
/* Given the read/write lock cluster fl, lock its ith element */
int filerwreadlock(filerwlock *fl, int i);
int filerwwritelock(filerwlock *fl, int i);
/* Given the lock cluster fl, unlock its ith element */
int filerwunlock(filerwlock *fl, int i);
/* Given the lock cluster fl, it read locks all its elements */
int filerwlongreadlock(filerwlock *fl);
/* Given the lock cluster fl, it unlocks all its elements */
int filerwlongunlock(filerwlock *fl);
/* fcntl.c -- Defines mutexes in terms of read/write locks on files.
* (code is mostly from Stevens: Advanced Programming in the
* Unix environment. See from page 367 on.
* filerwlock, filerwlockCreate, filerwlockDelete,
* filerwreadlock, filerwlongreadlock, filerwlongunlock,
* filerwlockUnlock
*/
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int lock_reg(int, int, int, off_t, int, off_t);
#define read_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define readw_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len)
#define write_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define writew_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define un_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)
pid_t lock_test(int, int , off_t , int , off_t );
#define is_readlock(fd, offset, whence, len) \
lock_test(fd, F_RDLCK, offset, whence, len)
#define is_writelock(fd, offset, whence, len) \
lock_test(fd, 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; /* F_RDLCK, F_WRLCK, F_UNLCK */
lock.l_start = offset; /* byte offset relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */
return (fcntl(fd, cmd, &lock));
}
pid_t lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;
lock.l_type = type; /* F_RDLCK or F_WRLCK */
lock.l_start = offset; /* byte offset relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */
if (fcntl(fd,F_GETLK,&lock) < 0){
perror("fcntl"); exit(1);}
if (lock.l_type == F_UNLCK)
return (0); /* false, region is not locked by another process */
return (lock.l_pid); /* true, return pid of lock owner */
}
typedef struct {
int fd;
int n;} filerwlock;
/* Create N read/write locks and returns the id of this cluster of locks. */
filerwlock * filerwlockCreate(char *filename, int n) {
filerwlock *fl = (filerwlock *)malloc(sizeof(filerwlock));
if (((fl->fd) = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IWUSR)) < 0) {
perror("open");
exit(1);}
fl->n = n;
return fl;
}
/* Delete the cluster of read/write locks associated with fl. */
int filerwlockDelete(filerwlock *fl) {
if (close(fl->fd) < 0) {
perror("close");
exit(1);}
return free(fl);
}
/* Given the read/write lock cluster fl, lock its ith element */
int filerwreadlock(filerwlock *fl, int i) {
if ((i < 0) | (i >= fl->n)) {
printf("filerwlockLock needs i in range 0 .. %d\n", (fl->n)-1);
exit(0);}
readw_lock(fl->fd, i, SEEK_SET, 1);
}
int filerwwritelock(filerwlock *fl, int i) {
if ((i < 0) | (i >= fl->n)) {
printf("filerwlockLock needs i in range 0 .. %d\n", (fl->n)-1);
exit(0);}
writew_lock(fl->fd, i, SEEK_SET, 1);
}
/* Given the lock cluster fl, unlock its ith element */
int filerwunlock(filerwlock *fl, int i){
if ((i < 0) | (i >= fl->n)) {
printf("filerwlockUnlock needs i in range 0 .. %d\n", (fl->n)-1);
exit(0);}
un_lock(fl->fd, i, SEEK_SET, 1);
}
/* Given the lock cluster fl, it read locks all its elements */
int filerwlongreadlock(filerwlock *fl) {
readw_lock(fl->fd, 0, SEEK_SET, fl->n);
}
/* Given the lock cluster fl, it unlocks all its elements */
int filerwlongunlock(filerwlock *fl) {
un_lock(fl->fd, 0, SEEK_SET, fl->n);
}
/* This is just a driver to test the filerwlock objects defined in fcntl.c */
/* MAXCHILD processes are forked. They take turns in using LOCKSSIZE locks.*/
/* I compiled the program as follows */
/* cc fcntlmain.c fcntl.c -o fcntlmain */
/* and then run the image fcntlmain. */
/* Notice that after the program has run I find the file "mylock" in my */
/* directory. Not very desirable. Perhaps there is a way to avoid that? */
#include <stdio.h>
#include <sys/types.h>
#include "fcntl.h"
#define LOCKFILE "mylock"
#define LOCKSSIZE 5
#define MAXCHILD 4
void child (int self);
pid_t cldrn[4];
filerwlock *fl;
int
main(void){
int i;
fl = filerwlockCreate(LOCKFILE, LOCKSSIZE);
for (i=0;i < MAXCHILD; i++) {
if ((cldrn[i]=fork()) < 0) {
perror("fork");
exit(1);}
if (cldrn[i]==0)
child(i);
}
for (i=0; i < MAXCHILD; i++)
wait();
filerwlockDelete(fl);
exit(0);
}
void child (int self) {
int i, j;
char s[256];
for (j=0; j<8; j++) {
if (self == 0)
filerwwritelock(fl,1);
else if (self == (MAXCHILD-1))
filerwlongreadlock(fl);
else
filerwreadlock(fl,1);
printf("Child %d starts to sleep on lock %d\n", self, 1);
sleep(3);
printf("Child %d ends sleep on lock %d\n", self, 1);
if (self == (MAXCHILD-1))
filerwlongunlock(fl);
else
filerwunlock(fl,1);
sleep(1);
}
exit(0);
}
ingargiola.cis.temple.edu