Win10 家庭版上安装 Hyper-V 的方法

WSL2 需要打开 Hyper-V 的支持,而 Win10 家庭版上没有 Hyper-V 完整组件,有 2 种方法可以开启 Hyper-V:

  1. 在管理员权限的 PowerShell 中执行如下命令行即可开启 Hyper-V 服务:
1
bcdedit /set hypervisorlaunchtype auto
  1. 将以下代码另存为 hyper-v.bat,以管理员权限执行即可安装完整的 Hyper-V 组件:
1
2
3
4
5
6
pushd "%~dp0"
dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hv.txt
for /f %%i in ('findstr /i . hyper-v.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"
del hv.txt
dism /online /enable-feature /featurename:Microsoft-Hyper-V -All /LimitAccess /ALL
pause

参考资料

在 WSL2 环境中使用 Win10 宿主系统中代理的方法

背景

Win10 中 WSL2 环境是一个 HyperV 管理的独立容器,其网络相对宿主系统独立,不像 WSL1 环境那样是和宿主系统网卡直接桥接起来的。WSL2 这样的架构虽然运行效率提高不少,但却无法像 WSL1 那样能直接使用宿主系统提供的代理服务,造成了不少麻烦。

解决方案

解决办法很简单,只要允许代理服务为非 localhost 来源地址服务,然后直接使用宿主系统的 IP 地址就能访问到代理了。以 Ubuntu WSL2 环境访问 Shadowsocks 代理为例,步骤如下:

  1. 允许 Shadowsocks 服务非本地来源地址:
  2. .bashrc 中加入如下的函数方便打开/关闭代理(假设宿主系统 IP 地址为 192.168.175.173,Shadowsocks SOCKS5 代理在 1080 端口):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
proxy () {
# 设置全局代理服务器环境变量
export ALL_PROXY="socks5://192.168.175.173:1080"
export all_proxy="socks5://192.168.175.173:1080"
# 给 apt-get 增加代理配置
echo -e "Acquire::http::Proxy \"http://192.168.175.173:1080\";" | sudo tee -a /etc/apt/apt.conf > /dev/null
echo -e "Acquire::https::Proxy \"http://192.168.175.173:1080\";" | sudo tee -a /etc/apt/apt.conf > /dev/null
# 提示用户当前出口地址
curl myip.ipip.net
}

noproxy () {
# 清除全局代理服务器环境变量
unset ALL_PROXY
unset all_proxy
# 清除 apt-get 代理设置
sudo sed -i -e '/Acquire::http::Proxy/d' /etc/apt/apt.conf
sudo sed -i -e '/Acquire::https::Proxy/d' /etc/apt/apt.conf
# 提示用户当前出口地址
curl myip.ipip.net
}

这样在终端上就可直接用 proxy 开启代理,noproxy 关闭代理。

  1. 对于那些不使用全局代理服务器环境变量的应用,可以安装 proxychains 来实现拦截其网络通信强制走代理的目的:
1
$ sudo apt install proxychains tor

安装完毕后,向 /etc/proxychains.conf 增加如下配置:

1
2
[ProxyList]
socks5 192.168.175.173 1080

然后就能用 proxychains <app_path> <cmdline> 的形式强制应用使用代理了。

参考资料

自动配置网线直连数据传输方案

背景

在很多场景下(手头没有 U 盘、某台电脑 USB 口不可用或没有足够权限安装存储设备驱动等等),我们都需要通过网线直连的方式在两台电脑之间传输数据,这样就要求正确地将两台电脑设置为同一子网下的不同静态 IP。对于电脑知识较为丰富的用户来说此设置过程非常简单,但对于一般用户来说就比较困难了,此时就希望通过程序自动配置好这种网络环境。

分析

这里主要针对 Windows XP 系统自动配置进行分析,因 Vista 以上版本系统已经有了 zeroconf 的实现,有现成的解决方案可用。

网卡地址要么是静态设置,要么是动态 DHCP 分配的。对于静态设置 IP 的情况,拔掉再插上网线后 Windows 系统会监测到媒介状态变化,并自动发送若干源 MAC 为对应网卡 MAC,源/目标 IP 均为当前网卡静态设置 IP 的 ARP 包(称为 Gratuitous ARP 包,用来向子网内其他机器或网关主张自己的静态 IP 所有权),若将网线另一侧机器的 IP 设置为同一子网内的地址,两台机器就能正常通讯了。

Static IP Capture

对于动态 DHCP 分配的情况,拔掉再插上网线后 Windows 系统会自动发送 DHCP 相关请求,要求可能存在的 DHCP 服务器分配 IP,若在网线另一侧机器上运行 DHCP 服务,即可自动设置双机 IP 在同一子网中,也能正常通讯。

DHCP IP Capture

实现

参考实现程序在此。该程序使用 pcapy 模块操作 WinPCAP 在指定网卡(Windows 环境下通过 wmi 模块自动获得首个可用的有线网卡)上监听数据包,通过 impacket 模块解析后根据数据包特征决定使用静态还是动态配置逻辑,最后用 OS 相关方法设置本地监听的有线网卡 IP 后(Windows 环境下通过 wmi 模块实现)完成双机 IP 配置。

参考

Ubuntu 18.04 LTS 中让桌面系统不占用 N 卡显存的方法

背景

使用自己电脑的 N 卡进行 CUDA 加速时都希望能使用全部显存,但在 Ubuntu 18.04 LTS 中安装官方专有驱动后,如果用 nvidia-settings 将 PRIME profiles 设为 Nvidia,会让包括 X 桌面在内的所有图形加速功能都走 N 卡,会占用不少显存。若将 PRIME profiles 改为 Intel,N 卡驱动又不会被加载,无法使用 CUDA 加速。

解决方法

PRIME profiles 是通过 /usr/bin/prime-select 命令完成切换的,PRIME profiles 设为 Intel 时该命令会生成一个驱动黑名单 /lib/modprobe.d/blacklist-nvidia.conf,修改该文件内容将如下几行注释掉:

1
2
3
4
blacklist nvidia
blacklist nvidia-modeset
alias nvidia off
alias nvidia-modeset off

logout 后重新 login 即可实现 X 桌面系统图形加速走集成显卡且 N 卡驱动正常加载的效果,可使用全部显存进行 CUDA 计算。

在 CentOS 6.x 下使用 gcc 4.9.x 的方法

尝试在 CentOS 6.x 下编译使用 PyPy 5.x 时发现其生成的 interpreter.c 使用了 __int128 内置类型,但系统自带的 gcc 4.4.x 并不支持该类型,所以需要按如下步骤装一个 gcc 4.9.x:

1
2
3
4
5
6
7
8
9
10
11
$ sudo yum install centos-release-scl
$ sudo yum install devtoolset-3-gcc devtoolset-3-binutils devtoolset-3-gcc-c++
# 用如下 scl 命令在新 gcc 环境中执行命令,这里执行的是 bash
$ scl enable devtoolset-3 bash
# 进入了新 gcc 环境的 shell,验证一下 gcc 版本
$ gcc --version
gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$

参考:

Java 正则库 bug 一例

一日同事尝试用 Java 正则库匹配某模式总是抛出 String index out of range 异常,百思不得其解。经过一番研究,得到了这样一个最小复现用例:

1
2
3
4
5
6
7
8
9
10
11
import java.util.regex.*;
public class RegexBug {
public static void main(String[] args) throws Exception {
String s = "x"+Character.highSurrogate(0x10000)+Character.lowSurrogate(0x10000);
Pattern p = Pattern.compile("(.)*xx");
Matcher m = p.matcher(s);
if(m.find()) {
System.out.println("matched");
}
}
}

跟踪后发现这是 Java 正则库中的一个实现 bug,用例模式中的 (.)* 会被 Pattern 类解析为 GroupCurly 算子,而该算子内对应贪婪匹配模式的 match0 方法实现有误,当某次匹配不成功需要进行回溯时,其假定 . 匹配的任意字符都是相同长度而每次都减去了最后一次匹配成功的字符长度;然而 Java 内部使用 UTF-16 编码保存字符串,仅有 Unicode BMP 内的字符(即码点小于等于 U+FFFF 的字符)才能用单个 char 表示,Unicode SMP 内的字符(即码点大于 U+FFFF 的字符)都需要用 2 个替代码元表示 1 个字符,当匹配目标字符串中有 BMP/SMP 区字符混合存在的情况且匹配不成功时,回溯逻辑的字符等长假定就会失效,导致字符串下标计算出错而出现访问越界异常。不过同在 Pattern 类中表达非捕获重复算子的 Curly 就没有这个问题,如果将以上用例中的 (.)* 改为 .* 就一切正常了。

经过一番搜索,发现该 bug 已经在 2013-02-01 被人提交给 JDK buglist (见这里) ,JDK 5~7 都受其影响,不过该 bug 已经在 JDK 8 中修复了。考虑到 Java 正则库实现细节非常不堪,也许还会有更多的坑在实践中被发现,定期关注一下 java.util.regex 相关的 bug (见这里) 应该是个好主意……

IPython Notebook 转成 LaTeX 时的中文问题解决方案

从 IPython 1.0 开始 IPython Notebook 就自带了 nbconvert 工具,可以将 notebook 转换为 HTML/LaTeX/Markdown/reStructure/Slides 等多种外部格式,以方便内容的快速重用。从内容展现的一致性上看,将 notebook 转成 LaTeX 再处理为 PDF 文件是表现最好的,因此使用频率也最高,然而当 notebook 中出现中文时,默认生成的 LaTeX 文件无法进行支持,会导致最终生成的 PDF 文件中中文部分都是空白。经过实验,对生成的 LaTeX 文件进行如下手工修改可以快速修正此问题:

  • 首先用 nbconvert 将 notebook 转为 LaTeX 文件:
1
ipython nbconvert --to latex example.ipynb
  • 在生成的 LaTeX 文件里 \documentclass 行之后增加如下内容:
\usepackage{fontspec, xunicode, xltxtra}
\usepackage[slantfont, boldfont]{xeCJK} % 允许斜体和粗体
\setCJKmainfont{WenQuanYi Micro Hei} % 默认中文字体
\setCJKmonofont{WenQuanYi Micro Hei Mono} % 中文等宽字体
\setmainfont{TeX Gyre Pagella} % 英文衬线字体
\setmonofont{Monaco} % 英文等宽字体
\setsansfont{Trebuchet MS} % 英文无衬线字体
\punctstyle{kaiming} % 开明式标点格式: 句末点号用全角, 其他半角
  • 注释掉生成的 LaTeX 文件里 \usepackage{babel} 这一行
  • (可选) 将生成的 LaTeX 文件里 \date{...}\author{...} 中的内容改为需要的日期和作者信息
  • 最后用支持 UTF-8 的 xelatex 命令直接将修改后的 LaTeX 文件转为 PDF 即可:
1
xelatex example.tex

另外,最后转 PDF 时若出现类似这样的错误:

> Runaway argument?
> ! File ended while scanning use of @writefile.
>
> par
> l.110 begin{document}

可以直接删除 *.aux 文件再重新转换即可。

OpenJDK 中查看 JIT 编译结果的方法

背景

大家都知道 JVM 有 HotSpot 引擎可以对热代码路径进行有效的 JIT 优化,大幅度提升计算密集代码的性能,但 HotSpot 对自己编写的 Java 代码时进行了哪些 JIT 优化?优化后生成的 native code 长什么样?知道了这些信息之后,才能有的放矢地改进自己的代码。

初步查看 JIT 工作情况

以如下 Java 代码为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Accumulator.java
public class Accumulator {
public static void main(String[] args) {
int max = Integer.parseInt(args[0]);
System.out.println(addAllSqrts(max));
}

static double addAllSqrts(int max) {
double accum = 0;
for(int i = 0; i < max; i++) {
accum = addSqrt(accum, i);
}
return accum;
}

static double addSqrt(double a, int b) {
return a + sqrt(b);
}

static double sqrt(int a) {
return Math.sqrt(a);
}
}

一般情况下,我们只需要知道 JIT 内联函数是否满足自己的期望即可,可以用如下命令行达到这个目的:

1
2
javac Accumulator.java
java -XX:+PrintCompilation Accumulator 50000

注意默认情况下一个方法至少要被调用 10k 次以上才可能被 JIT 优化,故这里运行时给出的循环参数也要大于该值,输出结果如下:

     61    1             Accumulator::addSqrt (7 bytes)
     62    2             Accumulator::sqrt (6 bytes)
     67    3 %           Accumulator::addAllSqrts @ 4 (23 bytes)
...

各个数据列的含义分别为:

列1 列2 列3 列4 列5
自 JVM 启动到尝试优化的时间(ms) 尝试优化的顺序(从 1 开始) 特殊方法标识[1] 方法全名及生成代码长度 若有 "made not entrant" 或 "made zombie" 字样就表明没有进行优化

用如下命令行可以更多地了解内联优化的实际情况以及优化发生的级别:

1
java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+TieredCompilation Accumulator 50000

输出结果如下:

     50    1       3       java.lang.String::equals (81 bytes)
     51    2       3       java.lang.Object:: (1 bytes)
     53    3     n 0       java.lang.System::arraycopy (native)   (static)
     53    4       3       java.lang.String::charAt (29 bytes)
                              @ 18  java/lang/StringIndexOutOfBoundsException:: (not loaded)   not inlineable
     54    6       3       java.lang.String::hashCode (55 bytes)
     55    5       3       java.lang.String::indexOf (70 bytes)
                              @ 66   java.lang.String::indexOfSupplementary (71 bytes)   callee is too large
     56    7       3       java.lang.String::length (6 bytes)
     59    8       3       java.lang.Math::min (11 bytes)
     61    9       3       Accumulator::addSqrt (7 bytes)
                              @ 2   Accumulator::sqrt (6 bytes)
                                @ 2   java.lang.Math::sqrt (5 bytes)   intrinsic
     62   10       3       Accumulator::sqrt (6 bytes)
                              @ 2   java.lang.Math::sqrt (5 bytes)   intrinsic
     64   11       4       Accumulator::addSqrt (7 bytes)
...

这里新增的第 3 列代表了优化发生的级别(Tier 0~4,其中 Tier 0 为 Interpreter,Tier 1~3 为 client 模式的优化策略,Tier 4 为 server 模式的优化策略)

查看 JIT 生成的汇编代码

为了更好地了解 JIT 优化效果,查看生成的 native code 是很有必要的,不过 OpenJDK 并没有自带反汇编插件 hsdis-*.so ,需要手工安装一下该插件。一般来说有这样几种安装方法:

  1. 对于 Ubuntu 系的发行版,可以直接用命令 sudo apt-get install libopenjdk-hsdis 安装该插件
  2. 用别人从 OpenJDK 中扣出来的代码自行编译安装,具体见 这里
  3. 按照 HotSpotInternals - PrintAssembly 中的描述自己从 OpenJDK 源码树中编译

安装完成后,即可用一下命令输出所有 JIT 后函数的 native code 汇编代码:

1
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Accumulator 50000

输出结果如下:

Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Loaded disassembler from hsdis-amd64.so
Decoding compiled method 0x00007ffd9905ee50:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} 'addSqrt' '(DI)D' in 'Accumulator'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rsi       = int
  #           [sp+0x20]  (sp of caller)
  0x00007ffd9905ef80: sub    $0x18,%rsp
  0x00007ffd9905ef87: mov    %rbp,0x10(%rsp)    ;*synchronization entry
                                                ; - Accumulator::addSqrt@-1 (line 16)
  0x00007ffd9905ef8c: vcvtsi2sd %esi,%xmm1,%xmm1
  0x00007ffd9905ef90: vsqrtsd %xmm1,%xmm1,%xmm1
  0x00007ffd9905ef94: vaddsd %xmm1,%xmm0,%xmm0  ;*dadd
                                                ; - Accumulator::addSqrt@5 (line 16)
  0x00007ffd9905ef98: add    $0x10,%rsp
  0x00007ffd9905ef9c: pop    %rbp
  0x00007ffd9905ef9d: test   %eax,0xc5f905d(%rip)        # 0x00007ffda5658000
                                                ;   {poll_return}
  0x00007ffd9905efa3: retq
  0x00007ffd9905efa4: hlt
  0x00007ffd9905efa5: hlt
