记录锁

来源:互联网 发布:三拼动物域名 编辑:程序博客网 时间:2024/06/05 18:24




一、记录锁record locking

若两个人同时编辑一个文件,会出现什么样的后果呢?对于很多UNIX系统,该文件的最后状态取决于写该文件的最后一个进程。但是,对于某些应用程序(例如数据库),进程有时需要确保它正在单独写一个文件。记录锁就是提供这种功能的机制。

记录锁的功能是:当一个进程正在读或者修改文件的某个部分时,它可以阻止其他进程修改同一文件区。

 

二、fcntl记录锁

#include <fcntl.h>

int fcntl(int filedes,int cmd,.../*struct flock *flockptr*/);

返回值:若成功则依赖于cmd,失败则返回-1

参数:

filedes:文件描述符

cmd:F_GETLK,F_SETLK或F_SETLKW

         F_GETLK:获取记录锁状态;F_SETLKW:设置记录锁;F_SETLK:F_GETLK的阻塞版本

flockptr:指向struct flock 的指针

struct flock {
     short l_type;   /* F_RDLCK, F_WRLCK, or F_UNLCK */
     off_t l_start;  /* offset in bytes, relative to l_whence */
     short l_whence; /* SEEK_SET, SEEK_CUR, or SEEK_END */
     off_t l_len;    /* length, in bytes; 0 means lock to EOF */
     pid_t l_pid;    /* returned with F_GETLK */
   };

 

示例1:加锁和解锁一个文件区域

#include "apue.h"
#include <fcntl.h>

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));
}

 

因为大多数锁调用是加锁或解锁一个文件区域,故常用以下几个宏:

#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))


示例2:测试一把锁

#include "apue.h"
#include <fcntl.h>

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)
        err_sys("fcntl error");

    if (lock.l_type == F_UNLCK)
        return(0);      /* false, region isn't locked by another proc */


    return(lock.l_pid); /* true, return pid of lock owner */
}

对应的宏为:

#define is_read_lockable(fd, offset, whence, len) /          (lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)#define is_write_lockable(fd, offset, whence, len) /          (lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)
示例3:死锁
死锁:如果两个进程互相等待对方持有并且锁定的资源,则这两个进程就处于死锁状态。
#include "apue.h"#include <fcntl.h>static void lockabyte(const char *name, int fd, off_t offset){    if (writew_lock(fd, offset, SEEK_SET, 1) < 0)        err_sys("%s: writew_lock error", name);    printf("%s: got the lock, byte %ld/n", name, offset);}int main(void){    int      fd;    pid_t    pid;    /*     * Create a file and write two bytes to it.     */    if ((fd = create("templock", FILE_MODE)) < 0)        err_sys("creat error");    if (write(fd, "ab", 2) != 2)        err_sys("write error");    TELL_WAIT();    if ((pid = fork()) < 0) {        err_sys("fork error");    } else if (pid == 0) {         /* child */        lockabyte("child", fd, 0);        TELL_PARENT(getppid());        WAIT_PARENT();        lockabyte("child", fd, 1);    } else {                       /* parent */        lockabyte("parent", fd, 1);        TELL_CHILD(pid);        WAIT_CHILD();        lockabyte("parent", fd, 0);    }    exit(0);}
示例4:在文件整体上加锁
#include <unistd.h>#include <fcntl.h>int lockfile(int fd){    struct flock fl;    fl.l_type = F_WRLCK;    fl.l_start = 0;    fl.l_whence = SEEK_SET;    fl.l_len = 0;    return(fcntl(fd, F_SETLK, &fl));}
#define lockfile(fd) write_lock((fd), 0, SEEK_SET, 0)
三、锁的隐含继承和释放
1 锁与进程和文件两方面有关。
当一个进程终止时,它所建立的锁全部释放。任何时候关闭一个文件描述符时,则该进程通过这一描述符可以引用的文件上
的任何一把锁都被释放。
2 由fork产生的子进程不继承父进程所设置的锁。
3 在执行exec后,新程序可以继承原程序的锁。
四、在文件尾端加锁
在接近文件尾端加锁或解锁要特别小心,否则可能会得不到预期的结果。
五、建议性锁和强制性锁
建议锁要求所有程序自觉遵守协议,先申请资源再使用资源,对不遵守协议的程序没有强制力。
一般unix系统上使用的上建议性锁,只是在合作进程之间才能有效发挥作用,但是对于其他的进程,则没有约束力. 所以定义了强制锁.
示例:确定是否支持强制性锁
#include <errno.h>#include <fcntl.h>#include <sys/wait.h>#include <sys/stat.h>#include <unistd.h>#include <stdio.h>
void set_fl(int fd,int flags);int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len);
void set_fl(int fd,int flags){ int val;
 if((val=fcntl(fd,F_GETFL,0))<0)  printf("fcntl F_GETFL error/n"); val|=flags; if((val=fcntl(fd,F_SETFL,0))<0)  printf("fcntl F_SETFL error/n");}
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));}
#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))
int main(int argc, char *argv[]){    int             fd;    pid_t           pid;    char            buf[5];    struct stat     statbuf;    if (argc != 2) {        fprintf(stderr, "usage: %s filename/n", argv[0]);        exit(1);    }
    if ((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC)) < 0)        printf("open error/n");    if (write(fd, "abcdef", 6) != 6)         printf("write error");
    /* turn on set-group-ID and turn off group-execute */    if (fstat(fd, &statbuf) < 0)        printf("fstat error");    if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)        printf("fchmod error");
   // TELL_WAIT();
    if ((pid = fork()) < 0) {        printf("fork error");    } else if (pid > 0) {   /* parent */        /* write lock entire file */        if (write_lock(fd, 0, SEEK_SET, 0) < 0)           printf("write_lock error");
       // TELL_CHILD(pid);
        if (waitpid(pid, NULL, 0) < 0)            printf("waitpid error");    } else {                /* child */        sleep(5);//WAIT_PARENT();      /* wait for parent to set lock */
        set_fl(fd, O_NONBLOCK);
       /* first let's see what error we get if region is locked */       if (read_lock(fd, 0, SEEK_SET, 0) != -1)    /* no wait */           printf("child: read_lock succeeded");       printf("read_lock of already-locked region returns %d/n",errno);
       /* now try to read the mandatory locked file */       if (lseek(fd, 0, SEEK_SET) == -1)           printf("lseek error");       if (read(fd, buf, 2) < 0)           printf("read failed (mandatory locking works)");       else           printf("read OK (no mandatory locking), buf = %2.2s/n",            buf);    }    exit(0);}
[root@localhost chapter_14]# ./14_6 yuanread_lock of already-locked region returns 11read OK (no mandatory locking), buf = ab
原创粉丝点击