When using the fcntl to do the record locking, the operating system will automatically detect the deadlock when iterating all struct flock in vnode. If it detect one deadlock, fcntl will return error(<0).
lock.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<signal.h>
#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))
volatile sig_atomic_t sigflag;
sigset_t newmask, oldmask, zeromask;
void sig_usr(int signo)
{
sigflag = 1;
}
void TELL_WAIT(void)
{
// Setup the signal handler
if(signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("signal error!\n");
exit(3);
}
if(signal(SIGUSR2, sig_usr) == SIG_ERR) {
printf("signal error!\n");
exit(3);
}
// Make signal mask
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
// Block the SIGUSR1, SIGUSR2
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
printf("sigprocmask error!\n");
exit(4);
}
}
void TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2);
}
void WAIT_PARENT(void)
{
// Child Process wait for the SIGUSR1 from parent
while(sigflag == 0)
sigsuspend(&zeromask);
sigflag = 0;
// After receiving the SIGUSR1 from parent
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
printf("sigprocmask error!\n");
exit(4);
}
}
void TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1);
}
void WAIT_CHILD(void)
{
// Parent Process wait for the SIGUSR2 from child
while(sigflag == 0)
sigsuspend(&zeromask);
sigflag = 0;
// After receiving the SIGUSR2 from child
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
printf("sigprocmask error!\n");
exit(4);
}
}
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));
}
void lockabyte(const char *name, int fd, off_t offset)
{
if(writew_lock(fd, offset, SEEK_SET, 1) < 0) {
printf("%s, writew_lock error!\n", name);
return;
}
printf("%s: got the lock, byte %ld\n", name, offset);
}
int main(int argc, char* argv[])
{
int fd;
pid_t pid;
if((fd = creat("templock", O_RDWR)) < 0) {
printf("creat error!\n");
exit(5);
}
if(write(fd, "ab", 2) != 2) {
printf("write error!\n");
exit(6);
}
TELL_WAIT();
if((pid = fork()) < 0) {
printf("fork error!\n");
exit(7);
} else if(pid == 0) {
lockabyte("child", fd, 0);
TELL_PARENT(getppid());
WAIT_PARENT();
lockabyte("child", fd, 1);
} else {
lockabyte("parent", fd, 1);
TELL_CHILD(pid);
WAIT_CHILD();
lockabyte("parent", fd, 0);
}
exit(0);
}
shell:
the program's parent process try to lock byte 1 and then byte 0, its child process tries to lock byte 0 and then byte 1. When the child process tries to acquire the lock, the os detect the deadlock, then child process exits. After it exits, all its lock are released. Finally parent process acquire the lock.
ubuntu@ip-172-31-23-227:~$ ./lock.out
parent: got the lock, byte 1
child: got the lock, byte 0
child, writew_lock error!
parent: got the lock, byte 0
No comments:
Post a Comment