...

还可以增加选项 -XX:PrintAssemblyOptions=hsdis-print-bytes 额外输出指令实际字节序列:

1
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:PrintAssemblyOptions=hsdis-print-bytes Accumulator 50000

输出结果为:

Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Loaded disassembler from hsdis-amd64.so
Decoding compiled method 0x00007f482105ee50:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} 'sqrt' '(I)D' in 'Accumulator'
  # parm0:    rsi       = int
  #           [sp+0x20]  (sp of caller)
  0x00007f482105ef80: sub    $0x18,%rsp         ;...4881ec18 000000
  0x00007f482105ef87: mov    %rbp,0x10(%rsp)    ;...48896c24 10
                                                ;*synchronization entry
                                                ; - Accumulator::sqrt@-1 (line 20)
  0x00007f482105ef8c: vcvtsi2sd %esi,%xmm0,%xmm0  ;...c5fb2ac6
  0x00007f482105ef90: vsqrtsd %xmm0,%xmm0,%xmm0  ;...c5fb51c0
                                                ;*invokestatic sqrt
                                                ; - Accumulator::sqrt@2 (line 20)
  0x00007f482105ef94: add    $0x10,%rsp         ;...4883c410
  0x00007f482105ef98: pop    %rbp               ;...5d
  0x00007f482105ef99: test   %eax,0xc048061(%rip)        # 0x00007f482d0a7000
                                                ;...85056180 040c
                                                ;   {poll_return}
  0x00007f482105ef9f: retq                      ;...c3
