【问题标题】:Slow load time of bash in cygwincygwin中bash的加载时间慢
【发布时间】:2013-03-20 22:43:57
【问题描述】:

目前 bash 大约需要 2 秒才能加载。我已经用-x 标志运行了bash,我看到了输出,似乎PATH 在cygwin 中被加载了很多次。有趣的是我在 linux 环境中使用相同的文件,但它工作正常,没有重新加载问题。以下是否会导致问题?

if [ `uname -o` = "Cygwin" ]; then
    ....
fi

【问题讨论】:

  • 您似乎在这里问了两个问题:bash 是否有--startuptime,以及那个.bashrc 出了什么问题。你会更幸运地分别提出每个问题,对于第二个问题,解释具体出了什么问题。
  • 我在手册页和网络的任何地方都找不到 bash 的 --startuptime 选项。我认为这些问题是相辅相成的,所以我一起提出了这些问题。
  • @me_and:我认为你是对的。我应该把它们分开。我会相应地改变我的问题。
  • 不知道时间能不能?也许Bash loads slowly in Cygwin 更好?这与你的英语无关,只是想把一个好问题做得更好。

标签: bash debugging cygwin bash-completion


【解决方案1】:

正如您在回答中指出的那样,问题出在 Cygwin 的 bash-completion 包上。快速简单的解决方法是禁用 bash-completion,正确的方法是运行 Cygwin 的 setup.exe(如果需要,download it again)并选择卸载该软件包。

更长的解决方案是处理/etc/bash_completion.d 中的文件并禁用您不需要的文件。在我的系统上,减慢 Bash 加载时间的最大罪魁祸首(mailman、shadow、dsniff 和 e2fsprogs)都没有做任何事情,因为没有安装它们被创建来完成的工具。

如果您将/etc/bash_completion.d 中的文件重命名为具有.bak 扩展名,它将停止加载该脚本。以这种方式在我的一个系统上禁用了除选定的 37 个脚本之外的所有脚本后,我将 bash_completion 加载的平均时间缩短了 95%(6.5 秒到 0.3 秒)。

【讨论】:

【解决方案2】:

在我的例子中是 Windows 域控制器。 我这样做是为了找到问题:

我从一个简单的 windows cmd.exe 开始,然后输入:
c:\cygwin\bin\strace.exe c:\cygwin\bin\bash

就我而言,我注意到以下顺序:

    218   12134 [main] bash 11304 transport_layer_pipes::connect: Try to connect to named pipe: \\.\pipe\cygwin-c5e39b7a9d22bafb-lpc
     45   12179 [main] bash 11304 transport_layer_pipes::connect: Error opening the pipe (2)
     39   12218 [main] bash 11304 client_request::make_request: cygserver un-available
1404719 1416937 [main] bash 11304 pwdgrp::fetch_account_from_windows: line: <CENSORED_GROUP_ID_#1>
    495 1417432 [main] bash 11304 pwdgrp::fetch_account_from_windows: line: <CENSORED_GROUP_ID_#2>
    380 1417812 [main] bash 11304 pwdgrp::fetch_account_from_windows: line: <CENSORED_GROUP_ID_#3>

    etc...

关键是识别client_request::make_request: cygserver un-available 行。你可以看到,在那之后,cygwin 如何尝试从 windows 中获取每个组,并且执行时间变得疯狂。

一个快速的谷歌揭示了cygserver 是什么: https://cygwin.com/cygwin-ug-net/using-cygserver.html

Cygserver 是一个旨在作为后台运行的程序 服务。它为 Cygwin 应用程序提供需要的服务 安全仲裁或需要在没有其他 cygwin 的情况下持续存在 应用程序正在运行。

解决方案是运行cygserver-config,然后运行net start cygserver 来启动Windows 服务。此后 Cygwin 的启动时间显着下降。

