[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