[Exception Handler]
[Stub Code]
  0x00007f482105efa0: jmpq   0x00007f482105eda0  ;...e9fbfdff ff
                                                ;   {no_reloc}
[Deopt Handler Code]
  0x00007f482105efa5: callq  0x00007f482105efaa  ;...e8000000 00
  0x00007f482105efaa: subq   $0x5,(%rsp)        ;...48832c24 05
  0x00007f482105efaf: jmpq   0x00007f4821038f00  ;...e94c9ffd ff
                                                ;   {runtime_call}
  0x00007f482105efb4: hlt                       ;...f4
...

上述命令总是输出所有方法的 JIT 结果,当结果很多时难以看出重点,可以将 -XX:+PrintAssembly 选项改为 -XX:CompileCommand=print,*class.method 来限制输出哪些方法,例如:

1
java -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand='print,*Accumulator.sqrt' Accumulator 50000

这样输出结果就变得很少了:

CompilerOracle: print *Accumulator.sqrt
Java HotSpot(TM) 64-Bit Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output
Compiled method (c2)      62    2             Accumulator::sqrt (6 bytes)
 total in heap  [0x00007fcacc1d18d0,0x00007fcacc1d1a98] = 456
 relocation     [0x00007fcacc1d19f0,0x00007fcacc1d19f8] = 8
 main code      [0x00007fcacc1d1a00,0x00007fcacc1d1a20] = 32
 stub code      [0x00007fcacc1d1a20,0x00007fcacc1d1a38] = 24
 oops           [0x00007fcacc1d1a38,0x00007fcacc1d1a40] = 8
 scopes data    [0x00007fcacc1d1a40,0x00007fcacc1d1a50] = 16
 scopes pcs     [0x00007fcacc1d1a50,0x00007fcacc1d1a90] = 64
 dependencies   [0x00007fcacc1d1a90,0x00007fcacc1d1a98] = 8
