【问题标题】:Is there a good way to detect a stale NFS mount有没有检测过时 NFS 挂载的好方法
【发布时间】:2010-12-11 05:31:18
【问题描述】:

我有一个程序,我只想在成功完成多个测试后启动。

我需要的一个测试是我的所有 NFS 挂载都正常运行。

我能比蛮力方法做得更好吗:


mount | sed -n "s/^.* on \(.*\) type nfs .*$/\1/p" | 
while read mount_point ; do 
  timeout 10 ls $mount_point >& /dev/null || echo "stale $mount_point" ; 
done

这里的timeout 是一个实用程序,它将在后台运行命令,并在给定时间后将其杀死,如果在时间限制之前没有捕获到SIGCHLD,则以明显的方式返回成功/失败。


英文:解析mount 的输出,检查(以超时为界)每个NFS 挂载点。可选地(不在上面的代码中)在第一个陈旧的装载上中断。

【问题讨论】:

    标签: linux bash unix shell nfs


    【解决方案1】:

    我的一位同事遇到了您的脚本。这并不能避免“蛮力”方法,但如果我可以在 Bash 中:

    while read _ _ mount _; do 
      read -t1 < <(stat -t "$mount") || echo "$mount timeout"; 
    done < <(mount -t nfs)
    

    mount 可以直接列出 NFS 挂载。 read -t(shell 内置)可以使命令超时。 stat -t(简洁输出)仍然像 ls* 一样挂起。 ls 会产生不必要的输出,在巨大/缓慢的目录列表中存在误报的风险,并且需要访问权限 - 如果没有这些权限也会触发误报。

    while read _ _ mount _; do 
      read -t1 < <(stat -t "$mount") || lsof -b 2>/dev/null|grep "$mount"; 
    done < <(mount -t nfs)
    

    我们将它与lsof -b 一起使用(非阻塞,因此它也不会挂起)以确定挂起的来源。

    感谢指点!

    • test -d(shell 内置)也可以代替stat(标准外部)工作,但read -t 只有在没有超时并读取一行输入时才返回成功。由于test -d 不使用标准输出,因此有必要对其进行(( $? &gt; 128 )) 错误级别检查 - 不值得对易读性造成影响,IMO。

    【讨论】:

    • 虽然后一个示例允许命令(不挂在统计数据上),但 lsof -b 2 似乎只是跳过所有统计数据测试并且不返回任何内容。
    • 如您所知,&lt;(...) 在子 shell 中执行,如果 stat(1) 由于 NFS 陈旧而挂起,子 shell 将不会正常终止。请参阅check-nfs.sh 了解对此的改进。
    • 这真的很好,除了当挂载的源在路径中有空格时。这是一个适用于空白的变体:while read mount;超时 -k 2 2 stat -t $mount > /dev/null || echo "$mount timeout" done
    【解决方案2】:

    花了我一些时间,但这是我发现的适用于 Python 的内容:

    import signal, os, subprocess
    class Alarm(Exception):
        pass
        
    def alarm_handler(signum, frame):
        raise Alarm
    
    pathToNFSMount = '/mnt/server1/' # or you can implement some function 
                                     # to find all the mounts...
    
    signal.signal(signal.SIGALRM, alarm_handler)
    signal.alarm(3)  # 3 seconds
    try:
        proc = subprocess.call('stat '+pathToNFSMount, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 
        stdoutdata, stderrdata = proc.communicate()
        signal.alarm(0)  # reset the alarm
    except Alarm:
        print "Oops, taking too long!"
    

    备注:

    1. 感谢answer here

    2. 您也可以使用替代方案:

      os.fork()os.stat()

    检查分叉是否完成,如果超时,您可以将其杀死。您将需要使用time.time() 等等。

    【讨论】:

      【解决方案3】:

      除了在某些情况下挂起的先前答案之外,此 sn-p 检查所有合适的挂载,使用信号 KILL 杀死,并且也使用 CIFS 进行测试:

      grep -v tracefs /proc/mounts | cut -d' ' -f2 | \
        while read m; do \
          timeout --signal=KILL 1 ls -d $m > /dev/null || echo "$m"; \
        done
      

      【讨论】:

        【解决方案4】:

        您可以编写一个 C 程序并检查 ESTALE

        #include <sys/types.h>
        #include <sys/stat.h>
        #include <unistd.h>
        #include <iso646.h>
        #include <errno.h>
        #include <stdio.h>
        #include <stdlib.h>
        
        int main(){
            struct stat st;
            int ret;
            ret = stat("/mnt/some_stale", &st);
            if(ret == -1 and errno == ESTALE){
                printf("/mnt/some_stale is stale\n");
                return EXIT_SUCCESS;
            } else {
                return EXIT_FAILURE;
            }
        }
        

        【讨论】:

        • 来自man 3 errno:ESTALE 保留。这是否意味着我应该寻找其他解决方案?
        • 我猜这取决于你的内核。
        • @Teddy,实际上,你试过你的代码了吗?在我的 Linux 机器中,如果有一个陈旧的 NFS,命令 stat 将挂起,就像你的函数 stat("/mnt/some_stale", &st),所以代码永远不会返回......
        • 确认@Oz123 的观察 - 挂起的 NFS 挂载将无限期挂起该程序。
        • 我在 python 中为这个答案编写了一个非常复杂的代码(并在我有一个陈旧的文件系统时设法对其进行了测试):github.com/guysoft/stale_mount_checker
        【解决方案5】:

        如果您不介意由于文件系统陈旧而等待命令完成,那么编写一个检查 ESTALE 的 C 程序是一个不错的选择。如果您想实现“超时”选项,我发现实现它的最佳方法(在 C 程序中)是派生一个尝试打开文件的子进程。然后检查子进程是否在分配的时间内成功地完成了文件系统中的文件读取。

        这里有一个小型的概念证明 C 程序来做到这一点:

        #include <stdlib.h>
        #include <stdio.h>
        #include <stdint.h>
        #include <unistd.h>
        #include <errno.h>
        #include <fcntl.h>
        #include <sys/wait.h>
        
        
        void readFile();
        void waitForChild(int pid);
        
        
        int main(int argc, char *argv[])
        {
          int pid;
        
          pid = fork();
        
          if(pid == 0) {
            // Child process.
            readFile();
          }
          else if(pid > 0) {
            // Parent process.
            waitForChild(pid);
          }
          else {
            // Error
            perror("Fork");
            exit(1);
          }
        
          return 0;
        }
        
        void waitForChild(int child_pid)
        {
          int timeout = 2; // 2 seconds timeout.
          int status;
          int pid;
        
          while(timeout != 0) {
            pid = waitpid(child_pid, &status, WNOHANG);
            if(pid == 0) {
              // Still waiting for a child.
              sleep(1);
              timeout--;
            }
            else if(pid == -1) {
              // Error
              perror("waitpid()");
              exit(1);
            }
            else {
              // The child exited.
              if(WIFEXITED(status)) {
                // Child was able to call exit().
                if(WEXITSTATUS(status) == 0) {
                  printf("File read successfully!\n");
                  return;
                }
              }
              printf("File NOT read successfully.\n");
              return;
            }
          }
        
          // The child did not finish and the timeout was hit.
          kill(child_pid, 9);
          printf("Timeout reading the file!\n");
        }
        
        void readFile()
        {
          int fd;
        
          fd = open("/path/to/a/file", O_RDWR);
          if(fd == -1) {
            // Error
            perror("open()");
            exit(1);
          }
          else {
            close(fd);
            exit(0);
          }
        }
        

        【讨论】:

          【解决方案6】:

          我写了https://github.com/acdha/mountstatus,它使用类似于 UndeadKernel 提到的方法,我发现这是最强大的方法:它是一个守护进程,它通过分叉一个尝试列出顶部的子进程定期扫描所有已挂载的文件系统-level 目录和SIGKILL 如果它在一定的超时时间内没有响应,成功和失败都会记录到 syslog 中。这避免了某些客户端实现(例如较旧的 Linux)的问题,这些实现永远不会为某些类别的错误触发超时,NFS 服务器部分响应但例如不会响应listdir等实际调用。

          我没有发布它们,但包含的 Makefile 使用 fpm 使用 Upstart 脚本构建 rpm 和 deb 包。

          【讨论】:

          【解决方案7】:

          另一种方式,使用 shell 脚本。对我有用:

          #!/bin/bash
          # Purpose:
          # Detect Stale File handle and remove it
          # Script created: July 29, 2015 by Birgit Ducarroz
          # Last modification: --
          #
          
          # Detect Stale file handle and write output into a variable and then into a file
          mounts=`df 2>&1 | grep 'Stale file handle' |awk '{print ""$2"" }' > NFS_stales.txt`
          # Remove : ‘ and ’ characters from the output
          sed -r -i 's/://' NFS_stales.txt && sed -r -i 's/‘//' NFS_stales.txt && sed -r -i 's/’//' NFS_stales.txt
          
          # Not used: replace space by a new line
          # stales=`cat NFS_stales.txt && sed -r -i ':a;N;$!ba;s/ /\n /g' NFS_stales.txt`
          
          # read NFS_stales.txt output file line by line then unmount stale by stale.
          #    IFS='' (or IFS=) prevents leading/trailing whitespace from being trimmed.
          #    -r prevents backslash escapes from being interpreted.
          #    || [[ -n $line ]] prevents the last line from being ignored if it doesn't end with a \n (since read returns a non-zero exit code when it encounters EOF).
          
          while IFS='' read -r line || [[ -n "$line" ]]; do
              echo "Unmounting due to NFS Stale file handle: $line"
              umount -fl $line
          done < "NFS_stales.txt"
          #EOF
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-09
            • 1970-01-01
            • 2018-08-22
            • 1970-01-01
            相关资源
            最近更新 更多