zoukankan      html  css  js  c++  java
  • 文件记录锁的释放

    引言:apue中提到文件记录锁的释放中的两条规则:当进程终止的时候,进程在文件上建立的记录锁会全部释放;当关闭文件,执行close(fd)函数的时候,进程释放描述符可以引用的文件上的任何一把锁。对于第一条规则的理解应该没有分歧。但对于第二条规则的理解,则会出现疑惑,执行close(fd)的时候,是仅仅释放closf(fd)调用进程在文件上的记录锁,还是会释放所有进程(包括非调用进程)在文件上的记录锁。本文通过具体相关程序对此问题进行验证。

    思路:需要设计验证程序对此问题进行验证,通过fork产生不同进程。子进程对文件0字节加写锁,然后子进程执行close(fd)函数,同时父进程对文件1字节加写锁。完成以上操作后,发送信号,在信号处理程序中,父进程尝试对0字节进行加写锁,子进程尝试对1字节进行加写锁。

    代码:

     1 /*
      2  * =====================================================================================
      3  *
      4  *       Filename:  2.c
      5  *
      6  *    Description:  
      7  *
      8  *        Version:  1.0
      9  *        Created:  2014年08月21日 15时30分58秒
     10  *       Revision:  none
     11  *       Compiler:  gcc
     12  *
     13  *         Author:  3me  
     14  *   Organization:  
     15  *
     16  * =====================================================================================
     17  */
     18 #include <stdlib.h>
     19 #include <stdio.h>
     20 #include <fcntl.h>
     21 #include <signal.h>
     22 #include <unistd.h>
     23 #include <signal.h>
     24 static int fd,fd_c,fd_p;
     25 /* 
     26  * ===  FUNCTION  ======================================================================
     27  *         Name:  lock_reg
     28  *  Description:  
     29  * =====================================================================================
     30  */
     31    int
     32 lock_reg ( int fd, int cmd, int type, off_t offset, int whence, off_t len )
     33 {
     34     struct flock lock;
     35 
     36     lock.l_type = type;
     37     lock.l_start = offset;
     38     lock.l_whence = whence;
     39     lock.l_len = len;
     40 
     41     return fcntl(fd, cmd, &lock);
     42 }   /* -----  end of function lock_reg  ----- */
     43 /* 
     44  * ===  FUNCTION  ======================================================================
     45  *         Name:  sig_usr
     46  *  Description: 子进程和父进程共用的信号处理程序,子进程收到信号SIGUSR1后,尝试申请文件
     47  *  第一个字节的写锁,父进程收到SIGUSR2后,尝试申请第0个字节的写锁.如果申请的锁未释放,则                                                                              
     48  *  申请的锁会失败,打印对应errno,可以验证close(fd)函数对文件锁的释放.
     49  * =====================================================================================
     50  */
     51     static void
     52 sig_usr ( int signo )
     53 {
     54     if ( signo == SIGUSR1 ) {
     55         if ( lock_reg(fd_c, F_SETLK, F_WRLCK, 2, SEEK_SET, 1) == -1 )
     56         {
     57             perror("child lock_reg  1 error");
     58             return;
     59         }
     60         printf("child get parent's write lock success.
    ");
     61     }
     62     else if ( signo == SIGUSR2 ) {
     63         if ( lock_reg(fd_p, F_SETLK, F_WRLCK, 0, SEEK_SET, 1) == -1 )
     64         {
     65             perror("parent lock_reg  0 error");
     66             return;
     67         }
     68         printf("parent get child's write lock success.
    ");
     69     }
     70     else
     71         printf("unexpected signo.
    ");
     72 }   /* -----  end of function sig_usr  ----- */
     73 
     74 /* 
     75  * ===  FUNCTION  ======================================================================
     76  *         Name:  main
     77  *  Description: 子进程申请第0个字节的写锁,然后子进程执行close(fd),父进程申请第一个字节的写锁。 
     78  *  如果close(fd)函数仅仅关闭该函数(close(fd))调用进程在文件上的记录锁,则在信号处理程序中,父进程可以
     79  *  获得子进程的锁(因为已有子进程经过closf(fd)释放),子进程无法获得父进程的锁(因为在子进程中
     80  *  执行close(fd)无法释放父进程的写锁).如果close(fd)函数能够关闭所有进程在文件上的记录锁,
     81  *  则在信号处理程序中,父进程可以获得子进程的写锁,子进程可以获得父进程的写锁.
     82  * =====================================================================================
     83  */
     84     int
     85 main ( int argc, char *argv[] )
     86 {
     87     pid_t pid;
     88 
     89     if ( (fd = open("2.c", O_RDWR) == -1) )
     90             perror("open 2.c failed");
     91 
     92     if ( (pid = fork()) < 0 )
     93         perror("fork error");
     94 
                                       95     else if ( pid == 0 ) {
     96         /*-----------------------------------------------------------------------------
     97          *  进入进程,先设置对应SIGNO(SIGUSR1/SIGUSR2)的信号屏蔽字,等待进程占有文件的字节
     98          *  锁后,才能释接受信号。避免进程占有文件锁之前进入信号处理程序,从而进入错误逻辑,
     99          *  使得结果不可控(父进程也做同样处理).
    100          *-----------------------------------------------------------------------------*/
    101         sigset_t newmask, oldmask;
    102 
    103         sigemptyset(&newmask);
    104         sigaddset(&newmask, SIGUSR1);
    105         sigprocmask(SIG_BLOCK, &newmask, &oldmask);
    106         if ( signal(SIGUSR1, sig_usr) == SIG_ERR )
    107             perror("register sigusr1 error");
    108         sleep(2);
    109 
    110         if ( lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 1) == -1 )
    111         {
    112             perror("child lock_reg  0 error");
    113             return EXIT_FAILURE;
    114         }
    115         printf("child lock 0 success.
    ");
    116         fd_c = dup(fd);
    117         close(fd);
    118         printf("child : after close(fd).
    ");
    119 
    120         printf("child:getppid = %d.
    ", getppid());
    121         if ( kill(getppid(), SIGUSR2) < 0 )
    122             perror("child push sigusr2 error");
    123         printf("child:send sigusr2 success.
    ");
    124 
    125         sigsuspend(&oldmask);
    126 
    127 
    128     }
    129 
    130     else {
    131         sigset_t newmask,oldmask;
    132 
    133         sigemptyset(&newmask);
    134         sigaddset(&newmask, SIGUSR2);
    135         sigprocmask(SIG_BLOCK, &newmask, &oldmask);
    136 
    137         if ( signal(SIGUSR2, sig_usr) == SIG_ERR )
    138             perror("register sigusr2 error");
    139 
    140         if ( lock_reg(fd, F_SETLK, F_WRLCK, 2, SEEK_SET, 1) == -1 )
    141         {
    142             perror("parent lock_reg  1 error");
    143             return EXIT_FAILURE;
    144         }
    145         printf("parenet lock_reg 1 byte success.
    ");
    146         fd_p = dup(fd);
    147 //        close(fd);
    148 
    149 
    150         printf("parent:pid = %d.
    ", pid);
    151 
    152         if ( kill(pid, SIGUSR1) < 0 )
    153             perror("parent push sigusr1 error");
    154         printf("parent:send sigusr1 success.
    ");
    155         sigsuspend(&oldmask);
    156 
    157     }
    158 
    159 
    160 
    161 
    162 
    163     return EXIT_SUCCESS;
    164 }       /* ----------  end of function main  ---------- */
    165 

     分析:

    执行以上代码,结果如下所示:

    parenet lock_reg 1 byte success.
    parent:pid = 10484.
    parent:send sigusr1 success.
    child lock 0 success.
    child : after close(fd).
    child:getppid = 10483.
    child:send sigusr2 success.
    parent get child's write lock success.
    child lock_reg  1 error: Resource temporarily unavailable

    从运行结果可以看出,在main函数中,父进程能够成功对1字节加锁,子进程能够成功对0字节加锁。在信号处理程序中,父进程能够成功对0自己加锁,说明在子进程中执行close(fd)确实释放了子进程的写锁。在信号处理程序中,子进程不能够对文件的1字节加锁,说明子进程中执行closf(fd),并不能释放父进程的写锁。因此,有理由推断,当执行closf(fd)的时候,仅仅释放该函数调用进程在此文件上的锁,不会影响其他进程载此文件上的锁。为了进一步验证此结论可以放开父进程中close(fd)的注释行,则在信号处理程序中,父进程能够对0字节加锁,子进程能够对1字节加锁。

    执行结果如下:

    parenet lock_reg 1 byte success.
    parent:pid = 10503.
    parent:send sigusr1 success.
    child lock 0 success.
    child : after close(fd).
    child:getppid = 10502.
    child:send sigusr2 success.
    child get parent's write lock success.
    parent get child's write lock success.

    思考:

    1/在main函数中,父进程加锁的偏移量设置的为2,即加锁文件文件实际的第3个字节,能够得到预期的结果。但是当加锁偏移量设置为1的时候,即加锁文件实际的第2个字节,有父进程获取写锁不成功的情况,会不会是如果两个进程在文件相邻位置获取写锁,fcntl容易执行失败?

  • 相关阅读:
    windows下如何添加、删除和修改静态路由
    node.js后台快速搭建在阿里云(二)(pm2和nginx篇)
    使用PM2守护Nodejs命令行程序
    使用pm2管理nodejs应用
    ubuntu 下安装nodejs以及pm2
    nginx优化缓冲缓存
    nginx proxy_buffer_size 解决后端服务传输数据过多,其实是header过大的问题
    经济规律 宏观调控 与个体
    宏观调控
    《论教育》-------叔本华
  • 原文地址:https://www.cnblogs.com/3me-linux/p/3927899.html
Copyright © 2011-2022 走看看