Loaded disassembler from hsdis-amd64.so
Decoding compiled method 0x00007fcacc1d18d0:
Code:
[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} 'sqrt' '(I)D' in 'Accumulator'
  # parm0:    rsi       = int
  #           [sp+0x20]  (sp of caller)
  0x00007fcacc1d1a00: sub    $0x18,%rsp
  0x00007fcacc1d1a07: mov    %rbp,0x10(%rsp)    ;*synchronization entry
                                                ; - Accumulator::sqrt@-1 (line 20)
  0x00007fcacc1d1a0c: vcvtsi2sd %esi,%xmm0,%xmm0
  0x00007fcacc1d1a10: vsqrtsd %xmm0,%xmm0,%xmm0  ;*invokestatic sqrt
                                                ; - Accumulator::sqrt@2 (line 20)
  0x00007fcacc1d1a14: add    $0x10,%rsp
  0x00007fcacc1d1a18: pop    %rbp
  0x00007fcacc1d1a19: test   %eax,0x9ea55e1(%rip)        # 0x00007fcad6077000
                                                ;   {poll_return}
  0x00007fcacc1d1a1f: retq
[Exception Handler]
[Stub Code]
  0x00007fcacc1d1a20: jmpq   0x00007fcacc1ceda0  ;   {no_reloc}