【讨论】:

  • 我在堆栈跟踪中没有看到类似的输出,并且我没有运行任何 cygserver.exe 实例,但感谢分享该信息。能否分享一下你使用的 cygwin 版本?
  • Cygwin 版本为1.7.34(0.285/5/3) 2015-02-04 12:12 i686
  • 这绝对是解决我问题的答案;我也在 Windows 域控制器环境中工作。感谢您解决这个问题。
  • 感谢 strace.exe 提示,它确认这是我的问题。我看到启动时间超过 15 秒,并且修复工作完美。这在今天更新我的 Cygwin 安装后立即开始。为什么几个月前或以前不需要这个?
  • @KevinCondon 真的很难说 - 他们在 Cygwin 1.7.34 中改变了一些东西 - 请参阅常见问题解答:cygwin.com/faq/faq.html#faq.using.startup-slow。我认为这可能是您的原因,尽管按照常见问题解答中的建议删除文件对我没有任何帮助。实际上我去年也经历了那些严重的延迟,只是一个不同的 Corpo 具有不同的 AD 域,所以这最终可能没有关系。
【解决方案3】:

所有答案都参考旧版本的 bash_completion,与最近的 bash_completion 无关。

现代 bash_completion 默认将大部分完成文件移动到/usr/share/bash-completion/completions,通过运行检查系统上的路径

# pkg-config --variable=completionsdir bash-completion
/usr/share/bash-completion/completions

那里有很多文件,每个命令一个,但这不是问题,因为它们是在您第一次对每个命令使用完成时按需加载的。旧的/etc/bash_completion.d 仍然支持兼容性,并且在bash_completion 启动时会加载那里的所有文件。

# pkg-config --variable=compatdir bash-completion
/etc/bash_completion.d

使用此脚本检查旧目录中是否有任何陈旧文件。

