进程同步方式(一)--记录锁

进程同步方式(一)--记录锁



记录锁

定义

    一个进程正在读或修改文件的某个部分时,可以使用记录锁锁定文件的一个区域(也可能是整个文件)。它是一个字节范围内的锁。


接口声明

    POSIX记录锁是使用fcntl 函数实现。Linux系统支持POSIX标准。


    #include <unistd.h>

    #include <fcntl.h>

    int fcntl(int fd, int cmd, ... /* arg */ );


    参数cmd 为F_GETLK,F_SETLK和F_SETLKW用于测试、获取、释放记录锁(也称为文件段或文件区锁)。 第三个参数lock是指向至少具有以下字段(未指定顺序)的结构的指针。其中,GETLK测试能否建立一把锁,F_SETLK设置由flock描述的锁,F_SETLKW是F_SETLK的阻塞版本。



    struct flock {

               ...

    short l_type;    /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */

    short l_whence;  /* How to interpret l_start: EEK_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) */

               ...

    };


多进程提出锁的请求及结果


单进程提出锁的请求及结果

    单进程如果在文件的某个区域加了锁,如果又在这个区域加一个锁,则原来的锁被覆盖。


建议性锁和强制性锁


    建议性锁:建议性锁并不从内核限制程序访问文件,而是依赖各个合作进程(cooperating process)之间遵循相应的规则。

    强制性锁:强制性锁会让内核检查每一个open、read、和write,验证调用进程是否违背了正访问的文件上的某一把锁。


强制性锁对其他进程的read和write的影响


测试例子

例子-测试文件所是否上锁

代码

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.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){
                printf("fcntl error\n");
                exit(1);
        }
        
        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 */

}

int main(){

        int fd = open("locktest.c", O_RDWR );
        struct flock fl;



        fl.l_type = F_WRLCK;
        fl.l_start = 0;
        fl.l_whence = SEEK_SET;
        fl.l_len = 100;

        fcntl(fd, F_SETLK, &fl);

         printf("pid_t %d \n",lock_test(fd, F_WRLCK, 2,SEEK_CUR , 10));

        sleep(1000);

}


运行

[root@bogon advio]# gcc locktest.c -o locktest1 

[root@bogon advio]# gcc locktest.c -o locktest2


终端1运行locktest1 ,如下所示:

[root@bogon advio]# ./locktest1 

pid_t 0 


终端2运行locktest2 ,如下所示:

[root@bogon advio]# ./locktest2 

pid_t 29423 


说明

    F_GETLK (struct flock *)


    在输入这个调用时,flock描述了一个我们想要放在文件上的锁。 如果可以放置锁,则fcntl()实际上并不放置它,但在锁的l_type字段中返回F_UNLCK,并保持结构的其他字段不变。 如果一个或多个不兼容的锁会阻止这个锁被放置,那么fcntl()返回有关锁的l_type,l_whence,l_start和l_len字段中的其中一个锁的详细信息,并将l_pid设置为持有该锁的进程的PID。知道这一点很重要。


例子-加锁释放锁

代码

int main(){

        int fd = open("locktest.c", O_RDWR );
        struct flock fl,fl2;
        fl.l_type = F_WRLCK;
        fl.l_start = 0;
        fl.l_whence = SEEK_SET;
        fl.l_len = 100;

        fcntl(fd, F_SETLK, &fl);

        fl2.l_type = F_UNLCK;
        fl2.l_start = 0;
        fl2.l_whence = SEEK_SET;
        fl2.l_len = 100;

        fcntl(fd, F_SETLK, &fl2);

}


CentOS开启强制性锁

    如果要开启强制性锁,要由以下两个步骤完成:

    1)在文件系统mount的时候加上-o mand参数

    2)打开文件的设置组ID位并且关闭其组执行位

    在shell下可以这样打开

    chmod g+s <filename>

    chmod g-x <filename>



    或者通过fchmod函数设置,参考下面的代码


    fchmod(fd,(statbuf.st_mode & ~S_IXGRP) | S_ISGID)



测试代码

说明:以下代码从《高级UNIX编程》代码中拷贝来的,特此说明。


#include "apue.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>

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, FILE_MODE)) < 0)
                err_sys("open error");

        if (write(fd, "abcdef", 6) != 6)
                err_sys("write error");

        /* turn on set-group-ID and turn off group-execute */

        if (fstat(fd, &statbuf) < 0)
                err_sys("fstat error");

        if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)
                err_sys("fchmod error");

        TELL_WAIT();

        if ((pid = fork()) < 0) {
                err_sys("fork error");

        } else if (pid > 0) {   /* parent */

                /* write lock entire file */
                if (write_lock(fd, 0, SEEK_SET, 0) < 0)

                        err_sys("write_lock error");

                TELL_CHILD(pid);

                if (waitpid(pid, NULL, 0) < 0)

                        err_sys("waitpid error");

                } else {                                /* child */

                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 */
                        err_sys("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)

                        err_sys("lseek error");

                if (read(fd, buf, 2) < 0)
                        err_ret("read failed (mandatory locking works)");
                else
                        printf("read OK (no mandatory locking), buf = %2.2s\n",  buf);

        }

        exit(0);

}


测试结果及说明


    [root@bogon advio]# ./mandatory temp.lock 

    read    _lock of already-locked region returns 11

    read OK (no mandatory locking), buf = ab

    从上面的输出可以看出来CentOS默认是没有开启强制性锁,因为子进程能从temp.lock文件读出数据。

父进程加了写锁,子进程加读锁的时候出错,errno为11。

注意事项

    1)当一个进程终止时,它建立的锁全部释放。
    2)进程使用文件锁,只能该进程解锁。其他进程不能解锁。
    3)子进程不继承父进程的锁。

    4)在执行exec函数后,新程序可以继承原执行程序的锁。


参考资料

[1]http://shareinto.github.io/2016/12/07/linux%E4%B8%AD%E7%9A%84%E6%96%87%E4%BB%B6%E5%BC%BA%E5%88%B6%E9%94%81/ 

[2].《UNIX高级编程》第三版


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值