[Deopt Handler Code]
  0x00007fcacc1d1a25: callq  0x00007fcacc1d1a2a
  0x00007fcacc1d1a2a: subq   $0x5,(%rsp)
  0x00007fcacc1d1a2f: jmpq   0x00007fcacc1a8f00  ;   {runtime_call}
  0x00007fcacc1d1a34: hlt
  0x00007fcacc1d1a35: hlt
  0x00007fcacc1d1a36: hlt
  0x00007fcacc1d1a37: hlt
OopMapSet contains 0 OopMaps

Tips: 用如下命令能看到 JVM 支持的所有内部选项及其当前值,可以用于排查环境问题或进行细节调整:

1
java -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version

参考资料


  1. ! 表示是异常处理函数, n 表示是 native 函数, % 表示进行了 OSR(On-Stack Replace) 优化 ↩︎

在 Ubuntu 下用 adb 连接京崎 Tooky T85 手机

获取手机的 vendor id

用 USB 线连接手机,执行 lsusb 命令后可以看到如下内容:

1
Bus 001 Device 010: ID 4bb0:30d2

那么 vendor id 就是 0x4bb0

增加识别手机设备的 udev 规则

编辑 /etc/udev/rules.d/51-android.rules ,加入如下内容:

1
SUBSYSTEM=="usb", ATTRS{idVendor}=="4bb0", MODE="0666", GROUP="plugdev"