#!/bin/sh
COMPLETIONS_DIR="$(pkg-config --variable=completionsdir bash-completion)"
COMPAT_DIR="$(pkg-config --variable=compatdir bash-completion)"
for file in "${COMPLETIONS_DIR}"/*; do
    file="${COMPAT_DIR}/${file#${COMPLETIONS_DIR}/}"
    [ -f "$file" ] && printf '%s\n' $file
done

它打印兼容目录中的文件列表,这些文件也存在于较新的(按需)完成目录中。除非您有特定理由保留其中一些文件,否则请查看、备份并删除所有这些文件。

因此,compat 目录应该大部分是空的。

现在,最有趣的部分是检查bash 启动缓慢的原因。 如果你只是运行bash,它将启动一个非登录的交互式 shell - 这个是 Cygwin 源代码/etc/bash.bashrc 然后~/.bashrc。这很可能不包括 bash 完成,除非您从 rc 文件之一获取它。如果您运行bash -l (bash --login),启动Cygwin Terminal(取决于您的cygwin.bat),或通过SSH 登录,它将启动一个登录、交互 shell - 这将提供/etc/profile~/.bash_profile 和前面提到的 rc 文件。 /etc/profile 脚本本身在/etc/profile.d 中获取所有可执行的.sh 文件。

您可以检查每个文件的来源需要多长时间。在/etc/profile中找到此代码:

for file in /etc/profile.d/*.$1; do
  [ -e "${file}" ] && . "${file}"
done

备份它,然后用这个替换它:

for file in /etc/profile.d/*.$1; do
  TIMEFORMAT="%3lR ${file}"
  [ -e "${file}" ] && time . "${file}"
done

启动bash,您将看到每个文件花费了多长时间。调查需要大量时间的文件。在我的例子中,它是bash_completion.shfzf.sh(fzf 是模糊查找器,是对 bash_completion 的一个非常好的补充)。现在的选择是禁用它或进一步调查。由于我想继续在 bash 中使用 fzf 快捷方式,我调查了,找到了减速的根源,对其进行了优化,并将我的补丁提交给了 fzf 的 repo(希望它会被接受)。

现在是花费时间最多的人 - bash_completion.sh。基本上该脚本来源/usr/share/bash-completion/bash_completion。我备份了那个文件,然后编辑了它。在最后一页有for 循环,它获取compat 目录-/etc/bash_completion.d 中的所有文件。再次,我添加了TIMEFORMATtime,并查看了导致启动缓慢的脚本。它是zzz-fzffzf 包)。我调查并发现一个子shell ($()) 在for 循环中多次执行,在不使用子shell 的情况下重写了该部分,使脚本快速运行。我已经向 fzf 的仓库提交了我的补丁。

所有这些速度变慢的最大原因是:fork 不受 Windows 进程模型的支持,Cygwin 在模拟它方面做得很好,但与真正的 UNIX 相比,它的速度非常慢。一个 subshel​​l 或一个单独做很少工作的管道将它的大部分执行时间花在fork-ing 上。例如。比较time echo msg(我的 Cygwin 上为 0.000 秒)与time echo $(echo msg)(我的 Cygwin 上为 0.042 秒)的执行时间——白天和黑夜。 echo 命令本身不会花费太多时间来执行,但创建子shell 的成本非常高。在我的 Linux 系统上,这些命令分别需要 0.000 秒和 0.001 秒。 Cygwin 的许多软件包都是由使用 Linux 或其他 UNIX 的人开发的,并且可以在未经修改的情况下在 Cygwin 上运行。因此,这些开发人员很自然地可以在方便的地方随意使用子外壳、管道和其他功能,因为他们不会觉得系统性能受到任何显着影响,但在 Cygwin 上,这些外壳脚本的运行速度可能会慢几十到几百倍。

底线,如果 shell 脚本在 Cygwin 中运行缓慢 - 尝试定位 fork 调用的来源并重写脚本以尽可能消除它们。 例如。 cmd="$(printf "$1" "$2")"(使用一个 fork 作为 subshel​​l)可以替换为 printf -v cmd "$1" "$2"

男孩,它出来的时间很长。读到这里的人都是真正的英雄。谢谢:)

【讨论】:

    【解决方案4】:

    我知道这是一个旧线程,但是在本周全新安装 Cygwin 后,我仍然遇到这个问题。

    我没有手工挑选所有 bash_completion 文件,而是使用这一行来实现 @me_and 的方法来处理我机器上未安装的任何内容。这对我来说大大减少了 bash 的启动时间。

    /etc/bash_completion.d 中,执行以下命令:

    for i in $(ls|grep -v /); do type $i >/dev/null 2>&1 || mv $i $i.bak; done
    

    【讨论】:

    • 这是一个很好的答案!对于任何与我想法相同的人(无论扩展名如何,该目录中的所有内容都会被解析),结果表明,读取 /etc/bash_completion 会忽略 .bak 文件以及其他一些文件。还有一个建议是查看带有新的 .bak 扩展名的完成文件,因为有些是上述情况的例外情况(例如配置、bash-builtins),有些可能会在以后安装,所以值得记住你这样做了以防你在安装 gcc 时发现 bash 补全没有按预期工作!
    【解决方案5】:

    旧线程的新答案,与原始问题的PATH 有关。

    大多数其他答案都与 bash 启动有关。如果您在 shell 中运行 bash -i 时发现加载时间很慢,则可能适用。

    在我的例子中,bash -i 跑得很快,但无论何时我打开一个新的 shell(无论是在终端中还是在 xterm 中),都需要很长时间。如果bash -l的时间长,说明是登录时间。

    https://cygwin.com/faq/faq.html#faq.using.startup-slow 的 Cygwin 常见问题解答中有一些方法,但它们对我不起作用。

    原始发帖人询问了PATH,他使用bash -x 进行诊断。我也发现虽然bash -i 很快,但bash -xl 很慢并且显示了很多关于PATH 的信息。

    Windows PATH 太长了,以至于登录过程一直在运行程序并在整个 PATH 中搜索正确的程序。

    我的解决方案:编辑 Windows PATH 以删除任何多余的内容。我不确定我删除的哪个部分起到了作用,但登录 shell 启动时间从 6 秒缩短到了 1 秒以下。

    YMMV.

    【讨论】:

      【解决方案6】:

      我的回答和上面的 npe 一样。但是,由于我刚刚加入,我无法评论甚至投票!我希望这篇文章不会被删除,因为它可以让任何寻求相同问题答案的人放心。

      npe 的解决方案对我有用。只有一个警告 - 在我充分利用它之前,我必须关闭所有 cygwin 进程。这包括运行 cygwin 服务,如 sshd,以及我从登录脚本启动的 ssh-agent。在此之前,cygwin 终端的窗口会立即出现,但会在出现提示之前挂起几秒钟。关闭窗口后它会挂起几秒钟。在我杀死所有进程并启动 cygserver 服务之后(顺便说一句,我更喜欢使用 Cygwin 方式 - 'cygrunsrv -S cygserver',而不是 'net start cygserver';我不知道它是否有任何实际区别)它立即启动。再次感谢 npe!

      【讨论】:

        【解决方案7】:

        我在一个设置非常复杂的公司网络上,这似乎真的扼杀了 cygwin 的启动时间。与 npe 的回答相关,我还必须遵循此处列出的一些步骤:https://cygwin.com/faq/faq.html#faq.using.startup-slow

        AD 客户端系统的另一个原因是 DC 响应缓慢,通常在具有远程 DC 访问权限的配置中观察到。 Cygwin DLL 查询有关您所在的每个组的信息,以便在启动时填充本地缓存。您可以通过将自己的信息缓存在本地文件中来稍微加快此过程。在 Cygwin 终端中运行这些命令,并对 /etc 进行写访问:

        getent passwd $(id -u) &gt; /etc/passwd
        getent group $(id -G) &gt; /etc/group

        另外,设置/etc/nsswitch.conf如下:

        passwd: files db
        group: files db

        这将限制 Cygwin 联系 AD 域控制器 (DC) 的需要,同时仍允许从 DC 检索其他信息,例如在列出远程目录时。

        在这样做加上启动 cygserver 之后,我的 cygwin 启动时间显着下降。

        【讨论】:

          【解决方案8】:

          正如上面有人提到的,一个可能的问题是 PATH 环境变量包含太多路径,cygwin 将搜索所有这些。我更喜欢直接编辑 /etc/profile,只需将 PATH 变量覆盖到 cygwin 相关路径,例如PATH="/usr/local/bin:/usr/bin"。如果需要,请添加其他路径。

          【讨论】:

            【解决方案9】:

            我编写了一个名为“minimizecompletion”的 Bash 函数,用于停用不需要的完成脚本。

            补全脚本可以添加多个补全规范或具有用于 shell 构建的补全规范,因此仅将脚本名称与 $PATH 中的可执行文件进行比较是不够的。

            我的解决方案是删除所有加载的完成规范,加载完成脚本并检查它是否添加了新的完成规范。根据这一点,通过将 .bak 添加到脚本文件名来禁用它,或者通过删除 .bak 来激活它。对 /etc/bash_completion.d 中的所有 182 个脚本执行此操作会导致 36 个活动和 146 个非活动完成脚本将 Bash 启动时间减少 50%(但应该清楚这取决于已安装的包)。

            该函数还检查未激活的完成脚本,以便在新安装的 Cygwin 软件包需要它们时激活它们。所有更改都可以通过激活所有脚本的参数 -a 撤消。

            # Enable or disable global completion scripts for speeding up Bash start.
            #
            # Script files in directory '/etc/bash_completion.d' are inactived
            # by adding the suffix '.bak' to the file name; they are activated by
            # removing the suffix '.bak'. After processing all completion scripts
            # are reloaded by calling '/etc/bash_completion'
            #
            # usage:  [-a]
            #         -a  activate all completion scripts
            # output: statistic about total number of completion scripts, number of
            #         activated, and number of inactivated completion scripts; the
            #         statistic for active and inactive completion scripts can be
            #         wrong when 'mv' errors occure
            # return: 0   all scripts are checked and completion loading was
            #             successful; this does not mean that every call of 'mv'
            #             for adding or removing the suffix was successful
            #         66  the completion directory or loading script is missing
            #
            minimizecompletion() {
              local arg_activate_all=${1-}
              local completion_load=/etc/bash_completion
              local completion_dir=/etc/bash_completion.d
            
              (
                # Needed for executing completion scripts.
                #
                local UNAME='Cygwin'
                local USERLAND='Cygwin'
                shopt -s extglob progcomp
                have() {
                  unset -v have
                  local PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin"
                  type -- "$1" &>/dev/null && have='yes'
                }
            
                # Print initial statistic.
                #
                printf 'Completion scripts status:\n'
                printf '  total:       0\n'
                printf '  active:      0\n'
                printf '  inactive:    0\n'
                printf 'Completion scripts changed:\n'
                printf '  activated:   0\n'
                printf '  inactivated: 0\n'
            
                # Test the effect of execution for every completion script by
                # checking the number of completion specifications after execution.
                # The completion scripts are renamed depending on the result to
                # activate or inactivate them.
                #
                local completions total=0 active=0 inactive=0 activated=0 inactivated=0
                while IFS= read -r -d '' f; do
                  ((++total))
                  if [[ $arg_activate_all == -a ]]; then
                    [[ $f == *.bak ]] && mv -- "$f" "${f%.bak}" && ((++activated))
                    ((++active))
                  else
                    complete -r
                    source -- "$f"
                    completions=$(complete | wc -l)
                    if (( $completions > 0 )); then
                      [[ $f == *.bak ]] && mv -- "$f" "${f%.bak}" && ((++activated))
                      ((++active))
                    else
                      [[ $f != *.bak ]] && mv -- "$f" "$f.bak" && ((++inactivated))
                      ((++inactive))
                    fi
                  fi
                  # Update statistic.
                  #
                  printf '\r\e[6A\e[15C%s' "$total"
                  printf '\r\e[1B\e[15C%s' "$active"
                  printf '\r\e[1B\e[15C%s' "$inactive"
                  printf '\r\e[2B\e[15C%s' "$activated"
                  printf '\r\e[1B\e[15C%s' "$inactivated"
                  printf '\r\e[1B'
                done < <(find "$completion_dir" -maxdepth 1 -type f -print0)
            
                if [[ $arg_activate_all != -a ]]; then
                  printf '\nYou can activate all scripts with %s.\n' "'$FUNCNAME -a'"
                fi
                if ! [[ -f $completion_load && -r $completion_load ]]; then
                  printf 'Cannot reload completions, missing %s.\n' \
                         "'$completion_load'" >&2
                  return 66
                fi
              )
            
              complete -r
              source -- "$completion_load"
            }
            

            这是一个示例输出和结果时间:

            $ minimizecompletion -a
            Completion scripts status:
              total:       182
              active:      182
              inactive:    0
            Completion scripts changed:
              activated:   146
              inactivated: 0
            
            $ time bash -lic exit
            logout
            
            real    0m0.798s
            user    0m0.263s
            sys     0m0.341s
            
            $ time minimizecompletion
            Completion scripts status:
              total:       182
              active:      36
              inactive:    146
            Completion scripts changed:
              activated:   0
              inactivated: 146
            
            You can activate all scripts with 'minimizecompletion -a'.
            
            real    0m17.101s
            user    0m1.841s
            sys     0m6.260s
            
            $ time bash -lic exit
            logout
            
            real    0m0.422s
            user    0m0.092s
            sys     0m0.154s
            

            【讨论】:

              猜你喜欢
              • 2013-11-07
              • 1970-01-01
              • 2021-11-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多