第09章 记录上锁

9.3 Posix fcntl记录上锁

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );
  • 用于记录上锁的cmd共有三个值,这要求arg指向flock结构体
struct flock {
    ...
    short l_type;    /* Type of lock: F_RDLCK,F_WRLCK, F_UNLCK */
    short l_whence;  /* How to interpret l_start:SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start;   /* Starting offset for lock */
    off_t l_len;     /* Number of bytes to lock */
    pid_t l_pid;     /* PID of process blocking our lock
                        (F_GETLK only) */
    ...
};
  • F_GETLK, F_SETLK and F_SETLKW are used to acquire, release, and test for the existence of record locks (also known as file-segment or file-region locks). The third argument, lock, is a pointer to a structure that has at least the following fields (in unspecified order).
  • F_SETLK (struct flock *)
    Acquire a lock (when l_type is F_RDLCK or F_WRLCK) or release a lock (when l_type is
    F_UNLCK) on the bytes specified by the l_whence, l_start, and l_len fields of lock. If a
    conflicting lock is held by another process, this call returns -1 and sets errno to EACCES
    or EAGAIN.

  • F_SETLKW (struct flock *)
    As for F_SETLK, but if a conflicting lock is held on the file, then wait for that lock to
    be released. If a signal is caught while waiting, then the call is interrupted and (after
    the signal handler has returned) returns immediately (with return value -1 and errno set
    to EINTR; see signal(7)).

  • F_GETLK (struct flock *)
    On input to this call, lock describes a lock we would like to place on the file. If the
    lock could be placed, fcntl() does not actually place it, but returns F_UNLCK in the
    l_type field of lock and leaves the other fields of the structure unchanged. If one or
    more incompatible locks would prevent this lock being placed, then fcntl() returns details
    about one of these locks in the l_type, l_whence, l_start, and l_len fields of lock and
    sets l_pid to be the PID of the process holding that lock.

9.3.1 例子

  • unp给了一个文件上锁的例子
#include    "unpipc.h"

void my_lock(int fd)
{
    struct flock    lock;

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;             /* write lock entire file */

    Fcntl(fd, F_SETLKW, &lock);
}

void my_unlock(int fd)
{
    struct flock    lock;

    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;             /* unlock entire file */

    Fcntl(fd, F_SETLK, &lock);
}

例子:简化使用的宏

  • 见书上161页

9.4 劝告性上锁

  • 上面说的都是

9.5 强制性上锁(没什么luan用)

  • 为了对特定文件施行强制性上锁

    • 组成员的执行位必须关掉
    • SGID位必须打开
  • 书上举例说明了只要某个进程违规就可能引发混乱,所以还是必须使用某种上锁形式协作

9.6 读出者和写入者的优先级

9.6.1 例子:某个写入锁待处理期间的额外读出锁

#include    "unpipc.h"

int main(int argc, char **argv)
{
    int     fd;

    fd = Open("test1.data", O_RDWR | O_CREAT, FILE_MODE);

    Read_lock(fd, 0, SEEK_SET, 0);      /* parent read locks entire file */
    printf("%s: parent has read lock\n", Gf_time());

    if (Fork() == 0) {
            /* 4first child */
        sleep(1);
        printf("%s: first child tries to obtain write lock\n", Gf_time());
        Writew_lock(fd, 0, SEEK_SET, 0);    /* this should block */
        printf("%s: first child obtains write lock\n", Gf_time());
        sleep(2);
        Un_lock(fd, 0, SEEK_SET, 0);
        printf("%s: first child releases write lock\n", Gf_time());
        exit(0);
    }

    if (Fork() == 0) {
            /* 4second child */
        sleep(3);
        printf("%s: second child tries to obtain read lock\n", Gf_time());
        Readw_lock(fd, 0, SEEK_SET, 0);
        printf("%s: second child obtains read lock\n", Gf_time());
        sleep(4);
        Un_lock(fd, 0, SEEK_SET, 0);
        printf("%s: second child releases read lock\n", Gf_time());
        exit(0);
    }

    /* 4parent */
    sleep(5);
    Un_lock(fd, 0, SEEK_SET, 0);
    printf("%s: parent releases read lock\n", Gf_time());
    exit(0);
}

9.7 启动一个守护进程的唯一副本

  • unpipc给出了一个怎么巧用记录上锁确保只打开一个守护进程
#include    "unpipc.h"

#define PATH_PIDFILE    "pidfile"

int main(int argc, char **argv)
{
    int     pidfd;
    char    line[MAXLINE];

        /* 4open the PID file, create if nonexistent */
    pidfd = Open(PATH_PIDFILE, O_RDWR | O_CREAT, FILE_MODE);

        /* 4try to write lock the entire file */
    if (write_lock(pidfd, 0, SEEK_SET, 0) < 0) {
        if (errno == EACCES || errno == EAGAIN)
            err_quit("unable to lock %s, is %s already running?",
                     PATH_PIDFILE, argv[0]);
        else
            err_sys("unable to lock %s", PATH_PIDFILE);
    }

        /* 4write my PID, leave file open to hold the write lock */
    snprintf(line, sizeof(line), "%ld\n", (long) getpid());
    Ftruncate(pidfd, 0);
    Write(pidfd, line, strlen(line));

    /* then do whatever the daemon does ... */

    pause();
}
  • 当然也能使用信号量实现,但是上述做法比较普遍,如果守护进程过早崩溃,内核会自动释放记录锁

猜你喜欢

转载自blog.csdn.net/qq_36337149/article/details/81302284