TrueNAS Core已更新至FreeBSD 13,其两款免费产品Scale和Core也已转至OpenZFS。考虑到之前一直使用SolarisZFS,可能会对OpenZFS的tuning不够熟悉,又想获得Linux的一些优势,因此使用了TrueNAS Scale。但体验不佳,后来换成了NixOS,这篇文章主要是讲述40GB网卡在TrueNAS下的使用,Infiniband和RDMA。
早些时候海淘拼车买了Mellanox SX6036,疫情闲赋在家就拿出来换了一套猫扇,NAS和Proxmox VE连接Mellanox SX6036,再连接家里的10Gbps交换机Mikrotik CRS312,至此把40Gbps网络并入整个家庭内网。
Mellanox SX6036交换机。闲鱼差不多2000-3000左右,但是海淘Ebay大概在600-1000左右,邮费非常贵,如果有拼车最好了,需要授权才能使用以太网ETH模式,可以自己算号得到授权的。
HP 544+ 40/56Gbps ETH/IB QSFP+网卡。闲鱼加转接卡一起卖大概在80-120之间,便宜一般来说带来了很多坑。
AOC 40Gbps QSFP网线。这个我闲鱼买的橘色的细线,价格大概在30-50元每米,比较方便理线,如果是DAC铜缆价格会更加便宜,但实在是太粗难以梳理。对了,这类线都带了模块的,你不需要再另购入模块。如果要用IB功能,线就有点贵了,自行搜索。
Mellanox MAM1Q00A-QSA-655874-B21 QSFP to SFP+ 转接头。闲鱼价格在100-150之间。这个转接头方便你把10Gbps的SFP+DAC线的其中一个头转换成QSFP,从而将一台10Gbps交换机和40Gbps交换机相连。
如果购买了MPO多模线的用户,可以考虑购买Finisar的FTL414QB2N-E5这个模块,65元左右,此模块支持ETH并且似乎是唯一支持56G Infiniband的多模模块,价格非常便宜。
如果你使用单模40GB模块,你可能无法获得任何Infiniband的收益,首先我没见过这样的模块,其次家用单模40GB模块请一定要加单模光纤衰减器,不然就是几个月烧毁,跟换灯泡差不多吧。也许单模是可以用到ROCE这种RDMA的,但即便是用多模ETH ROCE RDMA效果与Infiniband的RDMA还是差的有点多。ROCE需要交换机支持PFC ECN,而sx6036并不支持,性价比极低。
我把NAS机箱的四个8CM风扇卡槽换成了三个12CM风扇卡槽,并购买了追风者T30风扇,以解决因极高温度导致的硬盘过热问题。新的12CM风扇效果很好,因为它们具有兼具PWM和广泛转速范围的特点,而且噪音很低。此外,我还找淘宝上用PETG材质3D打印的卡槽来更换旧的卡槽。打印文件购买链接
Chelsio T580 40Gbps 网卡。这张卡我之前800左右买的,闲鱼现在我看有的1500,有的1000。此卡是我见过在没有RDMA情况下延迟最低的卡,并且能在MacOS下驱动。
在写这篇文章的时候MacOS突然更新支持了Mellanox ConnectX-4代及以上的eth网卡的驱动,看了一下匹配非常宽泛,感觉200G的都支持,但不确定是否带有ROCE2;同时也支持了intel的几张40G以上的网卡。如果只是MacOS使用的话,推荐mcx-4131a。
追风者T30风扇需要拨动风扇中央的开关到Advance模式才能达到3000转。
早前我购买了超微SAS3的背板和SAS3的9300-8i HBA卡,这样的话可以插几块SAS的SSD,如果你的前置面板只是用来插6GB的sata机械硬盘,没有必要购买24盘位的12GB的背板以及SAS3的HBA卡。HBA卡的坑我写在另一片文章中。但处理4K小文件多队列因SATA协议限制其实效果并不理想,如果无法购买或者硬盘的RAID无法更好的提高你的Queue Depth,可以考虑使用一到两个NVME SSD组成Raid0,并通过zfs send
脚本定期将Raid0的数据集备份到主池的HDD中。
需要警惕那些仿冒机箱,避免购买到非原装或浪潮的846机箱,因为上述提到的3D打印文件可能并不适用于这些机箱。同时,也要注意辨别机箱是否为原装,是否存在仿冒电源背板,这在闲鱼非常泛滥。
我的NAS RAID使用了两组10盘Z2,并将这两组Z2 Stripe为一组,可以说是Raid60。此外,我还放了两个ZFS热备盘以及两个SAS SSD在剩余的盘位。
管理交换机的方法有两种:一是通过RJ45网线连接交换机的管理口后台IP进行管理,二是通过Console线(RJ45 to USB)连接进入向导模式进行管理口IP等的设置。后者具有更高的权限,能做更多的设置。
使用SFP光缆和购物清单中的SFP to QSFP转换头连接10Gbps交换机和SX6036时,需要在后台的WEB UI中手动协商连接口的速率至10Gbps,否则无法通信。
如果使用QSFP一分四SFP线,则需要关闭其中一个口才能使用,并在ssh管理界面中标注4口为1分4。请查阅说明书来找到关闭对应口的方法,推荐购买Console线进行管理。
尽量不要使用单模40G模块,SX6036只有1、3、33、35口支持单模40G,并且需要在ssh中输入fae cable-stamping-unlock 40g_lr4
来开启单模支持。如之前所提示,建议不要购买单模模块。
此款交换机有一个可灵活拆卸的风扇盒,风扇盒中有4个4CM的风扇;电源中有一个4CM的风扇,所有的风扇都是PWM的。
我购买了5个猫头鹰NF-A4x20 FLX 3pin风扇,因为我觉得只要接地线和12v电源两个pin就够了,这风扇满转都听不到声音,更不会有心思登录到SX6036的后台通过CLI更改风扇转速。
原装风扇接口与普通主板上4pin接口不同,风扇盒的4pin定义如下:
颜色 | 定义 |
---|---|
红 | +12v |
蓝 | RPM |
黄 | PWM |
黑 | Black GND |
我只把FLX版本猫头鹰的红黑两根针接上去了;如果你购买了PWM版本的猫头鹰A4风扇,这款猫扇的4pin定义如下:
颜色 | 定义 |
---|---|
蓝 | PWM |
绿 | RPM |
黄 | +12v |
黑 | Black GND |
更换PSU电源风扇请自行考虑安全性!!个人主观认为300W的电源换个风扇问题不大,不做参考依据!电源上4pin定义如下:
颜色 | 定义 |
---|---|
蓝 | PWM |
黄 | RPM |
红 | +12v |
黑 | Black GND |
猫头鹰风扇和PSU的螺丝孔大小不一样,我用自带的橡皮固定了,见效果图。
此电源好像有其他几个版本,最好用万用表自己对照一下。
若使用PWM的风扇可以在SSH或者Console界面输入如下命令来控制转速:
enable #进入高级模式fae mlxi2c set_fan set_fan /FAN/FAN 1 65 #1代表FAN 1; 65为转速fae mlxi2c set_fan set_fan /PS1/FAN 1 65 #PS1代表第一个电源,此交换机可以插两个冗余
更换风扇后,无论你接2针还是4针,sx6036的指示灯都变红,不影响使用。如果有强迫症,可在淘宝买风扇转速欺骗器,十元左右一个。
效果图:
待传…..
说一下HP 544+和Chelsio T580这两张卡。
如果你不太想折腾,请购买原装原厂的Mellanox卡,最好是还在维护的Mellanox 4代以上。
我买了两张544+,说明一下情况,我的NAS和计算是彻底分开的两台机器,NAS是真的只做存储,而乱七八糟玩的是一台Proxmox VE,因此买两张分别给两台机器。
HP544+这张卡是Mellanox CX354a-FCCT的套娃卡,并不能刷入原装固件。HPE官网最新的固件在TrueNas使用的话,必须把此网卡的SR-IOV功能关闭,不然可能连开机都开不了。
apt update && apt install mstflintmstconfig -d 03:00.00 set SRIOV_EN=0
通过BIOS Legacy模式在开机引导此固件Rom的时候也可以直接进入Setup界面关闭SR-IOV,这样无需安装别的软件来设置
暂时不懂怎么在TrueNAS下修改一些内核相关的东西,并且SR-IOV在我的TrueNAS主板上不是必需品,如果你会用,则刷入下面的老固件。
我在Proxmox VE中习惯使用SR-IOV,请到HPE官网下载并刷入fw-ConnectX3Pro-rel-2_40_5072-764285-B21_Ax-CLP-8025-UEFI-14.11.42-FlexBoot-3.4.747.bin
这个老版本固件。
Mellanox的驱动一般是分为两种的,一种是系统自带的驱动,比如Linux内核中自带的,还有一种是Nvdia官方提供的驱动叫OFED。OFED对各种场景有更好的调优,但因其out-of-tree的原因,你每次升级内核都需要重新编译驱动。官方的OFED已经无法在Linux 5以上的内核编译出来,毕竟这张卡已经EOL了,我看了一下要编译几乎是等于重构,所以没啥办法了,家用的话使用kernel tree上的驱动足以。
已使用kernel tree上自带的驱动并使用SR-IOV,只是发现了windows虚拟机上的VF有个小问题,下面给了解决方案了,别的没碰到啥问题。
安装mstflint
,以Proxmox VE为例:
apt update && apt install mstflint
通过lspci
获得Mellanox网卡的设备号并设置,例如:
lspci |grep Mellanox03:00.0 Ethernet controller: Mellanox Technologies MT27520 Family [ConnectX-3 Pro]mstflint -d 03:00.00 query #获得固件信息mstflint -d 03:00.00 -i 固件路径 b #刷入固件mstconfig -d 03:00.00 query #获取设备设置信息mstconfig -d 03:00.00 set SRIOV_EN=1 NUM_OF_VFS=8 #开启SR-IOV以及设置VF总数为8个;确保主板已开通sr-iov,Proxmox VE已开通虚拟化等设置mstconfig -d 03:00.00 set LINK_TYPE_P1=ETH #设置p1端口为ETH模式
创建一个/etc/modprobe.d/mlx4_core.conf填入:
options mlx4_core port_type_array=1,2 num_vfs=3,4,0 probe_vf=1,4,0
0
。具体可以参考这个文档。设置完后记得update-initramfs -u
后重启。
一般来说更新的是当前系统运行的内核,
update-initramfs -u -k <kernel version>
来更新指定的内核,或者update-initramfs -u -k $(uname -r)
来更新当前使用的内核。
如果你完全不知道,也尽可能谨慎使用update-initramfs -u -k all
这个指令去更新所有内核,这不是一个靠谱的操作。
创建一个shell,assign mac地址以及vf link up:
nano /opt/sr-iov.sh
内容如下
#!/bin/baship link set dev enp2s0 mtu 9000 #这个你看自己需求是否要设置mtu。如果pf是1500,那么vf设置9000会有问题。ip link set dev enp2s0 vf 0 mac ae:51:1a:2b:c0:00ip link set dev enp2s0 vf 1 mac ae:51:1a:2b:c0:01ip link set dev enp2s0 vf 2 mac ae:51:1a:2b:c0:02ip link set dev enp2s0 vf 3 mac ae:51:1a:2b:c0:03ip link set enp2s0 upip link set enp2s0v0 upip link set enp2s0v1 upip link set enp2s0v2 upip link set enp2s0v3 up
记得chmod +x /opt/sr-iov.sh
创建systemd unit开机运行
nano /etc/systemd/system/mlx-sr-iov.service
内容如下
[Unit]Description=Script to enable MLX SR-IOVAfter=network.targetBefore=pveproxy.service[Service]Type=oneshotExecStart=/opt/sr-iov.sh[Install]WantedBy=multi-user.target
创建完执行systemctl start mlx-sr-iov.service
则看到效果了;执行systemctl enable mlx-sr-iov.service
则开机自动运行
注意给虚拟机增加一些延迟启动的时间。这个脚本我设置了在网络初始化完成后运行,但是在
pveproxy.service
(PVE的WEBUI等服务)之前执行。因为如果你的WEBUI套用了VF作为虚拟网桥,则必须要考虑这个启动顺序。
如果不是标准的服务器主板,是不具备ARI forwarding
功能的,没有此功能的主板在一张网卡上无法分出超过7个以上的VF的,当然多张卡可以叠加上去。这里指的是真正的服务器主板,不包括C246那些魔改家用主板的芯片组。你在支持的主板中开启此功能,并且在系统中通过lspci -vvv | grep ARIFwd
查看是否已经开启。
如果要用Windows的VM,则尽可能使用Windows的LTSC版本。在PVE内核大于Linux 5.3的情况下(我有点记不清了),使用3代网卡的VF直通到Windows你要对Proxmox的kernel进行patch,不然Windows Guest的设备管理器里,这个VF会报错code 43,参考这里。 我把我自己patch好的放在了这里。我自己用了几个月没啥毛病才放出来了,别的奇奇怪怪的功能未经测试,我已经把kernel patch的文件放在里面了,你也可以根据自己需求定制内核。下载后通过dpkg-i *.deb
安装,并且pin住这个内核。在使用这种自行修改的内核后,请非常注意apt upgrade升级的内容。
proxmox-boot-tool kernel list #查看pve下已经有的内核proxmox-boot-tool kernel pin 5.xxx-pve #pin住内核
说实在的,Windows我都懒得用。如果对端服务器不是Windows server,那么Windows client几乎是无法跑40/56Gbps,文件管理器是串行的,SMB是单线程的,ISCSI在较新的windows 10版本中也是单线程的,我认为在windows下直接用Virtio的虚拟网卡是几乎没有什么区别的。
Linux 6.1的ksmbd内核模块似乎支持了Linux作为服务端来连接Windows客户端达到多线程以及SMB-Direct(RDMA),没时间测试。
LXC很方便通PF或者VF。通过修改/etc/pve/lxc/xxx.conf文件,加入你想要通过去的VF即可了:
lxc.net.1.type: physlxc.net.1.flags: uplxc.net.1.link: enp3s0v0lxc.net.1.hwaddr: ae:54:1c:2b:c0:00lxc.net.1.name: eth11lxc.net.0.type: physlxc.net.0.flags: uplxc.net.0.link: enp3s0v1lxc.net.0.hwaddr: ae:54:1c:2b:c0:01lxc.net.0.name: eth2####下面这部分是我的NixOS LXC使用proxmox主板的核心显卡。lxc.cgroup2.devices.allow: c 226:0 rwmlxc.cgroup2.devices.allow: c 226:128 rwmlxc.cgroup2.devices.allow: c 29:0 rwmlxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dirlxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=filelxc.autodev: 1lxc.hook.autodev: sh -c "chmod -R 666 /dev/dri/card*; chmod -R 666 /dev/dri/render*"
因已有中文教程,自己也没啥时间写,请参照这里
Chelsio对FreeBSD的支持特别好,但在linux下的驱动些许麻烦,随手写一下在 Proxmox VE 7下面驱动此卡的方法:
从官网下载最新版的tar.gz压缩包
解压压缩包tar -zxvf ChelsioUwire-x.xx.x.x.tar.gz
安装rpm sudo apt install rpm
安装pve的linux headers
sudo apt install pve-headers-`uname -r`
确认你kernel sources的路径,应该是usr/src/linux-headers-x.xx.xx-x-pve/
回到刚才解压缩好的文件夹里,修改Makefile:
DEBIAN := 1PDEB := 1DISTRO := Debian
在当前目录编译驱动 make KDIR=/usr/src/linux-headers-x.xx.xx-x-pve/
完成后你可以使用此卡的高级功能 e.g.SR-IOV, etc.
每次更新PVE内核你都需要重新编译驱动。
这张卡直接在官网下载驱动安装就行,如果你希望干净一点安装,在MacOS下通过命令pkgutil --expand-full [pkg] [dir]
获得PKG内的KEXT。其中cxgb.kext是Chelsio T580的驱动,cxgb3.kext是T3xx系列的驱动。
这个官方驱动只支持到big sur。
这个patched驱动支持到Ventura,在MacOS下你应该要关闭SIP来加载这张过期的签名,或者如果是Hackintosh的话,使用Opencore加载这个驱动
这个卡超级超级超级热,拆卸此卡时小心灼伤,温度过高会导致掉光模块或者掉卡。一个风扇不够,你需要2-4个风扇全面覆盖它才可以;条件允许,水冷。
对于FreeBSD而言,SolarisZFS和OpenZFS使用上差别不是那么大。但是在Linux上实现OpenZFS我暂时没明白,OpenZFS的实现固然很有用,但是在BSD里物理硬盘与VDEV是依赖于Geom去实现的,Geom提供providers
给OpenZFS来做成Vdevs,则完成整一套的虚拟-物理设备之间的通信。而Linux是用mdadm来实现的,这个我也在慢慢消化中。
Ashift则是用来设置物理硬盘的块大小 (block size)。常见的硬盘块大小有512字节、4KB、8KB等。在创建存储池时,Ashift的设置会影响存储池的性能和空间利用率。
在老的FreeNAS版本中,默认给的是Ashift=9,代表块大小为512B。这个设置方式的问题是,对于4KB大小的文件,硬盘需要切割为8份才能存储。这样会对服务器带来一定的压力,但可以尽量减少空间损失。而如果设置成更大的块大小,则会浪费空间。因此,现代的TrueNAS默认给的是Ashift=0,代表自动辨别块大小。
书籍中建议的Ashift=12,代表块大小为4KB。这样设置会尽力减小服务器压力,而浪费的空间可以通过压缩来减少损失。对于一些新的SSD,块大小可能已经达到8K,此时应将Ashift设置为13。通过如下命令可以确认自己的Ashift设置:
zpool get all PoolName |grep ashift
为什么是9和12? 2^9=512;2^12=4096。一些新的SSD已经到了8k,则ashift=13
Meta Vdevs是近几年新出的一个功能。这个Vdev并不是一个缓存,而是一个Storage,因此,你需要确保这个Vdev有较高的安全性,比如两个optane硬盘组成一个Mirror甚至更大的冗余,如果这组Vdev坏了,在同一个Pool下的机械池数据也同样丢失。请不要想着试试看能不能用
,操作尤其是在raidz的阵列上几乎不可逆。
Meta Vdevs解决了两个问题。
Metadata
放在了Optane上,则至少可以减少storke的时间,也能收获到更多的IO queues,这对小文件多线程是很有帮助的。Block size
文件存于Optane中,而没有命中规则的文件,则放入原来的机械存储池中。Record size
,比如我的是128K,然后我在下面的Metadata (speical) small block size
中设置一个值,比如是64k。当一个文件≤ 64k,则自动存入Optane中。当Metadata (speical) small block size
=Record size
,则所有文件全会存入Optane中,显然这样做,还不如单独用Optane建立一个池。Meta Vdevs
的手册时,我思考了如下两个问题:根据手册的说法,Meta Vdev存满了,则会根据算法自动将使用频率少的文件回落到机械硬盘。
我们可以看一下你之前的一些比较具有代表性的小文件池来观察估算:
cd /mnt/poolname/test #cd到你想测试的dataset下find . -type f -print0 | xargs -0 ls -l | awk '{ n=int(log($5)/log(2)); if (n<10) { n=10; } size[n]++ } END { for (i in size) printf("%d %d\n", 2^i, size[i]) }' | sort -n | awk 'function human(x) { x[1]/=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf("%3d%s: %6d\n", a[1],substr("kMGTEPYZ",a[2]+1,1),$2) }'
看一下你的这个dataset下文件的整个block size分布,按比例预估。
另外通过如下命令也可以看到整个pool的情况,除掉L0 Total
外的总和就是你的metadata目前所需要的空间。具体的一些估算以及应用方法请参考这里。当然,我们讨论的文件大小都是通过compression压缩后的大小。非Optane的SSD作为Meta Devs你需要手动开启trim on
。
zdb -LbbbA -U /data/zfs/zpool.cache <poolname>
最后有个小问题,metadata对于Zvol是无用的。对ZFS来说,往zvol写入多少文件都只是block size
大小完全一样的存在,你可以收益到的只是Metadata对Zvol的加速。这个问题的讨论请参考这里。如果你是zvol用的频繁并且还需要更极致的zvol性能,参考章节6.ZFS Send。
一般来说,开启了dedup功能的dataset能做到文件去重复的作用,也就是多个一模一样的文件只占用一份文件的容量。以前用内存来做,代价是用内存换硬盘空间,现在即便是可以用Dedup Vdev来换机械硬盘空间,也不推荐。Meta Vdevs存储了dedup datasheet,同样做到了dedup功能,不如用meta dev。
开启dedup的dataset是不可逆的,请把已经开启dedup的dataset通过zfs send
到新的dataset里,再删除旧的。名字的话通过zfs rename
改回来。
如果说起ZFS的写缓存,往往会被误导到使用Slog,这里可以明确指出,Slog不会让你的写入速度超过当你dataset设置成async的速度的。ZFS在写入时先产生ZFS intent log(ZIL),也就是未来会写入的文件的一个操作日志,在5秒为预期的时间内,文件会先进入内存(TXG),再写入硬盘,当整个文件还没落盘,文件不具备可读性以及可执行性的情况下,我们称之为脏数据(dirty data),最后等落盘写入,整个操作完成,而这个文件通过判断可能会继续留在内存的ARC里作为未来的读缓存。我们看下来整个过程,其实ZFS并不具备传统意义上的写缓存,而是一个缓冲区为未来的读取做准备的。
另一方面这样的写入是异步操作的,在更严谨的环境中,需要同步写入(sync write)。同步写入的速度会非常慢,通过增加Slog,比如Optane这种写入快,安全性高的硬盘去帮我们提高写入速度,当然,当Optane开启了FUA bit作为Slog后,速度也不会太好看的。
总之,Slog只是保证了你文件的同步写入,家用情况,UPS足矣。
L2arc是ZFS的读缓存。在访问ZFS时,一些常用文件会放置于内存ARC中,以便快速读取,而内存的容量是相对有限且昂贵的,因此增加一个L2arc可以让你有更大的空间去缓存常读的文件。一般来说l2arc的容量需要一部分的arc来带动,官方推荐的是你可用arc的4倍左右的容量就足够了,如果买的太大,则需要tuning你的arc.max或者l2arc.max稍作控制。l2arc不需要任何冗余,你只要买的足够快就可以了。
如果你的arc仍然在growing,请不要添加任何l2arc,这样只会有反作用,因为内存的速度永远比SSD快,但是如果你的zfs是跑在网络上,比如我的网络带宽是40g,则l2arc的速度只要达到这个速度就够了。请通过arc_summary
来观察自己的内存arc做判断依据。
在设置了相应的l2arc后,也推荐通过zfs set secondarycache=metadata DatasetName
把metadata都给过去,同时请关注一下这个问题。
TrueNAS通过WebUI建立的pool非常平庸,但想了想也不知道写啥,默认的ashift=0,意味着是让系统自己判断,这里最好是自己在创建pool的时候设置好。别的一些功能应该根据不同dataset的应用去分别设置的,这个就没啥好说的了。
另外,现在机械硬盘自带的缓存越来越大了,尽管ZFS已经commit一个写入已经落盘,但在硬盘的层面上仍然会有物理的缓存落到机械硬盘磁碟的这一个过程。这个物理硬盘的缓存可以通过hdparm -W /dev/sda
查看当前硬盘是否开启了,一般默认是开启的。值得探讨的是否应该关闭这个缓存:hdparm -W 0 /dev/sda
。这个是否应该关闭值得探讨。
ZFS 的使用优化是没有参照的,即便是跟我买完全一模一样的机器,拿回去使用场景不同都不能tuning一样的。老老实实看OpenZFS的Manual吧,没有捷径。但我可以说一下我自己的一些40Gbps网路下的Tuning思路。
L2arc
提高读取速度。L2arc
是一个高性价比的选择。在内存arc仍然growing的情况下强行使用l2arc只会让你的读取速度变慢,按现代计算机架构来看,没有啥SSD是比内存更快的。L2arc
的容量尽可能匹配内存arc
容量的4-5倍之间,超过容量后的那部分tuning不切实际。dirty data
的设置需要花时间观察的,因为不同场景下的值不是一样的,也不是越大越好,需要花费大量时间观察调整,再观察调整循序渐进,没吃太饱的话还是保持默认吧。Meta Devs
这个功能。但凡是CoW的文件系统,都无法避免写放大的问题,在一段时间的使用后,尤其是有删除行为后,你的block会越写越碎,最后顺序写会变成随机写。这种情况在机械硬盘上尤其让人无法忍受,因为sata机械硬盘的queue非常小,虽然有很多种优化方法,比如:
record size
,比如电影文件夹的话,目前openzfs 2.2以上已经支持了large record size
选项。page size
来对应你dataset的record size
。但是,如开头所说,不只是ZFS,只要这个文件系统是CoW的,即便再怎么优化都无法彻底解决的,有一些极端的办法比如把zfs_txg_timeout
设置的非常高,尽量让文件写到内存里,我觉得这都不是一种比较好的办法。如果真的有一个读写非常敏感的dataset,更建议用1-2个ssd,哪怕是raid0的也行,写在上面并且每小时zfs send回HDD池,毕竟nvme的queue depth,可以硬抗这种碎片化带来的顺序写变成随机写的问题。另外,定期的阵列scrub以及trim还是很有必要的。
NFS对于小文件的读写是不理想的,有条件的话尽可能使用iSCSI。
NFS Version ≥ 4.1,并且在NFS Server 设置中把线程设置成24个或者更高(看你CPU),链接NAS是可以大文件跑到40Gbps的(在没有开启RDMA的情况下,这很看你的CPU配置)。如果是4k小文件请调小NAS服务端的Block Size
,同样客户端的wsize
rsize
都理应调小。另外MTU 9000对于4k是很有用的,条件允许,开启。
MacOS依然没有更新NFS版本,并且在Finder下因为Foundation API
通过版本NFS v4.0
连接NAS会出现断连的情况,而在MacOS的Terminal中使用POSIX权限则没有这样的现象。
但在MacOS中,无论使用什么40Gbps卡,你都只能在GUI界面环境中获得20Gbps的连接速度,这也许是因为协议层太过老旧而用户无从选择。
如果是Samba的话,我无法给出一个标准答案,这完全取决于你CPU的单核心能力,毕竟只有上传和下载两个线程。又或者说你希望使用SMB Multi-Channels
功能来解决这个问题。
Samba和SMB这两个东西实在是太畸形了,作为用户我唯一的权利就是选择不用。
机械SATA硬盘无法与现代的SSD比肩4k类的读写,尤其是在IO Depth
IOPS
等方面。如果你不满足于Meta devs
功能来提高4k的写入和读取,单独使用了一块SSD硬盘做日常的使用,并且设置了ZFS Send
按天甚至按小时回传数据到HDD池中。
zfs send SSD/works@3 | zfs recv Home/test
ZFS Send
必须先建立快照,其中3
代表快照名,而后的Home/test则为机械硬盘池。同样,你可以使用TrueNas Web UI中自带的Replication Task
来完成类似的操作。
在设置好TrueNAS的ZFS Pool后,另一台负责计算的Proxmox VE则不需要硬盘了。考虑无盘启动的话,可以参考很多IPXE教程,当然如果是超微主板或者Mellanox网卡可以直接使用Booting over iSCSI。超微的BIOS中直接有这个选项,Mellanox网卡的话使用它的Flexboot
功能就可以了。
除了启动盘外,那各个VM Guest则需要开启Proxmox VE的ZFS over iSCSI
功能。在PVE的Datacentre
–Storage
—Add
中添加。
这个
ZFS over iSCSI
其实和真正的还不一样,说白了只是一个远程的Zvol管理功能。
这里并没有可支持TrueNAS的iSCSI Provider
。你需要使用这个插件来连接TrueNAS。
这个插件的
API Password
就是root的登陆密码,不是API Key
。
使用这个工具可以对VM Guest自动化操作snapshot。这里的快照是使用ZFS Snapshot
的,可以完全利用到ZFS的功能了。链接中有好多个视频操作演示教程,这里就不赘述了。
InfiniBand是一种高速网络通信技术,用于在计算机系统和数据中心中连接高性能计算、存储和网络设备。InfiniBand提供了一种高效的低延迟和高带宽的解决方案,通过使用专用硬件和协议来提高数据传输的速度和可靠性。与传统的以太网技术相比,InfiniBand提供了更高的带宽和更低的延迟。
然而,不是所有的软件都支持Infiniband,因此赋予infiniband一个IP则可以让它兼容大部分的软件协议,这种方法就是IP over Infiniband(IPoIB)。
Proxmox VE和TrueNAS都支持IPoIB。这里最好买原装的Mellanox 56GB FDR QSFP+铜缆,别的牌子可能兼容性没有这么好,闲鱼大概2米的在100元左右(线比网卡贵系列)。
首先最好找一台机器把两张HP 544+的卡的其中一个口刷成IB mode,TrueNAS系统下可能要提权apt和修改源才能完成。
lspci |grep Mellanox03:00.0 Ethernet controller: Mellanox Technologies MT27520 Family [ConnectX-3 Pro]mstconfig -d 03:00.00 set LINK_TYPE_P1=IB LINK_TYPE_P2=ETH #设置p1端口为IB模式
这里假设p1口是IB模式,p2口是ETH模式。
在SX6036的WebUI下切换profile到vpi模式,然后在端口管理处修改端口类型。我这里把1-21口设置成了ETH模式,22-36口为IB模式。
在IB SM Mgmt菜单中找到base SM,选择SM Enabled来开启SM。注意,这些设置完后都要按右上角的SAVE按钮,不然重启就失效了。
这个SM Mgmt就是
Subnet manager
,是用来管理IB网络的,因为网卡的固件版本的更新,用新版本的SM可能会更好一点,但是SX6036是不会再去更新SM了。我们可以通过两个地方下载到新的SM(OpenSM):下载官网的OFED包,只安装里面的OPENSM的deb包,这个是最新的,或者从apt等软件管理包里通过apt install opensm
安装,这个版本在写这篇文章的时候还是2021年更新的,但也比6036上的新一点。OpenSM只需要在6036内的网络下任何机器安装一个跑就行了。实际体验下来,6036自带的SM和最新的OpenSM在我家用环境中没有啥区别。如果你使用4代及以上的卡的情况下,那么6036的SM就没用了,必须装新的了,4代以上的卡设置有很大区别,请不要拿4代以上的卡根据这个教程来设置。
输入modprobe ib_ipoib
加载ipoib的驱动,然后通过ip a
看到加载的ib口了,一般就是叫ibp2s0
或者ibp2S0d1
。
修改/etc/network/interfaces,按实际情况填写网卡名字,注意,这里面的网端不能跟你eth网段相同的。
auto ibp2s0iface ibp2s0 inet static address 10.99.0.1 netmask 255.255.255.0 #pre-up echo connected > /sys/class/net/ibp2s0/mode 三代卡可以用connected模式,如果家里还有别的4代以上的卡,建议不要写这条,默认就是datagram模式 mtu 2044
然后修改/etc/modules 开机加载ib_ipoib
和ib_umad
模块,这样开机就会自动加载IPoIB所需要的模块了。
# /etc/modules: kernel modules to load at boot time.## This file contains the names of kernel modules that should be loaded# at boot time, one per line. Lines beginning with "#" are ignored.ib_ipoibib_umad
最后输入systemctl restart networking.service
生效,我们可以看到SX6036交换机的管理界面中已经有IB流量出现了。
在Core的BSD分支中,直接在Tunables中增加一条:
mlx4ib_load Value: YEStype: loaders
并在Tasks/Init/Shutdown Scripts中增加一条postinit 命令:
kldload ipoib
最后如同设置普通网卡的ip地址一样,在webui中设置与PVE同一段的ip,比如10.99.0.2/24。
如果是Scale版本的话,插上网卡在WebUI上啥都别设置,输入modprobe ib_ipoib && modprobe ib_umad
后,你通过ls /sys/class/net
就能知道你IB口的名字了,接着直接修改/etc/network/interfaces
auto ibs1d1iface ibs1d1 inet static address 10.99.0.2 netmask 255.255.255.0 pre-up echo connected > /sys/class/net/ibs1d1/mode mtu 2044
其次,修改/etc/modules文件,增加这两个模块确保开机启动。
ib_ipoibib_umad
保存重启就好了,但每次系统更新都会导致其失效。
上一个章节中我们在设置好IPoIB后,我们发现虽然获得了更低的延迟,比如ping你能看到非常低的延迟,但是实际的传输速度又很慢,这主要是因为tcp/ip的overhead,协议等问题,这就非常考验你的CPU了。使用RDMA技术来解决这个问题。
Remote Direct Memory Access(远程直接内存访问,简称RDMA),是一种用于在网络中直接访问异地计算机内存的技术。传统的数据传输方式需要在发送端和接收端CPU中进行多次数据拷贝,增加了CPU的使用和数据传输的延迟。而RDMA技术可以通过网卡,让一个计算机的内存直接传输到另一个计算机的内存中,不需要CPU的干预,避免了数据拷贝和CPU资源的浪费,从而显著提高了数据传输的速度和效率。
RDMA有很多种,一般我们用InfiniBand是自带RDMA的,但是需要开启。同样在以太网络模式下(ETH)也有相应的RDMA比如iWrap, RoCE。这里我们只介绍IB的RDMA。
RDMA只有在scale中比较容易设置。
这里分为TrueNas Scale服务端和PVE作为客户端两部分设置。因为Scale在最近的版本中换回了NFS-Kernel-Server,因此非常简单;而Core版本用的是NFS-Ganesha,我不太懂怎么弄这个玩意。
auto ibs1d1iface ibs1d1 inet static address 10.99.0.2 netmask 255.255.255.0 #pre-up echo connected > /sys/class/net/ibs1d1/mode 三代卡可以用connected模式,如果家里还有别的4代以上的卡,建议不要写这条,默认就是datagram模式 pre-up echo rdma 20049 > /proc/fs/nfsd/portlist mtu 2044
修改/etc/modules文件,增加这三个模块确保开机启动。ib_ipoibib_umadsvcrdma
重启TrueNAS,其实echo命令应该放到WEBUI的init script开机脚本里加载,我这么写主要是我太懒了。xprtrdma
,保存重启。mount -t nfs 10.99.0.2:/mnt/SSD/works /mnt/test -o vers=4.2,_netdev,rdma,port=20049,hard,intr,noatime,nodiratime,async,nolock,noacl,sec=sys,noresvport
nfs: ib-nfs export /mnt/Home/Proxmox path /mnt/pve/ib-nfs server 10.99.0.2 content snippets,vztmpl,backup,images,iso,rootdir options vers=4.2,proto=rdma,port=20049 prune-backups keep-all=1
nfsstat -m
你可以看到proto已经是RDMA了。其实我几乎没有使用过scst这种iscsi,再者我也懒得在一个debian based truenas上折腾来折腾去,这部分我就想写个大方向。本质上就是scale的scst是自己魔改的,那么我们把原版的scst编译好替换掉应该就行了,但这么做的话,估计WEBUI那边的iscsi设置会有很多问题的,你要熟悉scst的cli。如果你熟悉linux的话,还是推荐用LIO更为方便简单。
最主要的是!我没有在用scale,写这个我还得抽空装一个,再者我平时工作挺忙的,在写这个博客没想到还是有很多人看的,就坚持写写吧,但我希望大家通过这里获得一些启发,主动学一些基本的zfs cli,那么在各种linux正式发行版中使用RDMA才游刃有余,不被客制化系统所束缚。
没写完,有空再说。
配置如下:
zfs send
传回HDD池通过IPoIB连接PVE和TrueNAS(最近把TrueNAS删了换成了NixOS),再通过ZFS over iser跑虚拟机。下面测试中10.99.0.0是IPoIB,10.10.10.0是ETH
通过ping测了一下latency
PING 10.99.0.2 (10.99.0.2) 56(84) bytes of data.64 bytes from 10.99.0.2: icmp_seq=1 ttl=64 time=0.046 ms64 bytes from 10.99.0.2: icmp_seq=2 ttl=64 time=0.069 ms64 bytes from 10.99.0.2: icmp_seq=3 ttl=64 time=0.079 ms64 bytes from 10.99.0.2: icmp_seq=4 ttl=64 time=0.051 ms PING 10.10.10.11 (10.10.10.11) 56(84) bytes of data.64 bytes from 10.10.10.11: icmp_seq=1 ttl=64 time=0.296 ms64 bytes from 10.10.10.11: icmp_seq=2 ttl=64 time=0.224 ms64 bytes from 10.10.10.11: icmp_seq=3 ttl=64 time=0.237 ms64 bytes from 10.10.10.11: icmp_seq=4 ttl=64 time=0.254 ms
在PVE和NAS之间通过ibping 10000个packets
--- nix-nas.(none) (Lid 4) ibping statistics ---10000 packets transmitted, 10000 received, 0% packet loss, time 77 msrtt min/avg/max = 0.003/0.007/0.024 ms
在PVE上连接iser,是SSD那个池
以下是4k随机写入 24线程psync:
fio -filename=/dev/sdb -direct=1 -ioengine=psync -bs=4k -size=10G -numjobs=24 -iodepth=32 -runtime=60 -thread -rw=randwrite -group_reporting -name="randwrite"randwrite: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=32...fio-3.25Starting 24 threadsJobs: 24 (f=24): [w(24)][100.0%][w=510MiB/s][w=130k IOPS][eta 00m:00s]randwrite: (groupid=0, jobs=24): err= 0: pid=176142: Sun May 21 17:26:35 2023 write: IOPS=132k, BW=518MiB/s (543MB/s)(30.3GiB/60001msec); 0 zone resets clat (usec): min=35, max=19045, avg=180.20, stdev=61.22 lat (usec): min=35, max=19046, avg=180.35, stdev=61.22 clat percentiles (usec): | 1.00th=[ 130], 5.00th=[ 147], 10.00th=[ 155], 20.00th=[ 163], | 30.00th=[ 167], 40.00th=[ 172], 50.00th=[ 178], 60.00th=[ 182], | 70.00th=[ 188], 80.00th=[ 194], 90.00th=[ 208], 95.00th=[ 221], | 99.00th=[ 253], 99.50th=[ 297], 99.90th=[ 502], 99.95th=[ 758], | 99.99th=[ 3130] bw ( KiB/s): min=482416, max=577304, per=100.00%, avg=530297.19, stdev=936.60, samples=2856 iops : min=120604, max=144326, avg=132574.20, stdev=234.14, samples=2856 lat (usec) : 50=0.01%, 100=0.16%, 250=98.74%, 500=0.99%, 750=0.05% lat (usec) : 1000=0.01% lat (msec) : 2=0.02%, 4=0.01%, 10=0.01%, 20=0.01% cpu : usr=0.95%, sys=2.09%, ctx=7949914, majf=0, minf=0 IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=0,7949243,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32Run status group 0 (all jobs): WRITE: bw=518MiB/s (543MB/s), 518MiB/s-518MiB/s (543MB/s-543MB/s), io=30.3GiB (32.6GB), run=60001-60001msecDisk stats (read/write): sdb: ios=110/7935683, merge=0/2, ticks=8/1393327, in_queue=1393335, util=99.89%
以下是1M顺序写入 24线程psync:
fio -filename=/dev/sdb -direct=1 -ioengine=psync -bs=1M -size=10G -numjobs=24 -iodepth=32 -runtime=60 -thread -rw=write -group_reporting -name="seqwrite"seqwrite: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=32...fio-3.25Starting 24 threadsJobs: 1 (f=1): [_(14),W(1),_(9)][98.1%][w=1576MiB/s][w=1575 IOPS][eta 00m:01s] seqwrite: (groupid=0, jobs=24): err= 0: pid=176521: Sun May 21 17:28:31 2023 write: IOPS=4808, BW=4808MiB/s (5042MB/s)(240GiB/51114msec); 0 zone resets clat (usec): min=450, max=61358, avg=4775.89, stdev=1543.22 lat (usec): min=475, max=61501, avg=4840.36, stdev=1543.55 clat percentiles (usec): | 1.00th=[ 1467], 5.00th=[ 3097], 10.00th=[ 3523], 20.00th=[ 3916], | 30.00th=[ 4178], 40.00th=[ 4359], 50.00th=[ 4555], 60.00th=[ 4817], | 70.00th=[ 5014], 80.00th=[ 5407], 90.00th=[ 6063], 95.00th=[ 6980], | 99.00th=[10552], 99.50th=[12911], 99.90th=[17957], 99.95th=[20579], | 99.99th=[26608] bw ( MiB/s): min= 4000, max= 6883, per=100.00%, avg=4955.55, stdev=33.42, samples=2359 iops : min= 4000, max= 6882, avg=4955.39, stdev=33.41, samples=2359 lat (usec) : 500=0.04%, 750=0.52%, 1000=0.12% lat (msec) : 2=0.49%, 4=21.63%, 10=75.94%, 20=1.21%, 50=0.06% lat (msec) : 100=0.01% cpu : usr=1.43%, sys=0.50%, ctx=246687, majf=0, minf=0 IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=0,245760,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32Run status group 0 (all jobs): WRITE: bw=4808MiB/s (5042MB/s), 4808MiB/s-4808MiB/s (5042MB/s-5042MB/s), io=240GiB (258GB), run=51114-51114msecDisk stats (read/write): sdb: ios=270/491442, merge=0/0, ticks=63/2193831, in_queue=2193894, util=99.79%
这里连接的是HDD的池。
1M顺序写入 24线程 psync,机械池。
fio -filename=write -direct=1 -ioengine=psync -bs=1M -size=10G -numjobs=24 -iodepth=32 -runtime=60 -thread -rw=write -group_reporting -name="write"write: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=32...fio-3.25Starting 24 threadsJobs: 24 (f=24): [W(24)][97.9%][w=5141MiB/s][w=5141 IOPS][eta 00m:01s]write: (groupid=0, jobs=24): err= 0: pid=178298: Sun May 21 17:38:07 2023 write: IOPS=5142, BW=5142MiB/s (5392MB/s)(240GiB/47790msec); 0 zone resets clat (usec): min=569, max=27777, avg=4633.72, stdev=757.33 lat (usec): min=592, max=27806, avg=4664.18, stdev=761.07 clat percentiles (usec): | 1.00th=[ 3916], 5.00th=[ 4293], 10.00th=[ 4359], 20.00th=[ 4424], | 30.00th=[ 4490], 40.00th=[ 4490], 50.00th=[ 4555], 60.00th=[ 4555], | 70.00th=[ 4621], 80.00th=[ 4686], 90.00th=[ 4752], 95.00th=[ 4883], | 99.00th=[ 7635], 99.50th=[ 8455], 99.90th=[15139], 99.95th=[17433], | 99.99th=[19792] bw ( MiB/s): min= 3902, max= 5414, per=100.00%, avg=5145.63, stdev=12.45, samples=2280 iops : min= 3902, max= 5414, avg=5145.60, stdev=12.44, samples=2280 lat (usec) : 750=0.01%, 1000=0.01% lat (msec) : 2=0.04%, 4=1.15%, 10=98.49%, 20=0.29%, 50=0.01% cpu : usr=0.73%, sys=1.00%, ctx=322779, majf=0, minf=0 IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=0,245760,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32Run status group 0 (all jobs): WRITE: bw=5142MiB/s (5392MB/s), 5142MiB/s-5142MiB/s (5392MB/s-5392MB/s), io=240GiB (258GB), run=47790-47790msec
4k随机写 24线程 psync,机械池。
fio -filename=randwrite -direct=1 -ioengine=psync -bs=4k -size=10G -numjobs=24 -iodepth=32 -runtime=60 -thread -rw=randwrite -group_reporting -name="randwrite"randwrite: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=32...fio-3.25Starting 24 threadsJobs: 24 (f=24): [w(24)][100.0%][w=189MiB/s][w=48.5k IOPS][eta 00m:00s]randwrite: (groupid=0, jobs=24): err= 0: pid=177907: Sun May 21 17:36:12 2023 write: IOPS=81.3k, BW=318MiB/s (333MB/s)(18.6GiB/60003msec); 0 zone resets clat (usec): min=45, max=145511, avg=293.97, stdev=887.94 lat (usec): min=45, max=145511, avg=294.15, stdev=887.94 clat percentiles (usec): | 1.00th=[ 78], 5.00th=[ 85], 10.00th=[ 88], 20.00th=[ 92], | 30.00th=[ 95], 40.00th=[ 98], 50.00th=[ 102], 60.00th=[ 109], | 70.00th=[ 143], 80.00th=[ 255], 90.00th=[ 529], 95.00th=[ 963], | 99.00th=[ 3228], 99.50th=[ 4359], 99.90th=[10552], 99.95th=[14746], | 99.99th=[28705] bw ( KiB/s): min=27632, max=934848, per=100.00%, avg=326662.59, stdev=8797.03, samples=2856 iops : min= 6908, max=233712, avg=81665.65, stdev=2199.26, samples=2856 lat (usec) : 50=0.01%, 100=46.46%, 250=33.26%, 500=9.61%, 750=3.95% lat (usec) : 1000=1.89% lat (msec) : 2=2.27%, 4=1.94%, 10=0.50%, 20=0.09%, 50=0.02% lat (msec) : 100=0.01%, 250=0.01% cpu : usr=0.65%, sys=1.38%, ctx=5699062, majf=0, minf=0 IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=0,4880906,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32Run status group 0 (all jobs): WRITE: bw=318MiB/s (333MB/s), 318MiB/s-318MiB/s (333MB/s-333MB/s), io=18.6GiB (19.0GB), run=60003-60003msec
虚拟机直接通了PVE上的iscsi,那个iscsi的zvol是SSD的池。方法大概是修改/etc/pve/qemu-server/xxx.conf:
scsi0: /dev/disk/by-id/scsi-3600140512333f9ef3744ed7bc7c2527f,size=100G,ssd=1
以下是在KVM虚拟机Ubuntu下跑的,4k随机 24线程 psync:
fio -filename=randwrite -direct=1 -ioengine=psync -bs=4k -size=10G -numjobs=24 -iodepth=32 -runtime=60 -thread -rw=randwrite -group_reporting -name="randwrite"randwrite: (g=0): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=32...fio-3.28Starting 24 threadsJobs: 24 (f=24): [w(24)][100.0%][w=41.9MiB/s][w=10.7k IOPS][eta 00m:00s]randwrite: (groupid=0, jobs=24): err= 0: pid=5532: Sun May 21 17:51:08 2023 write: IOPS=18.7k, BW=73.0MiB/s (76.5MB/s)(4378MiB/60003msec); 0 zone resets clat (usec): min=64, max=104970, avg=1283.01, stdev=3124.97 lat (usec): min=64, max=104970, avg=1283.23, stdev=3124.99 clat percentiles (usec): | 1.00th=[ 83], 5.00th=[ 87], 10.00th=[ 92], 20.00th=[ 99], | 30.00th=[ 119], 40.00th=[ 167], 50.00th=[ 208], 60.00th=[ 247], | 70.00th=[ 302], 80.00th=[ 408], 90.00th=[ 5669], 95.00th=[ 8586], | 99.00th=[14746], 99.50th=[15795], 99.90th=[17433], 99.95th=[18482], | 99.99th=[33424] bw ( KiB/s): min=17064, max=391024, per=100.00%, avg=75022.99, stdev=4135.19, samples=2856 iops : min= 4266, max=97756, avg=18755.73, stdev=1033.80, samples=2856 lat (usec) : 100=21.50%, 250=39.43%, 500=23.41%, 750=1.20%, 1000=0.37% lat (msec) : 2=1.11%, 4=1.59%, 10=7.95%, 20=3.41%, 50=0.03% lat (msec) : 100=0.01%, 250=0.01% cpu : usr=0.33%, sys=1.79%, ctx=1762463, majf=0, minf=0 IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=0,1120875,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32Run status group 0 (all jobs): WRITE: bw=73.0MiB/s (76.5MB/s), 73.0MiB/s-73.0MiB/s (76.5MB/s-76.5MB/s), io=4378MiB (4591MB), run=60003-60003msecDisk stats (read/write): sda: ios=0/1123915, merge=0/71430, ticks=0/207957, in_queue=207963, util=99.88%
虚拟机Ubuntu下,1M顺序写 24线程 psync:
fio -filename=write -direct=1 -ioengine=psync -bs=1M -size=10G -numjobs=24 -iodepth=32 -runtime=60 -thread -rw=write -group_reporting -name="write"write: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=32...fio-3.28Starting 24 threadsJobs: 24 (f=24): [W(24)][100.0%][w=3311MiB/s][w=3311 IOPS][eta 00m:00s]write: (groupid=0, jobs=24): err= 0: pid=5469: Sun May 21 17:49:44 2023 write: IOPS=3063, BW=3063MiB/s (3212MB/s)(179GiB/60008msec); 0 zone resets clat (usec): min=425, max=119934, avg=7775.27, stdev=4139.62 lat (usec): min=451, max=119976, avg=7832.54, stdev=4142.63 clat percentiles (usec): | 1.00th=[ 611], 5.00th=[ 848], 10.00th=[ 4883], 20.00th=[ 6194], | 30.00th=[ 6718], 40.00th=[ 7242], 50.00th=[ 7701], 60.00th=[ 8094], | 70.00th=[ 8586], 80.00th=[ 9241], 90.00th=[ 10290], 95.00th=[ 11338], | 99.00th=[ 20055], 99.50th=[ 30540], 99.90th=[ 55837], 99.95th=[ 69731], | 99.99th=[116917] bw ( MiB/s): min= 1092, max= 3761, per=100.00%, avg=3068.96, stdev=19.31, samples=2856 iops : min= 1092, max= 3761, avg=3067.40, stdev=19.31, samples=2856 lat (usec) : 500=0.06%, 750=4.13%, 1000=1.07% lat (msec) : 2=0.56%, 4=1.51%, 10=80.77%, 20=10.90%, 50=0.86% lat (msec) : 100=0.13%, 250=0.01% cpu : usr=1.03%, sys=0.39%, ctx=367956, majf=0, minf=1 IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=0,183806,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32Run status group 0 (all jobs): WRITE: bw=3063MiB/s (3212MB/s), 3063MiB/s-3063MiB/s (3212MB/s-3212MB/s), io=179GiB (193GB), run=60008-60008msecDisk stats (read/write): sda: ios=0/183581, merge=0/108, ticks=0/623119, in_queue=623127, util=99.94%
在NAS和PVE两台物理机之间跑perftest
以下是ib_write_bw
--------------------------------------------------------------------------------------- RDMA_Write BW Test Dual-port : OFF Device : mlx4_0 Number of qps : 1 Transport type : IB Connection type : RC Using SRQ : OFF PCIe relax order: ON ibv_wr* API : OFF TX depth : 128 CQ Moderation : 1 Mtu : 2048[B] Link type : IB Max inline data : 0[B] rdma_cm QPs : OFF Data ex. method : Ethernet--------------------------------------------------------------------------------------- local address: LID 0x04 QPN 0x0255 PSN 0x5aff2c RKey 0x90010100 VAddr 0x007ff8e4722000 remote address: LID 0x02 QPN 0x0264 PSN 0x89e18 RKey 0x8030c18 VAddr 0x007fcda477b000--------------------------------------------------------------------------------------- #bytes #iterations BW peak[MB/sec] BW average[MB/sec] MsgRate[Mpps]Conflicting CPU frequency values detected: 3000.000000 != 3300.384000. CPU Frequency is not max. 65536 469709 0.00 5871.31 0.093941Conflicting CPU frequency values detected: 3000.000000 != 3299.814000. CPU Frequency is not max. 65536 469523 0.00 5869.03 0.093905Conflicting CPU frequency values detected: 3000.000000 != 3259.731000. CPU Frequency is not max. 65536 469685 0.00 5871.09 0.093937Conflicting CPU frequency values detected: 3279.238000 != 3000.000000. CPU Frequency is not max. 65536 469546 0.00 5869.18 0.093907Conflicting CPU frequency values detected: 3000.000000 != 3299.655000. CPU Frequency is not max. 65536 469684 0.00 5871.05 0.093937--------------------------------------------------------------------------------------
以下是ib_write_lat
-------------------------------------------------------------------------------------- RDMA_Write Latency Test Dual-port : OFF Device : mlx4_0 Number of qps : 1 Transport type : IB Connection type : RC Using SRQ : OFF PCIe relax order: OFF ibv_wr* API : OFF TX depth : 1 Mtu : 2048[B] Link type : IB Max inline data : 220[B] rdma_cm QPs : OFF Data ex. method : Ethernet--------------------------------------------------------------------------------------- local address: LID 0x04 QPN 0x0256 PSN 0x9363e6 RKey 0x98010100 VAddr 0x00000000d23000 remote address: LID 0x02 QPN 0x0265 PSN 0xf60716 RKey 0x10030c18 VAddr 0x0055836d79c000--------------------------------------------------------------------------------------- #bytes #iterations t_min[usec] t_max[usec] t_typical[usec] t_avg[usec] t_stdev[usec] 99% percentile[usec] 99.9% percentile[usec] Conflicting CPU frequency values detected: 3000.000000 != 3299.067000. CPU Frequency is not max.Conflicting CPU frequency values detected: 3000.000000 != 3297.173000. CPU Frequency is not max. 2 1000 1.01 2.56 1.07 1.10 0.03 1.40 --------------------------------------------------------------------------------------
以下是ib_read_bw
--------------------------------------------------------------------------------------- RDMA_Read BW Test Dual-port : OFF Device : mlx4_0 Number of qps : 1 Transport type : IB Connection type : RC Using SRQ : OFF PCIe relax order: ON ibv_wr* API : OFF TX depth : 128 CQ Moderation : 1 Mtu : 2048[B] Link type : IB Outstand reads : 16 rdma_cm QPs : OFF Data ex. method : Ethernet--------------------------------------------------------------------------------------- local address: LID 0x04 QPN 0x0258 PSN 0x48418e OUT 0x10 RKey 0xa8010100 VAddr 0x007f440f3bb000 remote address: LID 0x02 QPN 0x0267 PSN 0xc3159e OUT 0x10 RKey 0x20030c18 VAddr 0x007f6769c2f000--------------------------------------------------------------------------------------- #bytes #iterations BW peak[MB/sec] BW average[MB/sec] MsgRate[Mpps]Conflicting CPU frequency values detected: 2930.376000 != 3000.000000. CPU Frequency is not max. 65536 436871 0.00 5460.75 0.087372Conflicting CPU frequency values detected: 3000.000000 != 1876.346000. CPU Frequency is not max. 65536 435374 0.00 5442.16 0.087075Conflicting CPU frequency values detected: 3000.000000 != 3299.691000. CPU Frequency is not max. 65536 435425 0.00 5442.79 0.087085
以下是ib_read_lat
--------------------------------------------------------------------------------------- RDMA_Read Latency Test Dual-port : OFF Device : mlx4_0 Number of qps : 1 Transport type : IB Connection type : RC Using SRQ : OFF PCIe relax order: ON ibv_wr* API : OFF TX depth : 1 Mtu : 2048[B] Link type : IB Outstand reads : 16 rdma_cm QPs : OFF Data ex. method : Ethernet--------------------------------------------------------------------------------------- local address: LID 0x04 QPN 0x0257 PSN 0x147e9c OUT 0x10 RKey 0xa0010100 VAddr 0x000000021ab000 remote address: LID 0x02 QPN 0x0266 PSN 0x62a237 OUT 0x10 RKey 0x18030c18 VAddr 0x0055e271b30000--------------------------------------------------------------------------------------- #bytes #iterations t_min[usec] t_max[usec] t_typical[usec] t_avg[usec] t_stdev[usec] 99% percentile[usec] 99.9% percentile[usec] Conflicting CPU frequency values detected: 3000.000000 != 3242.373000. CPU Frequency is not max.Conflicting CPU frequency values detected: 3000.000000 != 3242.780000. CPU Frequency is not max. 2 1000 2.02 28.12 2.08 2.48 0.77 3.73 28.12 ---------------------------------------------------------------------------------------
一般来说,除非你一定要用ETH的RDMA(RoCE),我们需要设置MTU 9000, AKA Jumbo Frames
,去提高小文件的读写速度,但这个MTU有一个问题是如果你家里有一些智能的联网设备无法设置MTU,这些设备可能会联网异常的,因此我们要处理一下。有很多种处理方法比如划分VLAN等,但考虑每个人家中网络拓扑都不一样,就说一下简单的一个基本框架。
SX6036内的ETH全部走MTU 9000,然后SX6036连接到你家中万兆或者千兆的交换机这一段也设置成9000,而万兆/千兆的交换机接到家中的AP那些低速网络层全部使用MTU 1500。
网络inbound的话,也许需要修正mss,具体看你git clone
之类的命令会不会遇到TLS握手问题。Mikrotik ROS则加一条mangle:
/ip firewall mangle add action=change-mss chain=forward new-mss=clamp-to-pmtu passthrough=yes protocol=tcp tcp-flags=syn
如果更为复杂的网络情况,要用到VLAN则实在没法写一个说明了,请自行脑补。
尽可能使用Infiniband网络,因为Infiniband内的网络走多少帧,跟你家中的以太网络毫无关系,解决很多烦恼。
Option Matcher
的功能。配合Option 93可以根据主板的架构分配不同的引导文件e.g.ArmUEFI,LegacyBIOS,x86UEFI.SMB-Direct
之类的功能。我觉得好像不用Windows不就解决了所有问题了吗?Apple TV(下文简称TV)相比国内很多机顶盒,比较干净,适合看NAS上的电影视,以及各种流媒体。
相反,对爱优腾支持比较不好。当然因为我一直使用香港APPLE ID,是有优酷和爱奇艺的,自制剧内容都是同步的,甚至有更多专做海外的国语内容,却无法在国内过审的(e.g.逆局);会员体系不一样,你买了后无法在国内的爱优上使用。提一句,IPTV有很多办法在TV上实现,这个不是问题,本文也不会提及。
在我想好好使用TV的利益驱动下,这篇文章就打算写写怎么搞Dolby Vision以及各种声音拓展。
此文中指的TV是2021年发布的Apple TV 4K。
2022年发布的新款TV除了支持三星的HDR10+之外,在功能性上与2021年发布的没有差异。但硬件和价格上的差别,请自行甄辨。
一般情况下TV只要电视有A/V或者HDMI接口既可以使用,但是你需要考虑如下问题:
TV与电视CEC
TV与Dolby Vision
TV与音频
CEC功能是HDMI1.3b开始提出的一项功能,自行搜索具体原理,大概就是电视遥控器也能通过CEC功能口直机顶盒。因此,在标注有HDMI CEC功能的电视机上,你可以不再使用TV的遥控器。
尽管CEC应该是一个比较标准的功能,各个厂家也许因自己利益驱使,或者技术上的问题,并不能完全体验一个遥控器控制所有设备(e.g.LG)。目前看来只有日本厂商在CEC上做的比较开放(e.g.Sony)。
值得一提的是,TV支持红外学习,但是小米电视的遥控器是蓝牙的。
一部支持Dolby Vision的电影,一般由BL+EL+RPU所组成。当视频中只含有BL+RPU,我们叫做单层杜比视界。这种视频一般通过杜比的Profile 5进行封装导出MKV格式(极少数蓝光原盘也有单层)。
目前,TV只支持BL+RPU的Dolby Vision,并且原生只支持profile 5封装的,当然一些App(e.g.Infuse)可以通过它自己的拓展播放Profile 7。
目前在整个苹果生态里,Infuse可能是唯一一个能够播放Dolby Profile 5.2封装的软件。
双层杜比则是指BL+EL+RPU所组成的杜比视频,目前看来,并没有机顶盒支持它,因为涉及到dolby私钥,也没啥好破解的。市面上说支持双层杜比的安卓机顶盒,大概率是通过双层转单层实现的(不能说死,万一这个安卓厂商愿意给杜比付钱呢)。
总结:TV无法播放双层杜比,但是可以转回单层,你可能需要购买蓝光碟机来播放双层杜比。
音频种类非常繁杂,我尽可能概括,也许有错误,疏漏,时效性问题,请留言讨论(先甩锅)。
电子音频被定义为Pulse Coded Modulation (PCM),这是一种无损(lossless),无压缩(uncompressed),原始记录的一种电子音频格式。
PCM可以与.bmp格式图片作为类比
我们现在的手机通话,是由声波(sound waves)转换成音频信号(analogue electrical audio signals),这些信号再转换成PCM。
在信号到PCM的转换过程中,声波被分割成各种网格,网格中块状的数量,被定义成了bits(e.g.16-bits),越多的bits代表着它能更精确的反应声音的原始程度,你可以参考4K图片比2K图片更加清晰是因为pixels更多。
同样在转换的过程中,不断的重复抓取声音是为了更精确的抓住声音的改变,比如44,000次每秒的抓取,等于是44kHz。你可以参考电影或者游戏中的fps,60fps的电影,你能感觉到更加流畅的观感效果。
综上所述,一个16bits+44kHz的音频,意味着在一个声道中704,000 bits每秒的输出。
相反,从电子音频格式到信号转换(Digital to Analog Converter;aka, DAC),是把PCM重新转换成音频信号,我们打电话的时候才能听到。这种DAC装置可以是你的电视,可以是声卡。
说到底,电子音频转换成PCM,再通过DAC装置让你听到,或者让电话另一头的人听到。
我们一般会下载到带多个声音文件的电影,这可能是为了兼容性,多语言,讲解等需求。尤其是多声道的情况下,这种无损音频所占据的空间和比特率可能会很大,类似于我们想把超级大的.bmp图片转换成.jpg。考虑到压缩可能带来损耗,因此就有了各种codec。
Codec包含了解码和编码两部分,比如你把一个文件通过ZIP压缩了,但解压可以是不同的7ZIP,好压等软件,类比于各种codec解码。
音频Codec压缩/解压缩主要分为两种:
有损(compressed, lossy):牺牲音频质量,来减少占用空间,主要是Dolby Digital(AC3)以及另一家Digital Theatre Systems (DTS)在使用。
无损(compressed, lossless):保护原有的音频,但压缩率偏低,一般就是Dolby TrueHD 或者 DTS HD Master Audio (MA)在用。可以参考.png的无损压缩,或者直接把.bmp文件打包成zip。
电影文件中DTS-HD MA是被编码成DTS的,并且DTS-HD MA,作为拓展层被包含在一起。这样做的好处是,当你的设备支持DTS-HD MA则可以播放音频,如果不支持,则自动回落到DTS上。Dolby TrueHD同样用了这种拓展层的方法去解决兼容性问题。
打个比方,一本电影中你可能看到如下的音频列表:
#1 [uncompressed, lossless] PCM 5.1 (English)
#2 [compressed, lossless] DTS-HD Master Audio 5.1 (English)
#3 [compressed, lossy] Dolby Digital 5.1 (French)
#4 [compressed, lossy] Dolby Stereo 2.0 (Mandarin)
#2/3/4是压缩的,需要被解码codec解压成PCM再发送到DAC装置,#1已经是PCM了,可以直接到DAC。
#1/2都是无损的,最终被转换成PCM的。那为什么要放两个无损的同一语言的5.1呢。也许是PCM 5.1占据的空间太大,所以它放了一个16bits/44 kHZ的PCM,然后DTS-HD MA占据的空间小,可以再放一个24-bits/96kHz的无损音频,用户就多了一个选择。
接着我们来说一下主要的两大根据空间定位为依据的音频格式:Dolby Atmos 和 DTS:X。在电影中,如果有一声枪响,这类音频格式会根据本地音响的位置,比如左后方,上中下的某一个喇叭发出来,让用户有更好的场景代入感。
Dolby Atmos或者DTS:X都不是codec,也不是单独的音轨,他们是音频拓展层上的东西,像之前说的那样,旧设备不支持,则不播放,当支持时,使用这种空间定位的meta信息。一般来说,常见的拓展层有如下几个:
我们用TV打开一个视频时,它一定会检测音轨,通过解码器首先把声音还原成PCM,或者音轨就是PCM则不需要。这里要求TV有这样的Codec或者APP有,或者你的解码在服务端(e.g plex)。就像别人给你了一个.psd格式的图片,是你让你同事的电脑去打开后转换成你电脑上就有的格式(e.g png)给你,或者是你自己下载Photoshop。
根据标准,所有的DVD碟机,或者蓝光碟机,都必须拥有Dolby Digital的Codec,但DTS-HD MA则没有明文规定。理论上,我们支持用终端机器来观影,可以得到最好的音频体验,但更优质的音频,则需要更大的带宽,无论S/PDIF 接口 或者 HDMI ARC都受限于自身带宽,最多放出2声道的PCM,或者5.1声道压缩音频,具体来说,S/PDIF或者HDMI ARC
支持
不支持
在这种限制下,HDMI推出了eARC,在HDMI eARC模式下,你可以享受到更多7声道甚至11声道的体验,当然目前似乎并没有11声道的电影。TV在通过eARC后,可以播放出更多声道,因此,如果你还没有买电视,并且想用TV的话,请购买带有HDMI eARC的电视机。
TV的工作机制如下:
但一些APP自带了Codec来解决一些问题(e.g. Infuse)。
当TV碰到这些不支持的格式,包括Dolby TrueHD Atmos或者带有DTS:X的DTS-HD MA,它会自动转成PCM然后通过2.0/5.1/7.1声道播放出来,丢弃空间Meta数据。
总结一下,你需要一个支持eARC的音响(e.g Samsung Q950),以及支持eARC的电视机,配合TV,播放出最高带有7.1声道的电影,并且无法享受到ATMOS或者DTS:X。
]]>我是一个很忠实的NAS用户,并且使用了FreeNas很多年,一直很稳定好用,也很想推荐给大家使用,就打算写这篇文章了。
现在改名叫
TrueNas
,并且分支为TrueNas Core
和TrueNas Scale
。文章里面提及的FreeNas
与TrueNas Core
是一回事。
最近TrueNas的官网好像被污染了,自己找梯子解决=。= https://www.v2ex.com/t/832355
缓存
机制首先刚开始玩的小白并不适合用FreeNas,上手比较难,但当然一开始就选择FreeNas,花费多一点的学习成本,肯定也是一个不错的选择。FreeNas有很多缺点,比如上手比较难(相比群晖什么的),入门条件也比较高,但你一但会用了,是一件很棒的事情。我罗列了一下条件:
你并不打算用FreeNas来做一个All in One(AIO)的服务器。FreeNas更适合单独使用作为存储使用,它的虚拟化真的不怎么样,我有另外一台PVE来专门做别的服务。
你打算使用10GB以上的万兆网络来使用FreeNas。其实FreeNas在万兆以上的网络环境情况下才能体现优势,不然好像跟群晖之类的也没啥区别,设置还更难。
你的7200转的硬盘数量必须大于或者等于10个。因为考虑万兆环境,我们并不喜欢通过加缓存提高读命中率的方式来达到万兆,直接10个盘来让读取速度达到万兆读取。(当然你可以加读缓存,但缓存永远是有限的,你还要考虑命中率等问题,没意思)
你的内存起码64GB,别慌,这其实不是很贵,比如我也会推荐DDR3的内存给X79用。
在FreeNas的官网有很多标准配置,我自己也是按照官网以及前辈的推荐来购买的,这样可以避免走很多弯路。
清单一是我自己的配置,价格也还好比较适中。
主板: 超微X10SRA-F 二手主板自带IPMI,这是我买服务器主板的首要条件,不然大机箱管理起来特别麻烦。 闲鱼买来950元
内存:海力士HMA82GR7CJR8N-VK 16G X8 一共128G内存,其实我觉得还是有点不够,淘宝二手价格还行。 380元X8=3040元
CPU:E5-2680V3 我买的线程数有点多了,非常够用,可以考虑买便宜一点的。665元
机箱:超微846 24盘位机箱。这款机箱淘宝有很多很多,并且自带背板,注意要问清楚自带的背板是SAS几的,最好买背板是SAS2或者SAS3的!如果机箱自带的是SAS1的话,你必须买回来把背板拆了,再买一个SAS2+的背板。1100元
散热器:猫头鹰(NOCTUA)NH-D15S 买这个完全是为了静音。这个高度是刚好给这个4U机箱用的,你可以按照此款高度购买别的静音款风扇。 600元
电源:PWS-920P-SQ 机箱自带的电源非常吵,不喜欢的话买这个,SQ结尾的是静音系列,这个机箱其实可以插两个电源,一个做冗余接在UPS,我买的是80块一个的成色非常差的(成色新的估计也只是抛光了一下),80元。
机箱背板:BPN-SAS2-846EL1 如果能买到超微846机箱自带SAS2+的最好不过了,如果不行,请购买这款。当然你要SAS3的就购买BPN-SAS3-846EL1(这款海外EBAY上很多,淘宝上就见到过一两次) 960元
机箱配件:机箱风扇,机箱开机键跳针 共200元
背板后的风扇:ARCTIC F8 8cm X3 更换教程请看2分28秒
机箱最后的出风口风扇:ARCTIC F8 8cm X2
跳线:超微 CBL084L 机箱上连接开关电源键,指示灯的接口非常特殊,846机箱有很多版本,有的给X9用的,有的是X10用的。你可以自己制作公母杜邦线(PDD3块钱一大包),也可以购买我推荐的成品(淘宝有一家,EBAY无数家卖)。
SAS线:MINI-SAS线SFF-8087 X2,SAS2的线,80CM的。如果是买的SAS3背板请购买相应的线,两根46元
万兆网卡:Intel 82599 万兆光口网卡 这张卡其实是我用黑苹果多余出来的。你可以买光口寨卡,82599或者mellanox的,都支持。但如果你购买万兆电口的话,尽可能购买原厂卡!(之前买的万兆寨卡一热就掉速) 闲鱼包含光模块250元(寨光卡)
机械硬盘:自己看着办
固态硬盘:intel s3510 1T 购买固态硬盘不是为了让他成为缓存什么的,因为ZFS的特殊性,尽可能设置PT软件(e.g. transmission)先把文件下载到固态,再自动转移到机械硬盘的池里,这主要是因为ZFS的特殊性而考虑。为什么买S3510呢?便宜吧,最主要是MLC,如果你用TLC做这个事,很快就会坏了的。1000元
主板: 超微X9SRL-F 因为X9这一代的CPU单路支持IPMI的特别难找,这是我搜到的,可以试试搜搜别的单路更便宜的。800元
内存:M386B4G70DM0-YK04 32G X4 因为是DDR3的内存所以超级便宜,可以考虑多买一点,一共128G总计1480元
CPU:E5-2650v2。根据自己情况可以选别的,165元
机箱:同清单一,1100元
电源,同清单一, 80元
散热器:利民thermalright ITX-R REV.A 清单一的太贵了,从高度来说可以,但不知道挡不档内存,500元
机箱背板:同清单一 960元
机箱配件:同清单一 共200元
SAS卡:同清单一 闲鱼100元
万兆网卡:同清单一 闲鱼包含光模块250元
机械硬盘:同清单一
固态硬盘:同清单一,1000元
当然,以上两个清单你不考虑噪音可以便宜一些。
安装包括硬件和系统,首先是把SAS卡LSI SAS 9217-8i刷成it模式,再讲FreeNas的安装。
ZFS请不要使用阵列模式,因为一旦用了ZFS就无法感知每一个物理硬盘,则data healing等功能是失效的,因此我们需要把这张卡刷成直通的it模式。
如果不刷rom,只刷bin文件,则在TrueNAS系统下输入:
sas2flash -o -f SAS9217_xx_IT.bin #这个是刷9217的sas3flash -o -f SAS9300_xx_IT.bin #这个是刷9300的
刷完后记得重启
如若要刷新Bin和Rom固件,根据流程:
下载如下软件
准备一个U盘
把文件放入U盘
一般情况下,shell文件放在efi/boot/BootX64.efi即可,只是有些主板对UEFI的读取有点奇怪,记得要关了BIOS中的secure boot这个选项
进入UEFI Shell
开机按启动选择键,以U盘作为启动,进入UEFI shell,应该是一个类似如下的界面:
如果你无法进入,提示错误,考虑一下升级主板bios以及确认主板是否支持UEFI启动,以及关闭主板的secure boot。
在界面上你会看到检测出来的硬盘,确认哪个是你的U盘,进入那个U盘则输入fs0:
接着输入dir
,看看目录下的文件是否是U盘里之前放的,不是的话退出去,再找别的路径进来。
sas2flash.efi -list
,记录下16位SAS Address (图片也是网上找的,可能不同,红色框内的所有东西都记下来!!!!!!!).sas2flash.efi -o -e 7
来删除当前固件。sas2flash.efi -o -f xxxxx.bin -b x64sas2.rom -b mptsas2.rom
写入新的固件。xxxxx.bin应该是你之前放入的固件名字,比如9207-8.bin。sas2flash.efi -o -sasaddhi XXXXXXX
写入SAS地址,其中XXXXXXX为刚才记录的SAS地址的前7位,一般是500开头。-
。sas2flash.efi -list
看看新的固件有没有刷进去。reset
就可以用了。怎么安装,怎么设置硬盘池,开共享目录那些就不说了,网上一大堆视频教程。如果是相关优化的话,因为每个人的机器需求不同,可以参考ZFS Tuning Guide。
相信新手碰到最头痛的肯定就是权限问题了,这里可以展开说一下。
其实这里有很多很多方法,我说一下我自己的吧,可能不是最好的办法,但是是我最习惯的办法。
一般新用户会去account里新建一个自己的用户,这个新的用户用来访问SMB,NFS等。比如我设置的用户叫做xjn,我打算用这个用户来使用所有的文件共享,我们就对所有的pool下面的datasheet的权限都给xjn,所有者和所有组都是(记得要apply permissions recursively)。提一嘴,如果是NFS共享,记得把Mapall User设置成xjn。
另一方面,我们可能会装一些jail来使用一些功能,比如安装transmission来下载,但是默认情况下,jail里transmission下载的所有文件都是归root用户所有,导致我们用自己的账户打不开这个文件。这里我们需要对transmission的权限给xjn这个用户,这样下载下来的文件都能打开了。
因为我不是特别喜欢freenas里面plugins一个插件一个jail的逻辑,我觉得你想要啥就全装在同一个jail里就够了。所以以下内容包括了设置权限,以及怎么手动装一个transmission(我打个比方而已)。
root@truenas[~] id xjnuid=1000(xjn) gid=1000(xjn) groups=1000(xjn),0(wheel),545(builtin_users)
对,我给xjn这个用户多给了一个wheel的组,以防万一,你们可以参考一下。
得到我的这个xjn账户的uid和gid都是1000,记下来。
相信你已经在存储页面(storage)下面建立好你的存储池(pool)了,并且建立了自己的datasheet,比如我的Pool叫做Home,并且在Home下建立了一个叫Media的dataset。点击Media最右边的设置按钮,修改权限,我们把这个dataset的权限修改成你自己的用户和组,而不是root。如图:
你可以进入shell里查看自己的池:
cd /mntls#一般会输出你刚才创建的pool名字,我们这里叫Homecd Homels -l#输出的是你刚才创建的Media,并应该可以看到这个文件夹的所有人是xjn这个用户
这样就可以记下了我们的Media的实际目录是/mnt/Home/Mediaiocage create -n "myjail" -r 12.2-RELEASE vnet="on" ip4_addr="vnet0|10.10.10.4/24" defaultrouter="10.10.10.3" boot="on"
我们创建一个叫做myjail的jail,使用的是12.2-RELEASE
这个版本,把这个jail的ip地址设置成了10.10.10.4
,并且本地的路由器地址是10.10.10.3
。请根据自己情况改一下。在Jail中安装软件,我们假设装一个transmission。
iocage exec myjail pkg updateiocage exec myjail pkg install -y nano transmission #在刚才创建的myjail的容器里安装transmission以及nano编辑器
在Jail中创建与主系统相连的文件目录
我们这里默认刚才得到的下载目录是/mnt/Home/Media 我们需要把主系统的目录和jail下的目录连接起来。
iocage exec myjail mkdir -p /mnt/downloads #在myjail下创建一个叫做downloads的文件夹iocage fstab -a myjail /mnt/Home/Media /mnt/downloads nullfs rw 0 0 #把主系统的Media与刚才在jail里创建的downloads连接起来。这样的话,到时候transmission下载到这个downloads文件夹,也等同于下载到了我们系统的Media目录下了。
修改安装好的transmission的权限,保持与主系统一致。
因为直接安装的transmission会以root的权限运行,这样的话transmission下载下来的文件的权限都是root所有的,我们这个xjn用户是没有权限编辑它的。因此,我们要用xjn这个账户去运行transmission来保证程序正常运行。
第一步里,记录下来我的常用用户为xjn (uid=1000),那么我们在这个jail里创建一个一模一样的用户:
iocage exec myjail "pw user add xjn -c xjn -u 1000 -d /nonexistent -s /usr/bin/nologin"#创建好之后,输入:iocage exec myjail id xjn#看一下jail里这个xjn的ID是否跟主系统中的一致
如果你的主系统NAS有多个用户要用,你可以把所用的用户归类到同一个组里去。
我们把transmission的权限交给jail里xjn这个用户:
iocage exec myjail chown -R xjn:xjn /usr/local/etc/transmissioniocage exec myjail chown -R xjn:xjn /mnt/downloadsiocage exec myjail sysrc "transmission_enable=YES"iocage exec myjail sysrc "transmission_download_dir=/mnt/downloads/"iocage exec myjail sysrc "transmission_user=xjn"iocage exec myjail sysrc "transmission_group=xjn"
iocage exec myjail service transmission start
不知道为什么,最新的transmission在运行后,要等十几分钟才能进网页端,慢慢等吧,ip地址是你刚才自己设置好的那个,端口号是9091,这个教程里的地址就是:
http://10.10.10.4:9091
你可能需要修改配置文件来符合自己的需求:iocage console myjail #登陆你的jailservice transmission stop #必须先停止transmission的主程序才能修改配置文件nano /usr/local/etc/transmission/home/settings.json
修改如下两项:
"rpc-whitelist-enabled": false,"umask": 7,其他你看情况吧
修改完后⌃+X保存
搞定后输入
service transmission startexit #退出jail,回到主系统
至此,完成。
强烈建议使用transmission配置文件中的
incomplete-dir
那个选项,把下载的东西先放到一个MLC的硬盘中,全下载完后,再自动移到主机械硬盘池。
刚才有说到官网的freenas tuning设置,因为每个人的习惯不同似乎没有一个比较统一的说法,这是我的微调:
这个值是脏数据,默认是4GB,这个是内存作为一个缓冲区的大小,单个大文件的话其实没有那么大的区别,但是在小文件为主的写入中,如果缓冲区设置不合理则会导致在高速复制过后机械硬盘IO无法反应导致速度急速下滑。这需要自己去摸索一个比较合理的值,而变量则是这个值以及机械硬盘的实际写入速度(无论是4k或者大文件顺序读写)。
如果不设置的话,默认会用完所有内存作为arc,自己看需求。
这里分别说一下nfs和smb这两种链接方式吧。MacOS源于Freebsd,在MacOS的文件系统中是很早就开始支持Async multi-threads for NFS的,可以参考一下官网的BSD Overview。对MAC用户来说NFS肯定是连接Freebsd NAS的第一选择。
但是在Big Sur之后开始NFS莫名其妙就变得非常慢,查阅了很多资料,问题是:
解决这些问题,在Truenas的Service/NFS中最大化CPU数值,比如我的CPU有12个核心,在Number of Servers中填入12,并且打勾Enable NFSv4
& NFSv3 ownership model for NFSv4
最后,在MacOS下修改/etc/nfs.conf,加入如下代码:
nfs.client.allow_async = 1nfs.client.mount.options = vers=4.0,async,wsize=131072,rsize=131072
修改后,重新挂载nfs生效
你可以在terminal中输入nfsstat -m
来检查链接方式是否正确。
MacOS同时挂载一个aysnc以及一个sync的池非常不方便,推荐NFS Manager,或者用自带的
Automator
来做。
看了Big Sur的一些更新记录,苹果在发力优化Samba,我们可以设置一下来优化的。
参考这个文章,可以把这个文件保存到MacOS的/etc/nsmb.conf,并重启MacOS。
另一方面Samba在Freenas下默认只有上传和下载2个线程,并没有办法修改,这样就非常考验单核能力了。事实上,除了服务端用Win Server的SMB好像没有别的办法。除此之外的话开启async io可以在parameters选项卡下面增加如下参数:
aio read size = 1aio write size = 1
保存即生效。
另外,MacOS 11.3开始支持Samba的multichannel
,可以参考官方文档自行设置体验,我没这个条件测试。
Slog应只应用于当你连接池时使用sync,是保证你数据绝对能写入,并不是帮你增加速度的,比较适用与金融票据等小文件场景,家用还是配好UPS吧。我看到国内很多文章宣称Slog是写缓存,我觉得与我们理解的写缓存差别还是很大的,总之,Slog无法让你获得比将硬盘池的sync关闭后更快的速度。老老实实加ECC内存吧,ddr3内存来一斤。
如果你真的很需要多层存储,可以去研究一下这个玩意:Bcache
L2arc的话是你arc不再增长的情况下用的,在arc仍然growing的情况下使用l2arc是完全没有必要的!你可以通过arc_summary
命令来观察。
10个7200转的硬盘,raid-z2已经达到万兆顺序读取速度,如果需要大量的4k读取以及随机则另当别论,这方面可以参考Meta-Dev
功能。
买来先测一下硬盘的stroke和速度,
diskinfo -ctv /dev/daX#X为你的硬盘编号,在管理页面的storage/disks下按照序列号对照过去
这是我刚用手机测的一块ST EXOS X16 12T的硬盘,刚好达到230MB的速度,stroke也很棒。请尽可能不要购买HGST,太手动挡了,买了后你可能需要刷各种固件以及他们家自己的HUGO软件做很多设置,并且操作比较危险,容易整个盘报废。
TrueNAS官网的Buyer Guide里只推荐WD Red Pro
和希捷。WD和HGST对我这样的老用户来说是两个不一样的品牌,现在产品线比较乱我也分不清,真的喜欢西数就买官网推荐的WD Red PRO。
顺便提一下希捷酷狼pro这个系列,我测出来的一些数据完全跟exos x16一模一样,不知道除了酷狼pro提供一次免费数据修复之外,还有啥区别。
checksum
并通过Geom 实现了data healing
。这话不是让你去优先选择非ecc内存的。我玩pt的时候老是遇到自己的垃圾mlc缓存盘因为过热,内存写入速度过快或者什么的导致缓存盘io suspend。请尽量确保你的pt缓存池已经打开了auto trim
功能。买傲腾当pt下载盘的不需要。
如果你还是碰到io suspend的情况,请先关掉任何有关这个盘的进程。比如先把transmission的jail关了
然后输入:
zpool status #看看你的pt缓存池有没有error,一般会有好多好多。zpool clear -F pt #我的pt缓存池就叫pt,按你自己情况写zpool scrub pt #scrub一下这个池
如果是过热导致的,你还是等他冷一会,或者自己加铜片加风扇什么的。
如果还是不行,使用smartctl查一下盘是不是挂了。
]]>uname -aDarwin xjns-iMac-Pro.local 19.6.0 Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64 x86_64
kASLR
功能 一般存放在/Library/Logs/DiagnosticReports/
目录下,报告以.panic
结尾。比如Kernel_2020-10-15-185538_Patricks-MacBook-Pro.panic
。
随便网上找了一份错误报告:
*** Panic Report ***panic(cpu 6 caller 0xffffff8008b6f2e9): Kernel trap at 0xffffff7f8c7ba8b1, type 14=page fault, registers:CR0: 0x000000008001003b, CR2: 0xffffff80639b8000, CR3: 0x0000000022202000, CR4: 0x00000000003627e0RAX: 0x0000000000000564, RBX: 0x0000000000000564, RCX: 0x0000000000000020, RDX: 0x000000000000002aRSP: 0xffffff92354ebc80, RBP: 0xffffff92354ebce0, RSI: 0x00000000000fbeab, RDI: 0xffffff92487b9154R8: 0x0000000000000000, R9: 0x0000000000000010, R10: 0x0000000000000010, R11: 0x0000000000000000R12: 0xffffff80639b6a70, R13: 0xffffff92354ebdc0, R14: 0xffffff92354ebdd4, R15: 0x0000000000000000RFL: 0x0000000000010297, RIP: 0xffffff7f8c7ba8b1, CS: 0x0000000000000008, SS: 0x0000000000000010Fault CR2: 0xffffff80639b8000, Error code: 0x0000000000000000, Fault CPU: 0x6, PL: 0, VF: 1Backtrace (CPU 6), Frame : Return Address0xffffff92354eb730 : 0xffffff8008a505f6 0xffffff92354eb780 : 0xffffff8008b7d604 0xffffff92354eb7c0 : 0xffffff8008b6f0f9 0xffffff92354eb840 : 0xffffff8008a02120 0xffffff92354eb860 : 0xffffff8008a5002c 0xffffff92354eb990 : 0xffffff8008a4fdac 0xffffff92354eb9f0 : 0xffffff8008b6f2e9 0xffffff92354ebb70 : 0xffffff8008a02120 0xffffff92354ebb90 : 0xffffff7f8c7ba8b1 0xffffff92354ebce0 : 0xffffff7f8c7ba40f 0xffffff92354ebd60 : 0xffffff7f8c7b85e8 0xffffff92354ebda0 : 0xffffff7f8c7b9db2 0xffffff92354ebe00 : 0xffffff7f8b2b3873 0xffffff92354ebe50 : 0xffffff7f8b2bd473 0xffffff92354ebe90 : 0xffffff7f8b2bcc7d 0xffffff92354ebed0 : 0xffffff8009091395 0xffffff92354ebf30 : 0xffffff800908fba2 0xffffff92354ebf70 : 0xffffff800908f1dc 0xffffff92354ebfa0 : 0xffffff8008a014f7 Kernel Extensions in backtrace: com.apple.iokit.IOAcceleratorFamily2(376.6)[5F8F39B4-41AB-3263-9867-D0FAF9BBD2AE]@0xffffff7f8b2b0000->0xffffff7f8b345fff dependency: com.apple.driver.AppleMobileFileIntegrity(1.0.5)[58669FC2-CC90-3594-AD69-DB89B923FD20]@0xffffff7f899ff000 dependency: com.apple.iokit.IOSurface(209.2.2)[AE58720D-7079-388F-AD95-FD2366F98F8D]@0xffffff7f8b294000 dependency: com.apple.iokit.IOPCIFamily(2.9)[C08F7FC1-78A4-3A1B-BFE2-C07080CF2048]@0xffffff7f89294000 dependency: com.apple.iokit.IOGraphicsFamily(517.22)[2AEA02BF-2A38-3674-A187-E5F610FD65B7]@0xffffff7f89a39000 com.apple.kext.AMDRadeonX4150(1.6)[DF336AB9-8300-3ED2-AAD3-7D2C8F4B8DEB]@0xffffff7f8c7b4000->0xffffff7f8cf20fff dependency: com.apple.iokit.IOSurface(209.2.2)[AE58720D-7079-388F-AD95-FD2366F98F8D]@0xffffff7f8b294000 dependency: com.apple.iokit.IOPCIFamily(2.9)[C08F7FC1-78A4-3A1B-BFE2-C07080CF2048]@0xffffff7f89294000 dependency: com.apple.iokit.IOGraphicsFamily(517.22)[2AEA02BF-2A38-3674-A187-E5F610FD65B7]@0xffffff7f89a39000 dependency: com.apple.iokit.IOAcceleratorFamily2(376.6)[5F8F39B4-41AB-3263-9867-D0FAF9BBD2AE]@0xffffff7f8b2b0000BSD process name corresponding to current thread: kernel_taskMac OS version:17C88Kernel version:Darwin Kernel Version 17.3.0: Thu Sep 9 18:09:22 PST 2020; root:xnu-4570.31.3~1/RELEASE_X86_64Kernel slide: 0x0000000008600000Kernel text base: 0xffffff8008800000__HIB text base: 0xffffff8008700000System model name: MacBookPro14,3EOF
错误报告有很多无用内容,我们直接看错误发生在哪里:
panic(cpu 6 caller 0xffffff8008b6f2e9): Kernel trap at 0xffffff7f8c7ba8b1, type 14=page fault
这段告诉我们系统奔溃是因为type 14=page fault
。这种错误往往指出这段内容无法写入或读取一段内存页。
接着,我们需要找到发生错误命令的注册的地址
。在这之前,我们先记录一下它的RIP
,也就是命令在哪一段死了,从报告中搜索RIP
,我们搜到的是0xffffff7f8c7ba8b1
。
据此,根据内存注入的地址,我们得到引起错误的地址
是0xffffff80639b8000
。
Fault CR2: 0xffffff80639b8000, Error code: 0x0000000000000000, Fault CPU: 0x6 ...
这份错误报告同样回溯了这样的错误是通过何种功能方法注入导致的错误指令。
Backtrace (CPU 6), Frame : Return Address 0xffffff92354eb730 : 0xffffff8008a505f6 0xffffff92354eb780 : 0xffffff8008b7d604 0xffffff92354eb7c0 : 0xffffff8008b6f0f9 0xffffff92354eb840 : 0xffffff8008a02120 0xffffff92354eb860 : 0xffffff8008a5002c 0xffffff92354eb990 : 0xffffff8008a4fdac 0xffffff92354eb9f0 : 0xffffff8008b6f2e9 0xffffff92354ebb70 : 0xffffff8008a02120 0xffffff92354ebb90 : 0xffffff7f8c7ba8b1 0xffffff92354ebce0 : 0xffffff7f8c7ba40f 0xffffff92354ebd60 : 0xffffff7f8c7b85e8 0xffffff92354ebda0 : 0xffffff7f8c7b9db2 0xffffff92354ebe00 : 0xffffff7f8b2b3873 0xffffff92354ebe50 : 0xffffff7f8b2bd473 0xffffff92354ebe90 : 0xffffff7f8b2bcc7d 0xffffff92354ebed0 : 0xffffff8009091395 0xffffff92354ebf30 : 0xffffff800908fba2 0xffffff92354ebf70 : 0xffffff800908f1dc 0xffffff92354ebfa0 : 0xffffff8008a014f7
我们需要找到这些内存地址归属于哪一个kexts才能真正去寻找这个错误的来源:
kext: com.apple.iokit.IOAcceleratorFamily2loaded at: 0xffffff7f8b2b0000kext: com.apple.kext.AMDRadeonX4150loaded at: 0xffffff7f8c7b4000
这份报告中可能缺少一些对我们非常重要的信息,但是Kernel slide
值0x0000000008600000
告诉了我们内核镜像如何通过kASLR
转换到了内存中。
总的来说,通过此份报告,我们得到了:
0xffffff80639b8000
发生了错误。0xffffff7f8c7ba8b1
。com.apple.iokit.IOAcceleratorFamily2
和 com.apple.kext.AMDRadeonX4150
这两个kexts是引起错误的主要原因。kASLR
转换进内存的地址是0x0000000008600000
。0xffffff8008a014f7
得到了主要的几个数据后,我们把/System/Library/Kernels/kernel
拖入Hopper disassembler软件里, 因为kASLR
把内核注入到了内存中,我们需要Hopper Disassembler
来重新定位镜像。按Modify
然后点击Change File Base Address
。输入之前得到的slide地址+0x100000
,也就是0x0000000008600000+0x100000=0xffffff8008700000
。
等待内核镜像被重新定位后,我们点击G
输入内存的最后一段地址0xffffff8008a014f7
拆开镜像后我们看到这条指令是直接被加载在一条call
指令之前。
一般来说,我们的CPU收到了call
指令后,它会马上保存这个地址,这样允许我们知道如何去return这个call
以及什么时候这个call
完成。当内核正在准备错误报告时,它通过回溯这个被保存的地址来找到错误关键。因此,当我们遇到一个回溯的地址比如0xffffff8008a014f7
时,它会立即保存并执行回溯。 所以根据这张图,在0xffffff8008a014f5
地址上的call rcx
是产生错误的位置。
我们根据这个地址,按照内存的排列顺序往上回溯,我们可以列出如下信息:
kernel.call_continuation()
kernel.IOWorkLoop::threadMain()
kernel.IOWorkLoop::runEventSources()
kernel.IOInterruptEventSource::checkForWork()
com.apple.iokit.IOAcceleratorFamily2.IOAccelEventMachine2::hardwareErrorEvent()
com.apple.iokit.IOAcceleratorFamily2.IOAccelEventMachine2::restart_channel()
com.apple.iokit.IOAcceleratorFamily2.IOAccelFIFOChannel2::restart()
com.apple.kext.AMDRadeonX4150.AMDRadeonX4150_AMDAccelChannel::getHardwareDiagnosisReport()
com.apple.kext.AMDRadeonX4150.AMDRadeonX4150_AMDGraphicsAccelerator::writeDiagnosisReport()
com.apple.kext.AMDRadeonX4150.AMDRadeonX4150_AMDAccelChannel::writeDiagnosisReport()
com.apple.kext.AMDRadeonX4150.AMDRadeonX4150_AMDAccelChannel::writePendingCommandInfoDiagnosisReport()
mov r8d, dword [r12+rax*4]
我们列出表格后,就可以更轻松看到这个错误是如何发生的。准确地说,这个错误是com.apple.iokit.IOAcceleratorFamily2
kext 在处理硬件时产生的。com.apple.iokit.IOAcceleratorFamily2
产生了restart_channel
,转而把信息递交到了com.apple.kext.AMDRadeonX4150
。一般来说,这个kext是AMD 560显卡的驱动。
除了这个restart_channel
之外,硬件的分析信息也同时生成了。com.apple.kext.AMDRadeonX4150
执行了 AMDRadeonX4150_AMDAccelChannel::writeDiagnosisReport
,这种方法我们叫做writePendingCommandInfoDiagnosisReport
。
我们看到第11个位置,它不是一个call
指令,而是一个move
指令:
0xffffff7f8c7ba8b1 mov r8d, dword [r12+rax*4]
另外,我们在载入com.apple.kext.AMDRadeonX4150
kext,同样发现了这个地址0xffffff7f8c7ba8b1
,与错误报告中的RIP地址一致。另外,下一段地址执行了一个call back
去处理一个trap(call_kernel_trap
),也就是这个报告里面的page fault
。这意味着这个move
指令是导致错误的真正元凶。
现在我们找到了导致错误的真正地址和指令,我们可以更详细地看这个指令。我们看到它在R12这个base上面增加了一个RAX*4
。通过计算我们得到增加到的是R8d
。因为我们原始的错误报告中记录了这个错误指令的注册值,我们可以重新计算这个地址:
registers: CR0: 0x000000008001003b, CR2: 0xffffff80639b8000, ... RAX: 0x0000000000000564, RBX: 0x0000000000000564, ... RSP: 0xffffff92354ebc80, RBP: 0xffffff92354ebce0, ... R8: 0x0000000000000000, R9: 0x0000000000000010, ... R12: 0xffffff80639b6a70, R13: 0xffffff92354ebdc0, ... RFL: 0x0000000000010297, RIP: 0xffffff7f8c7ba8b1, ...
mov r8d, dword [r12+rax*4]R12: 0xffffff80639b6a70RAX: 0x0000000000000564R12 + RAX*4 = 0xffffff80639b6a70 + (0x564 * 4) = 0xffffff80639b8000
计算出来的0xffffff80639b8000
地址,便是我们原始报告中的:
Fault CR2: 0xffffff80639b8000, Error code: 0x0000000000000000, Fault CPU: 0x6 ...
从这里看出来,0xffffff80639b8000
注册在了没有寻址过的页面中,因此,在com.apple.kext.AMDRadeonX4150
中执行mov
命令是不可行的,最终导致系统奔溃。
但是,我们不能100%确定为什么这个指令会在不被寻址的页面中执行。我们因此会有如下的疑问:
R12这个地址是被其他东西占用了或者本身就是无效的吗?
这个增加的RAX值本身就是无效的还是太大了所以超出了范围?
还是这本身就是个硬件问题?
显卡驱动是非常复杂的,并且你可能需要通过很多逆向工程才能获得一些基础的认识。但是我们可以假设应该有方法去绕过这个错误,比如说确认一下这个RAX
值是否在R12
范围内?
根据这种情况,我们何不考虑把R12这个值移动到可读的范围内呢?
我们尝试用一个r8
来替换,发现snprintf
功能引导出来了。
ffffff7f8c7ba8af mov eax, eax ffffff7f8c7ba8b1 mov r8d, dword [r12+rax*4] ;faulting instruction ffffff7f8c7ba8b5 xor eax, eax ffffff7f8c7ba8b7 lea rdx, qword [aC08x] ; "%c%08x" ffffff7f8c7ba8be call 0xffffff7f198091e8 ; snprintf
关于这个会话,macOS确认了ystem V AMD64 ABI。这意味着R8
是第五条arg去引入snprintf
。
int snprintf(char *str, size_t size, const char *format, ...);
这样的话,snprintf
被加入到了%c%08x
,第五个指令就会被寻址到%08x
。这样,R8
就能被注册进寻址过的页面中去了。
因为这个代码被加入到了writeDiagnosisReport
,我们可以猜测带有R8
的snprintf
被写到了user mode之外,因此用这样的方法可以暂时解决。
Objective-See., (2018). An Unpatched Kernel Bug., avaliable at https://objective-see.com/index.html. last accessed at 1st Nov.2020.
]]>Opencore引导的相关设置,已停更。
Xcode (从 App Store 或 Apple Developer 下载)
ProperTree 最新推荐的 config 编辑器
OpenCore(官方稳定版本)
适配OpenCore 0.8.7
LogModules
适配OpenCore 0.7.6
适配OpenCore 0.7.5
适配OpenCore 0.7.2
适配OpenCore 0.6.9
适配OpenCore 0.6.8
适配OpenCore 0.6.7
适配OpenCore 0.6.6
适配OpenCore 0.6.4
适配OpenCore 0.6.3
1.Opencore 0.6.2 正式版修改如下 variables:
直接抄袭 @BAT 了
英文 | 中文 |
---|---|
Fast Boot | 快速启动 |
CFG Lock (MSR 0xE2 write protection) | CFG 锁 (MSR 0xE2 写入保护) |
VT-d | VT-d |
CSM | 兼容性支持模块 |
Intel SGX | Intel SGX |
英文 | 中文 |
---|---|
VT-x | VT-x |
Above 4G decoding | 大于 4G 地址空间解码 |
Hyper Threading | 处理器超线程 |
Execute Disable Bit | 执行禁止位 |
EHCI/XHCI Hand-off | 接手 EHCI/XHCI 控制 |
OS type: Windows 8.1/10 | 操作系统类型: Windows 8.1/10 |
Legacy RTC Device | 传统 RTC 设备 |
将操作系统类型设置为
Windows 8.1/10
是因为部分主板在Other
模式下会将系统认作是 Windows 7 从而禁用 UEFI 的某些功能并开启 CSM, 200 系及以后的主板理论上不存在这个问题
打开下载好的最新版 OC,把 Doc 文件夹下面的 Sample.plist
改名为 config.plist
,并把此文件移动到 EFI 目录下面。
打开 EFI--Kexts
,我们把常用的一些 kexts 先放进去,一般情况下你需要放如下 Kexts:
Lilu.kext Acidanthera驱动全家桶的SDK
Applealc.kext 声卡驱动
VirtualSMC.kext 传感器驱动依赖
SMCProcessor.kext CPU核传感器
SMCSuperIO.kext IO传感器
WhateverGreen.kext 显卡驱动
IntelMausi.kext Intel类千兆网卡驱动
Usbinjectall.kext USB驱动 (你也可以定制自己的USB补丁)
一些机型用了
1820A,1560,1830
等网卡,需要自己放对应驱动;有线螃蟹卡也自己放一下驱动;笔记本类需要更多传感器的,请自行补齐VirtualSMC
的那些传感器补丁
打开EFI--Drives
,里面的驱动介绍如下:
AudioDxe.efi 开机UEFI界面若需要声音效果需要加载。
CrScreenshotDxe.efi 开机UEFI的截图工具。
HiiDatabase.efi 用于给 Ivy Bridge (3 代酷睿) 或更老代主板上支持 UEFI 字体渲染, UEFI Shell 中文字渲染异常时使用, 新主板不需要。
NvmExpressDxe.efi 用于在 Haswell (4 代酷睿) 或更老的主板上支持 NVMe 硬盘, 新主板不需要。
OpenCanopy.efi 加载第三方开机主题。
OpenRuntime.efi 内存运用等必要的插件,必须加载。
OpenUsbKbDxe.efi 给使用模拟 UEFI 的老主板在 OpenCore 界面正常输入用的, 请勿在 Ivy Bridge (3 代酷睿)及以上的主板上使用。
Ps2KeyboardDxe.efi PS2键盘所需插件。
Ps2MouseDxe.efi PS2鼠标所需插件。
UsbMouseDxe.efi 当MacOS被安装在虚拟机上所需要的鼠标插件。
XhciDxe.efi 用于在 Sandy Bridge(2代)及之前或更老的主板上加载XHCI控制器。
HfsPlus.efi 用于HFS格式文件系统,这是必须加载的。
这章会把 config 的项目分开来,内容繁琐,为了让小白明白各个选项的用途。当然有能力的人可以直接看我最前面的几个链接来配置 config.plist
。我这里强制要求你使用 Propertree
来编辑 Config.plist
,其他的任何软件我都不建议使用。
ACPI包括了四个部分:Add
, Block
, Patch
, Quirks
。这里我们先把root下面的几条 #WARNING
删除,这几条没有实际意义。
这部分主要填写我们使用的 SSDT 以及 DSDT 文件,如果没有请把 0-8 的 ssdt 全部删除。如果你有修改的 SSDT 或者 DSDT 文件,请先将文件放入 EFI/OC/ACPI
下。
因为我使用雷电卡,我需要添加两条关于雷电卡的 ssdt 文件:
Item 0Comment String Thunderbolt3-DTGP //填一个你自己能辨别的名字,方便知道是啥Enable Boolean YES //表示加载此SSDT,反之NO则为不加载Path String SSDT-DTGP.aml //为你ssdt放在EFI/OC/ACPI下的文件名,必须一致Item 1Comment String Thunderbolt3Enable Boolean YESPath String SSDT-TB3.aml
这个目录下是禁用一些 SSDT/DSDT,没什么用,我把下面的 item
全都删了。
这里我们需要填写一下热补丁。
Comment: EC0 to ECCount: 0Enabled: YESFind: <4543305F>Limit: 0Mask: <>OemTable: <>Replace: <45435F5F>ReplaceMask: <>Skip: 0TableLength: 0TableSignature: <>
一些主板的EC控制器名字可能会叫 H_EC 等,请自行提取 DSDT 并搜索
PNP0C09
,来获取 EC 控制器的名字,这些内容会在第三章中写出。
AWAC
时钟而无法进入系统,这同样需要添加 hotpatch
补丁来解决:Comment: RTC fixCount: 0Enabled: YESFind: <A00A9353 54415301>Limit: 0Mask: <>OemTable: <>Replace: <A00A910A FF0BFFFF>ReplaceMask: <>Skip: 0TableLength: 0TableSignature: <>
此目录下有五项,选择与解释如下:
NO
NO
YES
NO
NO
BGRT
表的系统上显示 OEM Windows 标志的硬件需要开启NO
内存相关选项设置。
默认的第一项是为 Haswell 芯片提供的内存寻址修复,如果此类芯片碰到内存相关问题,请开启它(enable
选择yes
)。
默认第二项是开机卡 PCI Configuration
这里。ACPI、PCI device 同时释放到内存时发生 0x1000
内存地址被占用而卡在 PCI Configration
,如果碰到此类问题,请开启它。
此项与 OpenRuntime.efi
有关。在 aptiomemoryfix
停更后,此补丁已经更名为 Openruntime
, 并将一些功能与 OC 合并、模块化。对于无法原生 nvram
的主板来说,这里的选项需要格外注意。当然我也会把像 300/400 系列、x299、C246、C422 这样支持原生 nvram
的选择方法一并写进去。
AllowRelocationBlock: NO
AvoidRuntimeDefrag: YES
YES
。DevirtualiseMmio: YES
KASLR
方式(分布式注射到各个内存地址中)以及连续性方式。在使用 KASLR
时,PCIe 加载到内存,可能会占据所有 avaliable
值而影响 OC 的内核以及内核缓存无法注入,导致启动失败。此项目前建议选择YES,并且在下一项 ProtectUefiServices
中也选择 YES
。
KASLR
是更加高效的内存注入方式,但不代表每台机器都能使用这种方案,这里我提供两种关于内存的设置:
1:
DevirtualiseMmio
选择yes
,ProtectUefiServices
选择yes
, 并删除 2.6.1 中boot-args
里面的slide=1
,以及删除 Drivers 文件夹下的Memoryallocations.efi
。即开启KASLR
内存注入方式。
2:
DevirtualiseMmio
选择yes
,ProtectUefiServices
选择no
, 保留 2.6.1 中boot-args
里面的slide=1
,以及保留 Drivers 文件夹下的Memoryallocations.efi
。即开启连续性内存注入方式。
DisableSingleUser: NO
DisableVariableWrite: NO
nvram
,请选择 NO
。非原生NVRAM主板需要模拟 nvram.plist
进而写入 variable
值,因此我们要禁止此项来防止其他程序对 nvram
进行写入,我们这里选 YES
DiscardHibernateMap: NO
NVRAM
都无法进行休眠(注意睡眠 sleep
和休眠 hibernation
是两个概念),台式机的话就更不需要休眠功能了,这里我选择 NO
。这里我们也不讨论如何休眠。EnableSafeModeSlide: YES
YES
,与正常情况下保持一致。EnableWriteUnprotector: YES
nvram
能正常写入而不受到 CR0
寄存器的写入保护影响。ForceBooterSignature: NO
ForceExitBootServices: NO
NO
。ProtectMemoryRegions: NO
AvoidRuntimeDefrag
类似,除非你明白这是什么,不然选择 NO
,其实我也不明白。ProtectSecureBoot: NO
NO
。ProtectUefiServices: NO
memoryallocation.efi
功能类似。ProvideCustomSlide: YES
slide
值(1 - 255)是否可用。由于 boot.efi
生成的这个值是利用 rdrand
指令随机生成的或者伪随机指令 rdtsc
随机生成的,因此当其选择了 一个冲突的 slide
值时有可能启动失败。由于这种潜在的冲突存在,此选项强制 macOS 在可用的值中使用一个伪随机值,这也确保了 slide=
启动参数不会因为安全原因传递给操作系统。Quriks
选项。我选择 YES
。如果你对 KASLR
有一定的认知并会运用,请注意这个值。内容从 @套陆 摘抄。ProvideMaxSlide: 0
KASLR
的话,请填写 1-255
之间的数字以存放休眠文件写进内存所需要的内存高度,反之则填写 0
。RebuildAppleMemoryMap: NO
EnableWriteUnprotector
存在冲突关系,确保开启这个的时候,另一个是关闭的。另外,此项又需要与 SyncRuntimePermissions
项搭配使用。一般情况下请选择 NO
。ResizeAppleGpuBars: -1
此选项用于适配BIOS具备Resizable BAR
功能的主板。BIOS支持此功能可以给Windows带来更好的显卡性能,但另一方面会导致MacOS不稳定。
填如下值对应相对功能:
-1
时:关闭此功能 0
时: 预留1MB1
时: 预留2MB2
时: 预留4MB10
时: 预留1GB0
SetupVirtualMap: YES
YES
。SignalAppleOS: NO
YES
此项是用来注入你的设备的,主要是显卡和声卡两部分。同样你也可以定制一些设备到你的 关于本机--系统报告--PCI
列表中,尽管没有多大的意义。
这里首先我们需要确认自己的声卡驱动已经被加载,终端下输入:
kextstat | grep -E "AppleHDA|AppleALC|Lilu"
我们会得到被加载的驱动,请确保 as.vit9696.Lilu
;as.vit9696.AppleALC
;com.apple.driver.AppleHDAController
;com.apple.driver.AppleHDA
已经被加载。
找自己声卡的地址,准备好在文章开头要求下载的 gfxutil
,将 gfxutil
程序放在桌面,输入:
~/desktop/gfxutil -f HDEF //一般来说我们在使用 Applealc 后,板载声卡的部件名都叫 HDEF
我们输入后会得到声卡的PCI路径,比如我输出的就是:
00:1f.3 8086:a2f0 /PC00@0/HDEF@1F,3 = PciRoot(0x0)/Pci(0x1F,0x3)
这里我们找到的声卡 PCI 路径为 PciRoot(0x0)/Pci(0x1f,0x3)
。我们把预先填写在那里的 PciRoot(0x0)/Pci(0x1b,0x0)
项替换成我们真正的声卡路径。
后面一段我们看到预先填写的声卡ID为 <01000000>
,这里我们需要把它换成合适自己声卡的 ID,输入以下命令得到自己声卡的 CodecID。
ioreg -l|grep IOHDACodecVendorID
点击此页面搜索刚得到的CodecID就可查询到自己声卡的型号名称,以及可用的 LayoutID
。
比如我的 CodecID 为 283906408
,声卡型号 ALCS1220A
,对应 1, 2, 3, 5, 7, 11, 13, 15, 16, 27, 28, 29, 34 的 layout ID
。我们需要一个个测试过去,选定自己能用的。这里我们选择 7 这个 ID 进行测试,将 7 转化成 16 进制格式为 07,后面为了满足格式要求添加 6 个 0,则为 07000000
,将这个值替换刚才预先填的 01000000
中;如果我们测试 ID 为 27,27 的 16 进制为 1b,补上 6 个 0 则为 1b000000
。
PciRoot(0x0)/Pci(0x1f,0x3) device-id data <70a10000> //一般情况下这段是不需要填写的,除非你的声卡需要仿冒 layout-id data <0b000000> //这个Layout id我瞎写的,你按实际情况写
如果你测试的ID都无效,请确保你的操作过程正确、并测试用万能声卡(VoodooHDA)补丁来替换 AppleALC 这个补丁。如果都不行,你可能需要自行编译声卡补丁。
打开 PciRoot(0x0)/Pci(0x2,0x0)
这项,此项为驱动核心显卡。驱动核心显卡我们要分情况讨论:
注意,这里我们只讨论 8代和9代CPU 的机器,其他CPU机型的请在论坛或者黑果小兵博客中搜索相关代码。
8代和9代的核显ID为:3E9b0007
,device ID为 3e9b0000
,但是我们需要符合苹果的倒叙格式填入:
AAPL,ig-platform-id data <07009b3e>device-id data <9b3e0000>framebuffer-unifiedmem data <00000080> //核显显存相关
带f的cpu (e.g. 9100f 9900kf), Xeon 等不带核心显卡的用户不需要管这项,直接把 AAPL,ig-platform-id
选项卡删了。
这种情况我们一般把核心显卡作为加速用,而显示则用独立显卡。这样,我们填一个作为加速用的核显ID即可了,8代9代除外的 cpu 请搜索黑果小兵的博客:
AAPL,ig-platform-id data <0300983e>
注意,10代cpu虽然使用同样的UHD 630,但具体加速代码在iMac 20.1机型下不同,请自行查询
这里是禁用一些设备的,我们按默认就行了,不需要任何修改。
这里是内核相关选项。
这里我们需要填写kexts的相关资料。值得注意的是 OC 的 kexts 填写必须注意顺序,比如 applealc 的依赖是 lilu.kext
,那么 lilu.kext
必须填在第一个,SMCProcessor.kext
依赖于 Virtualsmc.kext
。那 Virtualsmc.kext
必须放在 SMCProcessor.kext
之前。
这里默认情况下很多我们需要的补丁已经被加载里面了,但是 enabled
那块我们要手动改成 yes
去开启它,把一些不用的删了:
Item 0 BundlePath String Lilu.kext //kext的名字 Comment String //你自己填一个注释,可以不填 Enabled Boolean YES //启动此补丁,反之则为关闭 ExecutablePath String Contents/MacOS/Lilu //通过右键kext显示包内容查找lilu运行文件的真正路径 MaxKernel String //此补丁支持的最大系统版本,填19为10.15,18位10.14;我们一般情况下留空 MinKernel String //此补丁支持的最小系统版本 PlistPath String Contents/Info.plist //kext的plist位置,可以右键kext显示包内容查找正确路径 ............ ........... ............ ...........Item 7 BundlePath String USBPorts.kext Comment String Enabled Boolean YES ExecutablePath String //一些没有执行文件的kext不需要填写 MaxKernel String MinKernel String PlistPath String Contents/Info.plist ............ ........... ............ ...........
禁用一些 kexts,这里好像没啥用,不用理会。
此选项帮助 Ivy Bridge 和一些不受支持的CPU加载电源管理的,我们这里不做此方面讨论。所有选项按默认即可。
特殊情况下我们需要强制加载某一些 kext
来达到某种目的,一般我们不理会此项。
这里是为内核打补丁用的,我会在进阶教程中详细写一下这一块。尤其是 rtc 相关的内容,我都会写进第三章的进阶教程。
这里是内核相关的快捷选项,比较重要。
AppleCpuPmCfgLock: YES
YES
。AppleXcpmCfgLock: YES
YES
。AppleXcpmExtraMsrs: NO
Haswell-E
, Broadwell-E
, Skylake-X
这三种 CPU 需要填写 YES
。除此之外的 CPU 选择 NO
。AppleXcpmForceBoost: NO
CustomPciSerialDevice: NO
CustomSMBIOSGuid: NO
DisableIoMapper NO
DisableLinkeditJettison YES
lilu
等插件在 MACOS 11 系统的表现,用来替代 keepsyms=1
。DisableRtcChecksum NO
RTCMemoryFixup.kext
来防止它。ExtendBTFeatureFlags NO
BT4LEContinuityFixup.kext
来实现 continuity
。ExternalDiskIcons NO
ForceAquantiaEthernet NO
DisableIOMapper
、开启BIOS中的VT-D等选项以确保VT-D在MacOS中AppleVTD
正常运行。ForceSecureBootScheme NO
SecurebootModel
时考虑开启。IncreasePciBarSize NO
LapicKernelPanic NO
LegacyCommpage NO
PanicNoKextDump YES
kext
出错打报告而让我们看不到真正的 panic
原因,初始排错时最好打开。PowerTimeoutKernelPanic YES
YES
。ProvideCurrentCpuInfo NO
SetApfsTrimTimeout -1
APFS文件系统根据空间是否被使用
或者是空的
来设计的,这种设计跟其他文件系统是不同的,一般被设计成被使用
,空的
或者未映射
三种情况。这种不同可能带来在启动时系统需要更多的时间来做trim,甚至导致无法进入系统。这个选项是为了设计一个时间节点来终止trim进入系统。一般来说填入-1
是不阻止这个trim过程,但如果你有这个问题,你可以设置一个时间,单位为微秒。
因最近的系统更新,当trim过程超过10秒,系统开机时苹果会自动跳过trim,这样会导致一些品牌的ssd无法正常工作,出现系统崩溃,冻屏,死机等问题。解决的办法有两种,一种是将此选项设置一个超长的时间来保证trim结束,(e.g.4294967295),带来的后果是开机非常慢,第二种方法是设置一个非常短的时间直接跳过trim过程(e.g.999)。关闭trim会带来大量的问题,所有的闪存不具备覆写的功能(除了傲腾),需要通过trim来删除日志中需要删除的空间,不然的话,硬盘的速度会直线下降。我们应该避免使用有这类问题的SSD。
目前检查到的兼容性不够好的硬盘包括并不限于:
ThirdPartyDrives NO
trim
功能,我没有 SATA 类的 ssd,我选择 NO
。自行根据情况选择。XhciPortLimit YES
CustomKernel NO
FuzzyMatch NO
KernelArch NO
x86_64
。KernelCache Auto
这里都是一些开机引导类的设置。
用于覆盖 Windows bootmgfw.efi
的位置以便识别 Windows 引导项, OpenCore
和 Windows
的引导文件在同一硬盘的同一个 ESP 分区下使用
▼ Misc <Dictionary>|__ ▼ BlessOverride <Array> |__ Item 0 <String> \EFI\Microsoft\Boot\bootmgfw.efi
ConsoleAttributes 0
(0x01)
+ 红色背景 (0x40)
= 0x41
。色彩选择如下:字体颜色
背景颜色
HibernateMode None
检测休眠模式。与系统内的休眠模式 (hibernatemode 25) 配合, 引导进系统会还原休眠前的状态, 建议关闭
None
: 关闭休眠支持
Auto
: 自动检测 RTC 和 NVRAM 模式
RTC
: RTC 模式
HibernateSkipsPicker NO
picker
,一般不开启。HideAuxiliary NO
LauncherOption Disabled
Disabled
:对启动项不做任何事Full
:把OC的启动项永远置顶在uefi启动顺序中,填入此项后,你必须同时打开RequestBootVarRouting
Short
:主要给Insyde
cpu来使用并创建OC启动项。LauncherPath Default
Default
: 自动加载默认的OpenCore.efi路径+xxx.efi
:给出指定路径来加载OC。比如我们把OpenCore.efi更名为qq.efi并放在\EFI\qq.efi,你就可以填写\EFI\qq.efi
PickerAttributes 17
当你使用OC主题时,你可以通过计算以下数值之和来配合使用OC主题,建议使用数字17
0x0001
: 是否加载自制的图标。如果是tool类的图标应该放在Resource/Image/xx.icon(与tool同名);如果是自定义路径的启动硬盘,应放在同样位置并被命名为路径.icns
。
0x0002
: 在图标下显示渲染的文字指示。
0x0004
: 使用默认的图标。
0x0008
: 使用老式的图标。
0x0010
: 在主题界面中允许使用鼠标。
使用
17
这个值意味着是1+2+4+10
PickerAudioAssist NO
PickerMode Builtin
Builtin
: 不使用任何主题External
: 调用第三方主题Apple
: 给白果用的PickerVariant Auto
Auto
: 自动选择,根据DefaultBackgroundColour
来自动选择并具有时间控制的黑夜或白天的模式。Default
: 使用Resource/Images/GoldenGate/ 这个主题填路径
:使用自己的主题,主题文件夹该放在Resource/Images/下,比如Resource/Images/customised/xjn,则在这个选项中填customised/xjn官网推荐主题下载
请将下载好的Resources件放入ESP/EFI/OC下,同时,你需要将OpenCanopy.efi放入Drivers文件夹下并加载。
PollAppleHotKeys YES
ShowPicker YES
TakeoffDelay 0
Timeout 5
是否开启debug模式,这里我们暂时不需要,全部按默认设置。
这里是帮助我们添加一些你希望的引导路径,这个会在之后的进阶教程中讲,这里暂时略过不填写。
AllowSetDefault YES
yes
后即可在开机选择系统页面中通过 Ctrl+Enter 键设置默认启动盘。ApECID 0
AuthRestart NO
Filevault
相关项,选择 NO
。BlacklistAppleUpdate YES
DmgLoading Signed
Any
;如果使用安全启动,请填写 Signed
(注意大小写)。EnablePassword NO
ExposeSensitiveData 2
nvram
,填 3,如果你有原生 nvram
,填写 2。HaltLevel 2147483648
PasswordHash 留空
EnablePassword
,则填写密码的 hash
值。PasswordSalt 留空
EnablePassword
,则填写密码的 salt
值。ScanPolicy 0
SecureBootModel Default
Vault Optional
Optional
不开启它。init NO
Override NO
CustomPciSerialDevice
选项,另自定义串口设备的选项请查阅官方说明文档,此文不展开描述。用于运行 OC 调试工具, 例如验证 CFG 锁 (VerifyMsrE2)
NO
NO
默认不隐藏随便填
YES/NO
Auto
PickerAttributes
设置成0x0080
时适应主题需求NO
OpenRuntime.efi
对NVRAM的保护,一般情况请关闭。名称(如 openshell )
文件名称(如 openshell.efi)
Tools
文件夹下的文件名NO
NO
4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14 DefaultBackgroundColor Data <00000000> //默认开机背景色为黑色7C436110-AB2A-4BBB-A880-FE41995C9F82 boot-args String darkwake=0 -v //darkwake=0 代表一键唤醒机器并偏好设置中节能选项的小憩功能。如果你要用小憩功能请填8; -v 是跑代码,在没装好稳定的黑果前我建议加上,方便定位错误,弄完后再删除 -v csr-active-config Data <e7030000> //关闭 SIP 保护 nvda_drv Data <31> //对 10.13 系统之前的N卡的相关设置,我们不做讨论。 prev-lang:kbd Data <7a682d48616e733a323532> //语言设置相关,记得改成这个,这个是中文 ForceDisplayRotationInEFI Number 0 开机的UEFI界面是否需要调整,比如你是竖屏的,可以填0,90,180,270。请同时开启AppleEg2Info 7C436110-AB2A-4BBB-A880-FE41995C9F82 //默认就行,如果需要使用 RTC 屏蔽选项,具体参考RTC综述
当你的机型为带有内屏(e.g.iMac19.1),并且你使用Navi系列显卡,请在boot-args中增加agdpmod=pikera
NVRAM的数据不可被覆盖,必须先被删除再添加,我们这里按默认设置不必理会。
这里是模拟 NVRAM
的变量设置,大部分默认已经填好,我们只需添加两个变量即可。
打开 7C436110-AB2A-4BBB-A880-FE41995C9F82
这一栏,添加两个item如下:
item 11 String efi-boot-deviceitem 12 String efi-boot-device-data
一般情况下我们需要选择 YES
来保证启动磁盘
功能的正常使用,但开启后可能会在一段时间后导致 CMOS
被写满,主板无法经过自检。这样的问题请请参照RTC综述
对模拟 nvram
用户来说,将 nvram.plist
写入硬件,我认为不管是原生 nvram
还是模拟 nvram
,都选择 NO
。
这里我们填合适的机型。对于最近一代的主板来说,一般的原则,只有核显的机器我们选Macmini8,1;只有独显的机器我们选择iMac Pro 1,1;有核显和独显的我们选择iMac 19,1。笔记本请按照对应的cpu型号来选择。
是否自动补全系统信息。这里我选 YES
,不重要的信息让它自动填。
自定义内存选项,请选择NO
这里是我们需要填写的三码部分。
将 OpenCore
包下 Utilities/macserial
程序放到桌面,在终端下输入以下命令
~/desktop/macserial --model iMacPro1,1 //你也可以换成你想要的机型比如iMac 19.1
输入后你回获得一些序列号以及主板主板序列号,请自己选用一组,填写到 MLB
以及 SystemSerialNumber
后重启。
重启后,请再在终端下输入:
ioreg -d2 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $(NF-1)}'
得到你的主板 UUID,填入 Generic
的 SystemUUID
(此操作可以帮助你的 win 不丢失激活)
AdviseFeatures YES
MaxBIOSVersion 0
9999.999.999.999.999
。SystemMemoryStatus Auto
Auto
。SpoofVendor YES
YES
。ProcessorType 0
UpdateDataHub YES
UpdateNVRAM YES
UpdateSMBIOS YES
UpdateSMBIOSMode Create
EfiReservedMemoryType
替换原有的表, 戴尔笔记本需要使用 Custom
并开启 CustomSMBIOSGuid
UseRawUuidEncoding NO
NO
AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP
。解码后即是AA BB CC DD EE FF GG HH II JJ KK LL MM NN OO PP
,但在实际系统中显示DD CC BB AA FF EE HH GG II JJ KK LL MM NN OO PP
。你需要开启这个选项。EnableJumpstart YES
YES
。此选项仍然依据你的Scanpolicy
来做出决定,请确保在Scanpolicy
中放开 APFS 格式。GlobalConnect NO
HideVerbose YES
YES
。JumpstartHotPlug YES
MinDate 0
0
。如果你想加载旧的发行日期的 APFS 格式硬盘,请填 -1
。MinVersion 0
0
代表从 HIGH SIERRA
开始加载。填 -1
代表所有版本,建议填 0
。AppleEvent Auto
Builtin
:使用OpenCore的控制OEM
:使用Apple自带的控制Auto
:自动选择CustomDelays NO
AppleEvent
=OEM 时此选项无效。NO
:使用Apple默认的500ms首次延迟和50ms的随后延迟。YES
:根据KeyInitialDelay
KeySubsequentDelay
来设置延迟。GraphicsInputMirroring YES
NO
: 使用Apple的键盘控制,这可能导致在一些环境下无法使用键盘输入(e.g. Windows BitLocker)。YES
:可以让OpenCore在引导一些系统的UEFI模式下时仍然可以使用键盘输入。KeyInitialDelay 50
CustomDelays
=True时生效。KeySubsequentDelay 5
CustomDelays
=True时生效。PointerDwellClickTimeout 0
PointerDwellDoubleClickTimeout 0
PointerDwellRadius 0
UIScale
来确定。在容忍度内的值,则认为是指针设备悬停了,则触发PointerDwellClickTimeout
和PointerDwellDoubleClickTimeout
。PointerPollMask -1
PointerPollMax 0
PointerPollMin 0
PointerSpeedDiv 1
PointerSpeedMul 1
此项的内容是帮助你在开机阶段驱动板载音频,此项对DP等数字音频无效。
AudioCodec 0
PinConfigurator
提取。AudioDevice 0
PciRoot(0x0)/Pci(0x1f,0x3)
,请按你自己的实际情况填写。AudioOutMask 0
PinConfigurator
提取。AudioSupport NO
DisconnectHda NO
MaximumGain -15
MinimumAssistGain -30
MinimumAudibleGain -128
PlayChime Auto
Enabled
。ResetTrafficClass No
AppleALC.kext
时驱动声卡,一些芯片组上的声卡并不被设置为TC0,比如ICH09。当我们使用AppleALC时,默认被设置为了alctsel。SetupDelay 0
codecs
要求特殊的延迟才能正常使用。一般填0,而如需填写以0.5为单位增加。VolumeAmplifier 0
开启UEFI加载阶段的音频你必须下载Audio文件夹,并把 Audio 文件夹放到
ESP/EFI/OC/Resources
下。另外你必须注意AppleAudio
选项。
是否加载补丁,我们选择 YES
。
Drivers... item0 Dictionary Arguments...........String ..... Comment.............string...... HFSPlus Enabled.............Boolean..... Yes LoadEarly...........Boolean..... No Path................String...... HfsPlus.efi... item1 Dictionary Arguments...........String ..... Comment.............string...... OpenRuntime Enabled.............Boolean..... Yes LoadEarly...........Boolean..... No Path................String...... OpenRuntime.efi ... item2 Dictionary Arguments...........String ..... Comment.............string...... OpenCanopy.efi Enabled.............Boolean..... No (请参考PickerMode选项设置) LoadEarly...........Boolean..... No Path................String...... OpenCanopy.efi 3 ....................
此选项是原生 Apple 开机热键的选项,需要配合我们之前设置的 PollAppleHotKeys = yes
一起用。下面的设置完全按照默认情况就行了。
如果你是华硕的z87或者z97,你需要打开PointerSupport这个选项。
ClearScreenOnModeSwitch NO
NO
。ConsoleMode MAX
MAX
,或者留空。DirectGopRendering NO
ForceResolution NO
Ironlake/Arrandale
及之前的核心显卡需要开启此功能来自定义分辨率。GopPassThrough Disabled
ProvideConsoleGop
。IgnoreTextInGraphics NO
-v
跑马模式时候,开机日志导致的苹果 LOGO 显示不正确的问题。ProvideConsoleGop YES
ReconnectGraphicsOnConnect NO
ReconnectOnResChange NO
NO
。ReplaceTabWithSpace NO
UEFI Shell
下 Tab 功能键不生效,开启这个会用空格键代替。Resolution 留空
3840x2160
。根据情况填写或者不填。SanitiseClearScreen YES
TextRenderer BuiltinGraphics
UIScale 2
1
: 一倍拉伸,正常显示2
: 2倍拉伸,等同于苹果的HiDPI效果-1
: 不做任何设置0
: 自动选择 UgaPassThrough NO
NO
。AppleAudio NO
UEFI--Audio
的正确设置。AppleBootPolicy NO
AppleDebugLog NO
AppleEg2Info NO
ForceDisplayRotationInEFI
调整开机时画面的显示角度,当你要用竖屏的时候不仅要在nvram中设置ForceDisplayRotationInEFI
的角度,这个也要开启。AppleFramebufferInfo NO
NO
。AppleImageConversion NO
AppleImg4Verification NO
AppleKeyMap NO
AppleRtcRam NO
AppleRtc
协议。AppleSecureBoot NO
AppleSmcIo NO
VirtualSMC.efi
,在使用vault
功能时需要开启。AppleUserInterfaceTheme NO
Apple User Interface Theme
协议。DataHub NO
Datahub
。DeviceProperties NO
YES
才能注入 Device Property
,我们选NO。如果你发现你注入 Device Property
无效,请选择 YES
。FirmwareVolume NO
HashServices NO
OSInfo NO
NO
。UnicodeCollation NO
NO
。ActivateHpetSupport NO
EnableVectorAcceleration YES
EnableVmx NO
DisableSecurityPolicy NO
不要
在BIOS中开启Secure Boot
并同时开启这个!ExitBootServicesDelay 0
0
。旧的主板比如 Z87pro,填 3000000-5000000
。ForceOcWriteFlash No
ForgeUefiSupport NO
IgnoreInvalidFlexRatio NO
MSR0x194
,一定要选YES。ReleaseUsbOwnership NO
NO
。如果你开机键盘鼠标卡死了,或者 USB 失灵,试试选 YES
。ReloadOptionRoms NO
ForgeUefiSupport
使用。RequestBootVarRouting YES
启动磁盘
的可靠性。ResizeGpuBars -1
ResizeAppleGpuBars
是针对MacOS系统的。你可以设置的大小如:-1
时,此功能关闭0
时,设置BAR为1MB1
时,设置BAR为2MB2
时,设置BAR为4MB19
时,设置BAR为512GBMacOS的极限为1GB,此选项无法更改MacOS的上限。
TscSyncTimeout 0
TSCAdjustReset.kext
等类似补丁,推荐的值是 500000
。但是此选项并不能在睡眠唤醒后生效,所以请填写默认值 0
,并使用 TSCAdjustReset.kext
来做全核同步。UnblockFsConnect NO
此项为保留内存所用。一些硬件会把硬件 EFI 写进内存过程中占据必要的 UEFI 运行空间,所以我们可以通过此项来预留内存来保证 UEFI 的运行。填写方式可以参考小兵的文章。来寻找指定内存的起始位置,以 4K 为一个节点。一般情况下,此项我们并不需要理会。
对 OC 而言,NVRAM
是非常核心的一环,不管是原生还是模拟的。如果你是原生nvram的主板,请不必理会这章节。
这章节的主要内容为非原生 NVRAM 模拟生成 nvram.plist
。
首先打开我们之前下载好的 OpenCore,进入目录下的 Utilities/LogoutHook
文件夹,你会看到 LogoutHook.command
文件。
打开 Terminal(终端)并输入 cd ~
返回到用户根目录,输入 mv
和一个空格,然后鼠标拖动 LogoutHook.command
文件到 Terminal(终端),再在终端输入 ~/.LogoutHook.command
例如(不要直接复制这条命令行) mv /Users/xjn/Downloads/OpenCore-0.6.2-RELEASE/Utilities/LogoutHook/LogoutHook.command ~/.LogoutHook.command
sudo defaults write com.apple.loginwindow LogoutHook /Users/$USER/.LogoutHook.command
终端会提示要求你输入密码(密码打进去不会显示)。
重启,你会在 ESP/EFI/
下看到 nvram.plist
,代表已经成功模拟了。
这个教程主要针对的是非原生 nvram
主板的用户,如果你是原生 nvram
的用户,直接在偏好设置—启动磁盘中选定你希望设置为默认启动的磁盘即可,不必往下看浪费时间!
此步骤可能只适合 OC 纯净安装的系统,如果你之前使用过 clover 或者 oc mod 版本,请参照《精解opencore》中模拟 nvram 的方法。
非原生 nvram 用户必须完成以下几点:
通过 3.1的教程 建立 nvram.plist
.
确保 OpenCore 的版本为 2019年11月20日 之后的。更新 OpenCore 替换 BOOT/BOOTx64.efi
以及 OC/OpenCore.efi
即可。
确保 config.plist
下 /Misc/Security/
的 ExposeSensitiveData=3
; Booter/Quirks/
的 DisableVariableWrite=YES
; NVRAM/LegacyEnable
= YES
确保文件夹 ESP/EFI/OC/Drivers/
下没有 VariableRuntimeDxe.efi
如果你是华硕的非原生nvram主板,你还需要做(此文件由群友 @哞 提供):
LogoutHook.command
放在一起,并执行:sudo defaults write com.apple.loginwindow LoginHook ~/.LoginHook.command
3.2.1 以及 3.2.2 也是排列启动盘开机进入顺序的,比较麻烦,不推荐。
我们首先要对 Config--Misc--Security--scanpolicy
这个值进行修改,默认的是 0,代表着扫描所有硬盘,而我们现在只让它扫我们的苹果系统硬盘。
感谢 @xlivans 提供的 OC 扫描策略:
-
2F0303
—官方推荐值+允许扫描HFS文件系统+允许扫描USB设备
最终
扫描策略数值
=3080963
OC-引导扫描策略
- 设置位置:
Misc\Security\ScanPolicy
- 定义:
(01)0x00000001 — 限定为文件系统,由以下
允许扫描文件系统子项
开启
(02)0x00000002 — 限定为设备类型,由以下
允许扫描设备类型子项
开启
允许扫描文件系统子项
:
(03)0x00000100 — 允许扫描APFS文件系统
(04)0x00000200 — 允许扫描HFS文件系统
(05)0x00000400 — 允许扫描EFI系统分区文件系统
允许扫描设备类型子项
:
(06)0x00010000 — 允许扫描SATA设备
(07)0x00020000 — 允许扫描SAS和Mac NVMe设备
(08)0x00040000 — 允许扫描SCSI设备
(09)0x00080000 — 允许扫描NVMe设备
(10)0x00100000 — 允许扫描CD / DVD设备
(11)0x00200000 — 允许扫描USB设备
(12)0x00400000 — 允许扫描FireWire设备
(13)0x00800000 — 允许扫描读卡器设备
扫描策略数值
=(01)+(02)+1个或数个允许扫描文件系统子项
+1个或数个允许扫描设备类型子项
例如:希望扫描对象是APFS文件系统的USB设备,
扫描策略数值
=(01)+(02)+(03)+(11),经16进制加法计算得出,扫描策略数值
=0x200103
。
注意
,使用时需将16进制转换为10进制。示例最终扫描策略数值
=2097411
- 几种扫描策略
-
F0103
—官方推荐值:(01)+(02)+(03)+(06)+(07)+(08)+(09)
最终
扫描策略数值
=983299
-
0
—允许扫描所有已知文件系统+允许扫描所有已知设备类型。
-
2F0303
—官方推荐值+允许扫描HFS文件系统+允许扫描USB设备
最终
扫描策略数值
=3080963
我们这里让 OC 只扫描 MAC 盘。根据说明,(1)+(2)是必须选的;因为我的 MAC 是 APFS,所以系统子项类我选(3);我的 MAC 是安装在 NVME 上的,所以我在设备类型上选择(09),如果你是安装在 SATA 盘上的,你应该选(6)。
这样我们的公式就是:
(1)+(2)+(3)+(9)= 0x00000001+0x00000002+0x00000100+0x00080000
拿起苹果自带的计算器,按住command+3
切换到编程型计算器,并转换到16进制模式:
1+2+100+80000=80103
得到数值后,我们再按一下转换10进制的按键,得出 524547
这个数值。
我们把 524547
填到 scanpolicy
中,重启,你就会看到第一个选项就是你的MAC盘了。但如果你还安装了WINDOWS什么的,选项中却没有了,别急,我们现在要把WIN的引导添加到mac的后面去。
现在我们要在开机画面中,将其他的一些系统排在mac后面。此项需要你下载debug版本的Opencore,请在文章开始部分提到的Opencore程序下载界面进行下载,并备份好你目前的EFI。
首先我们需要把下载好的 Opencore-Debug 版本里的 ESP/EFI/BOOT/BOOTx64.efi
以及 ESP/EFI/OpenCore.efi
两个文件替换到你正在使用中的 EFI 里去。修改你正在使用的 EFI/OC/Config.plist
:
Misc Debug DisableWatchDog Boolean YES Target Number 65 Security ScanPolicy Number 0 //这里我们要先改为0来寻找windows的地址,之后找到后改回之前算出来的即可
接着,我们需要寻找你其他系统的UUID,我们打开终端,输入
diskutil list
找到你需要的盘的盘名,比如我的windows在disk0这个位置,而引导WIN的EFI文件夹的盘位是disk0s1。注意,Windows必须为GPT引导模式
。
/dev/disk1 (internal, physical): #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *1.0 TB disk0 1: EFI 314.6 MB disk0s1 2: Microsoft Basic Data BOOTCAMP 999.7 GB disk0s2
接着输入:
diskutil info disk0s1
在输出内容里,我们需要的是 Disk / Partition UUID
,我的是 FF555974-AB3F-40B7-8530-AE6462E197CE
,把它记下来。
现在我们通过oc的开机选项直接重启到windows,我们打开DiskGenius,并且到我们mac的EFI盘符下,我们看到有一个日志报告 opencore-xxxxx-xxxxx
生成,为了好记,我们右键把它改名叫 111.txt
,并拖到桌面。
打开刚才的 111.txt
,我们搜索刚才记录下来的 FF555974-AB3F-40B7-8530-AE6462E197CE
搜到类似这样的路径就是我们需要的:
PciRoot(0x0)/Pci(0x1B,0x0)/Pci(0x0,0x0)/NVMe(0x1,CB-27-B3-91-53-38-25-00)/HD(1,GPT,FF555974-AB3F-40B7-8530-AE6462E197CE,0x800,0x96000)/\EFI\BOOT\BOOTX64.EFI
把之前我们备份好的EFI,全部替换回去。把 disablewatchdog
,target
,scanpolicy
都改回原来的。这样我们的EFI又回到正式版本了,而不是 DEBUG 版本了。
回到Mac,打开 config.plist
,我们把 win 的引导路径添加到 misc--entries
,并在此下面添加一个 item,输入如图内容:
Entries Arguments String Comment String Windows 10//你随便取个名字 Enabled Boolean YES Name String Windows 10/开机选择界面的名字 Path String PciRoot(0x0)/Pci(0x1B,0x0)/Pci(0x0,0x0)/NVMe(0x1,CB-27-B3-91-53-38-25-00)/HD(1,GPT,FF555974-AB3F-40B7-8530-AE6462E197CE,0x800,0x96000)/\EFI\BOOT\BOOTX64.EFI TextMode Boolean No
保存重启后,你会看到windows10放在了mac的下面。
tips: 你可以
showpicker
选择no,就不会看到选择界面了,而当你需要时,只需要在开机时候按住option或者esc就可以唤出选择界面了,非常白果的体验哦。
尽管提取原始DSDT的方法非常多,我认为 CLOVER
的提取方法是最方便并且靠谱的。我们需要一个空的U盘或者空的ESP分区,我的教程是非常偏向小白的,所以这里提取我也会用到windows,以及Diskgenius这个软件,做最简单的示范。
进入Windows,插入U盘,打开DiskGenius,选中我们的U盘,并选择顶部菜单栏的快速分区
GUID
ESP
分区MSR
分区FAT32
格式化完成后,放入我从黑果小兵镜像包提取出来的EFI放进去。这是一个 clover 引导,但并不能引导你的系统,只能提取 DSDT。
插上U盘,重启,通过U盘引导,看到Clover界面,我们按F4,这样原始的DSDT文件就收集好了。
重新通过OC引导进入系统,我们打开U盘,EFI/Clover/ACPI/Orgin下,有我们的原始ACPI内容,我们只需要DSDT.aml这个就行了,保存到安全的地方。
提取 DSDT 后使用 MaciASL
打开,搜索你 CPU 的名字。一般情况下,CPU 的名字可能是: PR.CPU0
,PR.P000
,PR.PR00
,SB.CPU0
,SB.P000
,SB.PR00
, SCK0.C000
, SCK0.CPU0
。请依次搜索直到找到自己的CPU名字,比如我的就是SCK0.C000
Processor (C000, 0x00, 0x00001810, 0x06) { Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID Name (_UID, "SCK0-C000") // _UID: Unique ID Method (_PXM, 0, NotSerialized) // _PXM: Device Proximity { If ((CLOD == 0x00)) { Return (0x00) } Else { Local0 = DerefOf (APT0 [0x00]) Local1 = CNBS /* \CNBS */ Local1 -= 0x01 Local0 >>= Local1 Local0 &= 0x01 Local1 = 0x00 Local1 *= 0x02 If ((Local0 == 0x01)) { Local1 += 0x01 } Return (Local1) } }
打开宪武大大的OC Little,到 05-注入设备/05-1-注入x86/
目录下,我们看到宪武大大已经把大部分不同名字的CPU的 dsl 文件都做好了。我的 CPU 名字叫 SCK0.C000
,打开 SSDT-PLUG-_SCK0.C000.dsl
,左上角另存为(save as), 其中文件格式(file format)必须选择 ACPI Machine Language Binary
,文件名字随便写吧,我就叫 plug-xcpm.aml
,记住后缀为 .aml
。
将 plug-xcpm.aml
放入 EFI/OC/ACPI
下,并在 config.plist
中添加加载此 aml 文件:
ACPI Add Item 0 Comment String plug-xcpm Enable Boolean YES Path String plug-xcpm.aml
加载后,重启,并清理一次 nvram
,我们看到偏好设置–节能中,原生电源管理已经被加载了。
节能五项是白果台式机中,系统偏好设置
—节能
中的五个选项。在加载原生电源管理后,一般有4项节能出现,而第五项“断电后重启”这项还需要加载 PPMC
以及 LPCB
下的 PMCR
才能出现。虽然没啥鸟用,但对于强迫者而言,少一个一定很难受吧。如果你是笔记本,不需要看这章,白果笔记本本身没有。如果你没有机械硬盘,也不会出现 Put hard disks to sleep when possible
。
直接下载SSDT-PM.aml载入即可。
bugprogrammer给出了一个一劳永逸的解决方案:
自MacOS 11起,解锁/Library/system/extensions/不再适用,因系统快照签章。
如果要修改此目录,可以利用Opencore的Kernel block功能先禁用Library/system/extensions的某一个kext,再把修改好的kext通过OC加载;亦可以放置/library/extensions目录。
EC控制器是电脑自带的一个叫 embedded controller
的部件。在 10.15 的系统环境中,笔记本电脑必须重命名原来的 EC,而一些台式机主板则需要禁用。下面我会分开来讲解如何禁用 EC、如何加载 USB 电源管理支持。
之前的章节中提供了重命名这种方式去讲 EC0 更名为 EC,其实这种方法是只适合笔记本的,台式机最好还是禁用 EC。
打开之前提取出来的 DSDT.aml,搜索 PNP0C09
,这里我搜到我的EC真实名字叫做 H_EC
,你的可能叫 EC0
或者别的什么奇怪的名字。
这里我可以看到我的 H_EC
已经是屏蔽掉的,怎么判定,你看下面有一个 Return (Zero)
,意味着这个部件是不生效的,即禁用。这样的情况我们不需要做任何 SSDT 去禁用这个真的 EC。
Scope (_SB.PCI0.LPCB) { Device (H_EC) { Name (_HID, EisaId ("PNP0C09") /* Embedded Controller Device */) // _HID: Hardware ID Name (_UID, One) // _UID: Unique ID Method (_STA, 0, NotSerialized) // _STA: Status { ^^^GFX0.CLID = 0x03 Return (Zero) }
那么什么样的情况是需要我们去屏蔽的呢?我发现基本上华硕的台式机主板都会启用这个EC控制器,下面代码是华硕主板的EC部件,搜索 PNP0C09
,我们看到这种情况下,这个叫 EC0 的 EC
控制器是开启的,注意他没有 return (Zero)
这个语句,我们需要通过 SSDT 去屏蔽它。
Device (EC0) { Name (_HID, EisaId ("PNP0C09") /* Embedded Controller Device */) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0062, // Range Minimum 0x0062, // Range Maximum 0x00, // Alignment 0x01, // Length ) IO (Decode16, 0x0066, // Range Minimum 0x0066, // Range Maximum 0x00, // Alignment 0x01, // Length ) })
这里我提供了一个禁用 EC 的补丁,请直接下载,打开后左上角另存为(save as), 其中文件格式(file format)必须选择 ACPI Machine Language Binary
,文件名字随便写吧,我就叫 ssdt-no-EC.aml
,记住后缀为 .aml
。将 ssdt-no-EC.aml
放入 EFI/OC/ACPI
目录下,并在 config.plist
中加载此 aml 文件。
直接下载SSDT-PM.aml载入即可。
如果你的 EC 名字叫 H_EC 或者别的什么的,你打开这个 .dsl
文件,替换代码中所有的 EC0 为 H_EC。
{ External (_SB.PCI0.LPCB, DeviceObj) External (_SB_.PCI0.LPCB.H_EC, DeviceObj) Scope (\_SB.PCI0.LPCB.H_EC) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0) } Else { Return (0x0F) } } } }
当你的电脑没有EC后,你仍然需要仿冒EC来完成全部操作。
笔记本不能禁用 EC,禁用 EC 后会直接导致笔记本没有电池。那么我们怎么去重命名 EC 呢?按照上面提到的方法,找到你 EC 的真实名字,在之前的章节中我提供了假如你的笔记本的 EC 叫做 EC0
时候的重命名办法,那如果你的 E C叫别的乱七八糟的名字呢?
EC 名,比如你的 EC 叫做 H_EC
,我们打开在线的HEX转换器,输入 H_EC
,并点击下面的16进制转换,就可以看到转换出来的值是 485F4543
,把这个值替换到 Find
这个选项卡中就行了。你也会注意到,EC 的 hex-16 进制为 45435F5F
,刚好是 Replace
的值。这就是一个非常简单实用的 OC 重命名。切记!OC 万不得已不要用重命名!
Comment: H_EC to ECCount: 0Enabled: YESFind: <485F4543>Limit: 0Mask: <>OemTable: <>Replace: <45435F5F>ReplaceMask: <>Skip: 0TableLength: 0TableSignature: <>
禁用EC后你需要创建仿冒EC来进入系统,同时开启USB的快速供电技术(需设备支持)。你可以比较下面两张图,第二张是功能正确开启的。
这部分内容是仿冒 EC,注意不是每一个DSDT的路径都在 SB.PCI0.LPCB
,请搜索 0x001F0000
来确定实际位置和名称。比如我的主板是在 SB.PC00.LPC0
。
Scope (\_SB.PCI0.LPCB) { Device (EC) { Name (_HID, "ACID0001") // _HID: Hardware ID Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } }
睡眠即醒很大程度上跟 USB 的定制相关,一般一个好的 USB 定制就能解决睡眠即醒的问题。当然还有很多无法解决的问题,比如蓝牙不能在 HUB 下进行内建,等等。甚至有些时候我们都不知道为什么黑果会睡不着,那有没有一个办法让黑果强制睡眠呢?答案是有的。经过我的摸索,有几种方法能达到强制睡眠的效果,只是方法不同而已,但主要围绕的还是 0d/6d
的数值来做一些工作。这些方法涉及了很多 OC 领域的一些小技巧,我也顺便展示给大家。
0d/6d
补丁是阻止一些部件参与唤醒工作,这其中包括了xhc部件,意味着你无法使用鼠标键盘唤醒,只能用电源键唤醒。但若你有一组除了xhc之外的usb控制器,那把键盘鼠标插在那两个控制器上,可以在使用强制睡眠的情况下用键盘鼠标唤醒电脑。
主板一般有5个部件是直接参与唤醒工作的,这五个部件分别是 XHC(USB控制器)、CNVW(CNVI网卡,如果你的主板自带或者预留了口的话)、GLAN(有线网卡)、XDCI(USB相关)、HDEF(音频)。旧的一些主板可能会有不同的命名,比如 XHC 有叫 EH01,HDEF 叫做 HDAS 等,这里不做讨论。而这些设备往往会直接影响睡眠,比如你输入:
log show --last 1d | grep "Wake reason"
我们会看到类似的输出结果
2020-10-31 03:35:45.196371+0800 0x74 Default 0x0 0 0 kernel: (AppleACPIPlatform) AppleACPIPlatformPower Wake reason: XDCI CNVW2020-10-31 03:35:45.196373+0800 0x74 Default 0x0 0 0 kernel: (AppleACPIPlatform) AppleACPIPlatformPower Wake reason: XDCI CNVW
那么即 是XDCI
CNVW
导致了睡眠出现了问题。于是,我们用几种方法去屏蔽或者说修改这些部件,来达到电脑正常睡眠的效果。
我们打开之前提取的SSDT,随便搜索五大部件中的一个,比如说 XDCI:
主要是看上图中 XDCI
下的 _PRW
属性值,可以直接看到 Return 的值为 GPRW (0x6D, 0x04)
。其中 6D
这个数值看主板而定,有些主板叫做 0D
,而后面 04
这个值的含义为 S4 级别的电源管理,即休眠甚至关机情况下的唤醒;有些后面的数值是 03
,代表着 S3 级电源管理。这个我打一个大家比较熟悉的例子, GLAN
这个网卡部件的 PRW
值也是 0x04
,为什么要是 04
呢?因为这样我们可以使用远程通过网络启动主机功能。
上一步中已经确认了你的主板是 0D
还是 06
,打开 OC little
的 06/0D
补丁,选择合适自己主板的补丁集,比如我的是 Name-6D更名.plist
。将补丁抄入自己的 config.plist
后重启生效。
全局重命名会导致其他系统无法通过 OC 引导开机,不建议使用。
宪武大大做的 clover 版本的 0d/6d 补丁,其实没啥必要讲,只是有留言问了 tgtbridge 在 oc 下怎么用,那我就展示一下吧。这个补丁原理是一样的,通过重命名的方式改 _prw
。
直接下载宪武大大的clover hotpatch补丁包,打开plist文件。那我们拿出一组数据来讲解怎么把它翻译成oc版本:
Comment String XHC:_PRW to XPRWDisabled Boolean True//此补丁并未生效,这里要改成false才会生效Find 5F505257 //hex转text的含义即是:_PRW Replace 58505257 //hex转text的含义即是:XPRW TgtBridge 5848435F //hex转text的含义即是:XHC_
这组改名是对 XHC
下的 PRW 改名为 xprw
,这样的话,之前 prw 下的 (0x6D, 0x04)
即不生效了。而指定 xhc 的方法即是使用了 tgtbridge,因为整张 dsdt 上有几十上百个 _PRW
,你必须通过 tgtbridge 来指定到底是哪一个部件的 _PRW
。
那么 OC 到底怎么使用 tgtbridge 来特定某一部件下的内容重命名呢?我们先把上面一段 clover 的补丁转换成 oc 的版本先吧:
Comment String XHC:_PRW to XPRWCount Number //需要重点解释Enabled Boolean True //表示应用此补丁,不应用选FalseFind Data 5F505257 //hex转text的含义即是:_PRWLimit Number 0 //这个按默认即可 不去管他Mask Data <> //这个按默认即可 不去管他OemTableId Data <> //这个按默认即可 不去管他Replace Data 58505257 //hex转text的含义即是:XPRWReplaceMask Data <> //这个按默认即可 不去管他Skip Number //需要重点解释TableLength Number 0 //这个按默认即可 不去管他TableSignature Data 44534454 //hex转text的含义即是:DSDT,这里按默认即可,代表对dsdt进行修改
这里就是一个还没全部翻译好的 oc 版改名 xhc 的 prw。那么如何定位 xhc 下的 _prw
呢,主要是填写 Count
和 Skip
。其实 oc 的 tgtbridge
是通过一个个数过去来定位具体哪一个位置的。比如xhc的prw是整张dsdt里面的第55个,那 skip
填 54
,意味着跳过前 54
个,从第 55
个开始执行。那执行多少次呢?执行一次 count
就填 1;比如你要同时改第 55
个和 56
个,那 count 就填 2
。说了这么多,我来实操一下吧:
打开dsdt,在左下角直接搜索_PRW,就能把整张表的_PRW筛选出来了:
我总共数了一下,一共有56个_PRW。我们再在主内容栏上按 ⌘ +F 搜索 xhc,直接找到 xhc 的 _PRW
,刚好我们看到我的 xhc 实在整张表的倒数第 4 个,也就是正数第 53 个:
那么我们就可以补充完整张表了:
Comment String XHC:_PRW to XPRWCount Number 1Enabled Boolean TrueFind Data 5F505257Limit Number 0Mask Data <>OemTableId Data <>Replace Data 58505257ReplaceMask Data <>Skip Number 52TableLength Number 0TableSignature Data 44534454
如果你想第 53、54、55 个都改掉,那 count 就写 3,意味着顺序执行3次。好了,就这样,有问题留言。
oc 不提倡用户直接全局重命名,如果真的要用重命名,也一定是搭配 ssdt 去做重命名,所以这个方法也是宪武大大和我最推荐的一种方法。
打开宪武大大的 OC-SSDT 包,找到 0D/6D
文件夹,打开 SSDT-GPRW.dsl
。
// In config ACPI, GPRW to XPRW// Find: 47505257 02// Replace: 58505257 02 //这里提示你要应用这个补丁,你必须在config中的ACPI-PATCH里面加入如上重命名内容//DefinitionBlock ("", "SSDT", 2, "ACDT", "GPRW", 0){ External(XPRW, MethodObj) //寻找dsdt表中叫做XPRW的内容,这是要你在config中先把gprw改名成xprw才会生效,这就是为什么这个补丁的重命名必须是这个ssdt和重命名一起用的原因,你第一个重命名不生效,这个ssdt也不会生效。 Method (GPRW, 2, NotSerialized) { If (_OSI ("Darwin")) //为了不破坏dsdt完整性,这里做了系统判断,当你运行windows的时候,此ssdt不生效 { If ((0x6D == Arg0)) //如果你的dsdt中是6D进行判断 { Return (Package () { 0x6D, Zero }) } If ((0x0D == Arg0)) //如果你的dsdt中是0D进行判断 { Return (Package () { 0x0D, Zero }) } } Return (XPRW (Arg0, Arg1)) //当运行mac系统时,如果你的dsdt中XPRW为6d,或者0d时返回为0,即屏蔽。 }}
这个 ssdt 不需要你改任何内容,打开后左上角另存为(save as), 其中文件格式(file format)必须选择 ACPI Machine Language Binary
,文件名字就叫,记住后缀为 aml。记得将 ssdt-gprw.aml
放入 EFI/OC/ACPI
目录下,并在 config.plist
中加载此 aml 文件。
同时,我们需要在 ACPI--Patch
下增加一条全局重命名来配合此 SSDT。
Comment: GPRW to XPRWCount:0Enabled:YESFind:<4750525702>Limit:0Mask:<>OemTable:<>Replace:<5850525702>ReplaceMask:<>Skip:0TableLength:0TableSignature:<>
这里会长期更新OC官方提供的Kernel Patch。
注意此kp补丁只对10.14.4以上系统版本生效,旧的我懒得提供了,需要留言。更好的解决卡f1的方案请参照rtc综述
Kernel Patch Item 0 Base String Comment String f1 patch (随便填,好记就行) Count Number 1 Enabled Boolean Yes Find Data 75330fb7 Identifier String com.apple.driver.AppleRTC Limit Number 0 Mask Data MaxKernel String MinKernel String Replace Data eb330fb7 ReplaceMask Data Skip Number 0
Kernel Patch Item 2 Base String __ZN8AppleRTC14updateChecksumEv Comment String Disable RTC ck poweroff(随便填,好记就行) Count Number 1 Enabled Boolean Yes Find Data Identifier String com.apple.driver.AppleRTC Limit Number 0 Mask Data MaxKernel String MinKernel String Replace Data c3 ReplaceMask Data Skip Number 0
打开你的DSDT,搜索 001F0000
,确定自己的
LPC0
, 别的主板可能叫做LPCB
,请根据实际情况记录_SB_.PC00.LPC0
左上角另存为(save as), 其中文件格式(file format)必须选择 ACPI Machine Language Binary
,文件名字随便写吧,我就叫 ssdt-pmc.aml
,记住后缀为aml。记得将 ssdt-pmc.aml
放入 EFI/OC/ACPI
目录下,并在 config.plist
中添加加载此aml文件。
# 删除文件 LogoutHook.commandsudo rm -rf $(sudo defaults read com.apple.loginwindow LogoutHook)# 清空 LogoutHook 的触发设置sudo defaults delete com.apple.loginwindow LogoutHook
删除EFI下的nvram.plist(若有)。
同时你需要对config.plist进行设置:
此博客有很多疏漏,你可能会碰到很多奇怪的问题而无法解决。一般来说,一张开机-v的截图只能解决一些很简单的问题,一些帮助者可能通过经验判断你的错误所在,但这样的图无法定位错误,而Acidanthera提供了非常非常强大的排错功能,我们可以利用它收集错误报告,在网络上获得帮助。
如果我们需要debug报告,我们需要将所有的Acidanthera的kext以及OC bootloader替换成debug版本,所有的debug版本都会在github中提供。我们下载debug版本的Opencore,替换:
修改config.plist中的如下内容:
YES
YES
YES
67
2147483714
*
Builtin
在Config.plist/NVRAM/7C436110-AB2A-4BBB-A880-FE41995C9F82/boot-args栏目中增加
-liludbgall liludump=30
保存后重启,你会得到:
如果无法进入系统,则只需要第一份日志。
你可以通过这两份日志快速定位错误,或者在网路上寻求帮助。
以下内容对你正常使用黑苹果已经无关了,如果你追求更好的黑果表现,可以看下去,但这部分内容也需要你自己有更好的能力与耐心。如果你不具备足够的条件,我不建议你看下去;如果你的失误导致硬件的损坏,我也不会、也没能力负责。
此章节对你的要求会相对高一点,并且请你具备如下条件:
在Intel四代之后,苹果引入了新的内核级电源管理方式:XCPM
(XNU CPU Power Management),这种新的管理方式可以高效地管理电源及变频。同时,苹果也推出了HWP
(HardWare controlled Performance states),这种技术可以快速根据特定程序的需求,作出变频转换。我们这个章节,本质上就是在加载XCPM
的情况下,调整HWP
来优化CPU的变频。
同时我要说的是,我在论坛上看到很多所谓的“变频”,有的甚至加载了50多个档位的变频,其实这种是完全没有意义的。我认为,变频是能在你需要的高频的时候快速进入高频状态,在关闭程序后又能很快回到低档位,换句话说,其实只需要三个档位就高了:睿频档,正常频率,以及低频档。
你需要搞清楚自己的cpu型号,并找到、换成与自己cpu型号最接近或者一样的白苹果型号,并且此型号必须带有HWP
。一般新的笔记本机型都具有,台式机的话推荐iMac19.1以及Macmini8.1。可查询此帖选取适合的机型。
执行如下命令:
cd ~/desktopmkdir cpucd cpugit clone https://github.com/corpnewt/CPUFriendFriend.gitgit clone https://github.com/acidanthera/CPUFriend.git cp ~/desktop/cpu/CPUFriend/tools/ResourceConverter.sh ~/desktop/cpu/CpuFriendFriend/CPUFriendFriend.command
你会看到如图的命令行,这里1 of 4代表第一段睿频的设置,以此类推,数值越大睿频越高,下面要求你填写的是最低的频率值,你想要低一点的800MHZ就填08,高一点的1300MHZ就填0D(注意大小写)
填完前两段后,它会要求你填写EPP值,EPP值越低,性能表现越强。我们是填的前两段的低频率部分,我们可以选择节能型的,比如0x80,如果你想极致性能,可以填0x00。
直至你填完所有4段变频需求后,便会生成你的变频plist。我们执行以下命令:
cp ~/Desktop/cpu/CpuFriendFriend/Results/*.* ~/Desktop/cpu
ResourceConverter.sh
以及Mac-xxxxxxx.plist两个文件CPUFriendDataProvider.kext
,注意命令行中Mac-AA95B1DDAB278B95.plist
,请替换成你自己的文件名,这样我们就可以在桌面的CPU文件夹下拿到CPUFriendDataProvider.kext
。cd ~/Desktop/cpu./ResourceConverter.sh --kext ~/Desktop/cpu/Mac-AA95B1DDAB278B95.plist
我们再到CPUFriend的release页面下,下载最新的release版本,得到里面的CPUFriend.kext
将CPUFriendDataProvider.kext
与CPUFriend.kext
一起放到oc/kexts下,并在config中加载,注意:CPUFriend.kext
应该放在CPUFriendDataProvider.kext
的前面。
感谢 @请叫我官人 的帮助。完成,自行测试。
此教程已经发于pcbeta:点此直达
此文章以不再适用Big sur及后的系统。
]]>特别提醒:在使用以下方式修复rtc的过程之前,强烈建议先短接主板CMOS以及清除NVRAM后再进行操作!NVRAM和CMOS存储位置不同,请确定两件事都做了!!
RTC为l2c下的从设备,rtc为系统提供时间,电源,硬件信息等指示数据,l2c主动联系从设备rtc获取信息,反馈给系统。这些信息都被注册在rtc的各个内存位置上,这些内存地址对应的数据可通过OC-liilte或者搜索自己主板型号的intel datasheet获得。
举例:
系统设定一个自动关机的时间,比如22:20分,此信息通过系统trigger到rtc,l2c扫到rtc信息,则自动关机。
问题主要是因为Apple。
对于《苹果系统下RTC的问题如何产生?》,第一个问题是这样的:一些具有AWAC的设备我们可能需要禁用AWAC来启用Legacy RTC。这里可通过OC-Little包中的禁用AWAC来实现。其中SSDT-AWAC.dsl更适用于主板DSDT代码特别多的主板,请根据情况二选一并转换成aml后使用。
对于第二个问题,它可能带来的影响包括时钟不走,关机不断电,开机卡f1,无法唤醒,唤醒崩溃等。这种问题就像我之前说的,是因为apple rtc写入到了普通主板rtc不支持的范围而导致的。
我之前在主文章有写到通过二进制补丁来解决开机卡F1,但这种方式其实是不妥当的,这是因为:
因此,OC团队提供了三种方式来更好的解决RTC引起的各种问题。
提供DisableRtcChecksum选项来忽略RTC地址中的0x58-0x59范围。但遗憾的是此quirk只适用于0x58-0x59地址损坏的rtc情况,并不具备通用性。
ACDT团队提供了RTCMemoryFixup.kext让用户可以自主选择所有你想屏蔽的地址。此kext需要用二分法来使用。
将此kext放入OC/Kexts下面,并在config中加载它。
一般来说,我们CMOS的总内存池是从00-FF(这个是16进制,换算成10进制就是从0-255),我们可以通过增加boot-args:rtcfx_exclude=00-FF来完全屏蔽cmos(当然这样写你完全失去了cmos记忆的功能了)
我们需要通过二分法来定位你出错的cmos位置。把00-FF分成两部分,也就是00-7F以及80-FF。我们分别填一次rtcfx_exclude=00-7F以及rtcfx_exclude=80-FF,试试看问题有没有解决。比如说我使用的rtcfx_exclude=80-FF是解决了,那我们继续对80-FF进行拆分为:0x80-0xBF 和 0xC0-0xFF。以此类推,直到你拆分到最后的那一段位置为止。
ACDT团队提供了写入NVRAM的方式来屏蔽RTC地址。如图,RTC-blacklist中 8586878889 代表屏蔽85 86 87 89的RTC地址,如果屏蔽85以及86则填写8586。注16进制。
有两个非常特殊的情况:
Device (RTC){ Name (_HID, EisaId ("PNP0B00") /* AT Real-Time Clock */) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0070, // Range Minimum 0x0070, // Range Maximum 0x01, // Alignment 0x02, // Length ) IO (Decode16, 0x0074, // Range Minimum 0x0074, // Range Maximum 0x01, // Alignment 0x04, // Length ) IRQNoFlags () {8} })
0x57
应该根据自己的主板来决定。比如我的x299主板应该搜索x299 intel datasheet
,获得自己主板的datasheet
后,得知自己Smbus串口的位置为0xc6
,替换原文中的所有0x57
为0xc6
。此SSDT应只用于debug。/* * SMBus compatibility table. */DefinitionBlock ("", "SSDT", 2, "ACDT", "MCHCSBUS", 0x00000000){ External (_SB_.PCI0, DeviceObj) External (_SB_.PCI0.SBUS, DeviceObj) Scope (_SB.PCI0) { Device (MCHC) { Name (_ADR, Zero) // _ADR: Address Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } Device (_SB.PCI0.SBUS.BUS0) { Name (_CID, "smbus") // _CID: Compatible ID Name (_ADR, Zero) // _ADR: Address Device (DVL0) { Name (_ADR, 0x57) // _ADR: Address Name (_CID, "diagsvault") // _CID: Compatible ID Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If (!Arg2) { Return (Buffer (One) { 0x57 // W }) } Return (Package (0x02) { "address", 0x57 }) } } Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } Method (DTGP, 5, NotSerialized) { If ((Arg0 == ToUUID ("a0b5b7c6-1318-441c-b0c9-fe695eaf949b"))) { If ((Arg1 == One)) { If ((Arg2 == Zero)) { Arg4 = Buffer (One) { 0x03 // . } Return (One) } If ((Arg2 == One)) { Return (One) } } } Arg4 = Buffer (One) { 0x00 // . } Return (Zero) }}
Dortania Team, (2020).,
OC little, (2020)., <03-二进制更名与预置变量>. available at:https://github.com/daliansky/OC-little/tree/master/03-二进制更名与预置变量, last accessed at 10 Oct 2020.
Acidanthera (2020).,