这里 ATTRS{idVendor} 条件之后填的就是上面获得的 vendor id。

添加完毕后执行 sudo reload udev 重新加载新的 udev 规则,然后重新插拔一次手机

让 adb 识别手机设备

做完以上步骤后 adb devices 仍然不能识别手机,因为京崎的 vendor id 并没有放在 Android SDK 的默认厂商列表中。

这里需要修改 ~/.android/adb_usb.ini ,在最末尾增加 16 进制格式的 vendor id,这里增加后的内容就是:

1
2
3
4
5
# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.
# USE 'android update adb' TO GENERATE.
# 1 USB VENDOR ID PER LINE.

0x4bb0

注意运行 android update adb 会清空该文件,慎用此命令!

最后执行如下命令重启 adb server 即可:

1
2
3
adb kill-server
adb start-server
adb devices

看到 adb devices 显示如下字样就是成功了:

1
2
List of devices attached
0123456789ABCDEF device

完整的 chroot 过程

直接简单地运行 chroot <new-root-dir> 不足以正常运行一些程序,因为很多程序需要访问 procfssysfsdevfs 等特殊的子目录,直接 chroot 更改根目录后这些子目录没有被挂载,内容都是空,导致这些程序出现问题。

完整的 chroot 前准备过程可以通过下列命令完成:

1
2
3
4
5
6
cd <new-root-dir>
mount -t proc proc proc/
mount -t sysfs sys sys/
mount -o bind /dev dev/
mount -t devpts pts dev/pts/
cp -L /etc/resolv.conf etc/resolv.conf

上述命令将宿主系统的 procfssysfsdevfs 等特殊目录挂载到新的根目录下对应子目录上,并设置 chroot 系统使用宿主系统的域名解析配置,最后用以下命令即可完成 chroot 操作:

1
chroot <new-root-dir> /bin/bash

参考: