Urvanov Syntax Highlighter: WordPress 5.6+/PHP 74+/PHP 8+ 完全兼容

一直使用  Crayon Syntax Highlighter (Crayon语法显示) 作为 WordPress 的代码高亮插件。但是这个插件已经长时间没有更新维护了,导致无法兼容最新版本的 Wordoress

开发者 urvanov 进行维护的二次开发项目,在 Crayon Syntax highlighter 的基础上进行更新二次开发,解决了CrayonWordPress 5.x 上出现的各种问题。

插件主页: Urvanov Syntax Highlighter,作者在原基础功能上进行了语法语言支持完善等优化,使用下来很赞。

Urvanov Syntax Highlighter
Urvanov Syntax Highlighter

支持经典编辑器和新编辑器,代码块直接插入。使用体验与 Crayon Syntax highlighter相同。

参考链接


Flutter调试 iOS 真机卡在启动页面,报错 “try setting IDEPreferLogStreaming=YES”

最近,Flutter 升级到最新版本之后,iOS 真机调试的时候持续卡住在启动页面。

Xcode 输出如下报错信息:

Flutter 系统信息如下:

继续阅读Flutter调试 iOS 真机卡在启动页面,报错 “try setting IDEPreferLogStreaming=YES”

Compare Arrays in JavaScript

HarmonyOS NEXT 系统推荐使用 Lodash 库,更符合开发直觉。

Arrays are objects in JavaScript, so the triple equals operator === only returns true if the arrays are the same reference.

How do you compare whether two arrays are equal? Equality is a tricky subject: the JavaScript spec defines 4 different ways of checking if two values are "equal", and that doesn't take into account deep equality between objects.

In cases like this, it helps to be as explicit as possible about what you mean by "equal." In software engineering, asking a question in the right way often makes the answer obvious.

With that in mind, here's 3 definitions of equality for arrays and how to check them.

Same Length, Each Value Equal

One approach for comparing a and b is checking if each value of a is strictly equal to the corresponding value of b. This works well if all the elements of the arrays are primitives as opposed to objects.

Deep Equality With POJOs

The previous arrayEquals() function works great for primitive values, but falls short if you want to compare objects by value.

One neat way to take into account object values is comparing arrays by their JSON.stringify() output.

This approach is handy because it requires minimal code and no outside libraries. However, comparing JSON.stringify() output has an unfortunate edge case that may be a problem depending on your use case. Since undefined isn't a valid JSON value, the below arrays have the same JSON.stringify() output, because JSON.stringify() converts undefined to null.

Using Lodash's isEqual()

In addition to the null vs undefined quirk, comparing JSON.stringify() output also doesn't take into account object types. As far as JSON.stringify() is concerned, an object with a toJSON() function that returns 42 is the same as the number 42.

Similarly, a custom object is the same as a POJO:

Lodash's isEqual() function, on the other hand, takes all this into account.

Lodash's isEqual() function is the way to go if you need all the bells and whistles of checking that objects have the same class. The JSON.stringify() approach works well for POJOs, just make sure you take into account null and only use it with trusted data - toJSON() can be a security vulnerability.

参考链接


Compare Arrays in JavaScript

make: *** No rule to make target `sqlite_cfg.h', needed by `.target_source'

前置条件

  • macOS Sonoma (14.4.1) 
  • MacBook Pro 2023-Apple M2 Pro (4能效核、8性能核、32GB内存、2TB磁盘)
  • Homebrew (4.2.18 或更高版本)
  • Xcode Version 15.3 (15E204a)
  • DevEco Studio NEXT Developer Preview2 4.1.3.700

错误信息

源代码编译 sqlite3

或者使用 pod 安装 sqlite3 报错:

观察源代码目录,也确实没有 sqlite_cfg.h 文件生成。

继续阅读make: *** No rule to make target `sqlite_cfg.h', needed by `.target_source'

Flutter: Xcode error “Unable to boot the Simulator“

使用 flutter、Android Studio ,通过iOS模拟器运行项目,一直一切正常。

某次重启后无法启动模拟器,报错信息如下:

解决方法,亲测有效:

macOS 14.4.1以及更高版本:
进入 “系统设置”→“通用”→“存储空间”→“开发者” 删除 “XCode 缓存” 。

在 macOS 13 及更高版本上:
进入 “系统设置”→“常规”→“存储”→“开发人员”
删除“开发者缓存”

在 macOS 12 及更低版本上:
转到“关于本机”→“存储”→“管理”→“开发人员”

参考链接


Flutter: XCode error “Unable to boot the Simulator

Podman安装最新的JFrog Artifactory制品仓库

1、前言

JFrog Artifactory 是目前全球唯一的支持所有开发语言,功能最强大的二进制制品仓库。

2、系统平台

操作系统 : Ubuntu 22.04.4 LTS (GNU/Linux 6.5.0-26-generic x86_64)

3、安装依赖&拉取镜像

使用 OSS 社区版本,官方有详细说明介绍

完整的执行指令如下图所示

执行上面的指令后,可以看到 releases-docker.jfrog.io/jfrog/artifactory-oss 镜像下载进度。

4、启动配置

首先要创建本地的数据目录,然后启动的时候挂载本地创建的数据目录即可。

执行启动指令: 

查看进程及IP跟端口号,启动一切正常了,打开浏览器访问系统看看效果。

查看启动文件:

内容如下:

需要额外注意的一个地方是,给出的路径必须是完整路径 “/home/podman/.dockers/jfrogoss”,不能是 “~/.dockers/jfrogoss”,Systemd不能正确展开 “~” ,导致路径找不到,从而在启动的时候失败,报告错误代码 125 。

Systemd 配置,开机/重启自动启动服务:

5、访问、登录

Access Artifactory from your browser at: 

http://SERVER_HOSTNAME:18082/ui/

For example, on your local machine: 

http://localhost:18082/ui/

默认用户名:admin, 密码: password

启用匿名访问:

Maven服务器配置

客户端配置 http://SERVER_HOSTNAME:18082/artifactory/maven-remote/

参考链接


修复 SecurityException: getDataNetworkTypeForSubscriber 问题

1.问题

  • 用户切换改变网络的过程中,应用概率会出现崩溃,日志如下

另外一种场景就是集成了华为的二维码扫描  com.huawei.hms:scanplus:1.3.2.300 ,这个  SDK 比较奇葩的地方在于,使用 Wi-Fi 网络情况下是不会出现问题的。

但是在手机流量的情况下,不申请 android.permission.READ_PHONE_STATE 权限,初始化这个 SDK 会导致应用闪退。

崩溃堆栈如下:

2.分析

  • 根据 SecurityException: getDataNetworkTypeForSubscriber 可以看到,这是一个安全性异常,所以猜测应用在 Android11 的权限有关,由于缺少该权限导致无法访问接口而异常。

  • 找到网络状态检测方法,可以看到调用了 TelephonyManager.getNetworkType()接口获取网络类型,该方法是需要 READ_PHONE_STATE 权限的,该方法上面也有 RequiresPermission 注解声明

  • 进一步分析,接口的调用堆栈为:

3.解决方法

参考链接


修复 SecurityException: getDataNetworkTypeForSubscriber 问题

Linux使用SMB给macOS做无线Time Machine备份

前要:

一直以来使用外置硬盘给Mac做Time Machine备份盘,但是存在若干不够方便的地方,如:

  • 磁盘需要格式化为APFS格式,虽然APFS的“卷共享容器空间”的机制可以很方便的让Time Machine卷和其他资料卷共用空间,而不是像传统的分盘让空间分隔,还要考虑空间分配问题。但APFS格式只在macOS设备间方便使用
  • 虽然在不连接磁盘的情况下,内置存储也会保留24小时内到每小时快照,但总是需要刻意记起找出插入硬盘进行备份的操作

正好最近使用旧电脑刷了Ubuntu用作NAS使用,于是想了解关于如何配置无线Time Machine

无线Time Machine的共享协议选择:

最开始找到的教程是使用AFP的开源实现netatalk让Linux支持AFP共享协议,然后作为Time Machine盘。但发现netatalk最近曝出过严重漏洞项目本身在GitHub也只有0.2K Star的关注。主观感觉其稳定性是存在疑问的。

后来发现,并不一定是AFP协议的共享才能做Time Machine备份盘;Samba只要进行一些配置就能做Time Machine备份用了

在 Mac 上可以与时间机器配合使用的磁盘类型

在Apple官方文档中说明有写到

【提示】如果可以选择 SMB 或 AFP,请使用 SMB 来备份到外置备份磁盘。

目前Apple官方也是更推荐使用SMB协议来作为无线Time Machine备份的

Linux上Samba的配置

首先需要安装avahi和samba,关于其安装和配置和samba用户的设置管理这里不再赘述,不同的Linux发行版安装方式会有一些差异,可以自行搜索。

个人的 /etc/samba/smb.conf 内容如下

其中,与 Time Machine 的启用相关的关键部分为:

因为个人这里是只配合自己的Mac使用的,所以配置文件尽量确保了安全性和与macOS设备的兼容性,比如在global中把最小连接协议设置为SMB3_11,强制启用了传输加密,和很多更加兼容macOS的fruit选项。请根据个人需求修改。

配置可参考

Configure Samba to Work Better with Mac OS X

最后使用systemctl restart smbd另配置生效即可。

备份盘的选择:

上述配置文件中,/media/xiaoming/WD是外置硬盘在fstab中设置挂载的路径,而Time Machine备份的目录则放在了该目录的./tmbackup下。登陆用户我设置为“xiaoming”,并确保这两个目录都具有用户“xiaoming”的权限读写。后续在使用SMB访问时,才能正常读写文件

特别提示:如果你的外置磁盘并非ext4这样的适合Linux使用的格式,而是如exFAT这种,会出现问题。像exFAT是不兼容在Linux上的文件用户分组的。个人测试,exFAT磁盘中所有文件所属只能为root:root。且使用sudo chown -R xiaoming:xiaoming也无法更改权限。建议备份磁盘文件后,格式化为ext4这种适合Linux使用的格式

macOS上的配置:

打开访达,边栏上应该能直接看到服务器,点击后点上面的“连接身份...”,再点“注册用户”,输入用户和密码,勾选“在我的钥匙串中记住此密码”,点连接,随后会显示出多个共享,多选连接即可。

如果边栏没有,则需按command+K来手动连接,输入smb://xiaomingnas.local。这里xiaomingnas.local中的xiaomingnas是主机的hostname,通常avahi的mDNS服务会使用<hostname>.local作为域名。点连接,后续步骤与上述相同。

随后在Mac的系统设置-通用-时间机器,点+号,就能选择设置的Time Machine进行备份了。然后根据提示设置登录用户名,时间机器的加密密码即可。

无线备份受限于网络传输速度,首次备份可能需要数小时才能完成,可耐心等待。

Time Machine默认设置了速度限制,以保障网络和磁盘可正常使用,首次备份可以在Mac上暂时解除该限制

用外置磁盘和无线共享设置Time Machine的区别:

使用外置磁盘备份,点击边栏上的卷后,可以看到目录形式展示的每次的备份,并直接查看其中的目录文件。还可以方便地多选备份进行删除,来腾出空间占用

而使用SMB共享作为备份盘。这会在共享路径下生成一个类似“小明的MacBook Air.sparsebundle”目录。大致结构如下

其中bands目录中会有大量固定大小为67MB的二进制文件,这些便是备份的数据。

Apple官方对sparsebundle的解释的大致意思是,它是一个以二进制形式存储的,可按需收缩和扩大的可扩展文件。

在 Mac 上使用“磁盘工具”创建磁盘映像

这样看用SMB共享设置Time Machine实际上更加灵活,相比之下不挑磁盘格式,且备份在Linux上的磁盘中仅作为某个路径下的某个目录存在,而不像使用外置磁盘备份一样单独占据一个APFS卷。

但其不能像外置磁盘一样,可以任意多选删除备份来腾出空间。但可以设置fruit:time machine max size来限制备份大小,这样达到空间临界点时macOS就会自动删除最旧的备份来腾出空间。当然,磁盘备份同样也可以在添加APFS卷时,通过设置“配额大小”来限制Time Machine过度膨胀(但设置完毕后不易变更)

实际使用的体验:

无线备份的速度慢于磁盘备份,这取决于你的局域网环境。不过备份过程同样是完全无感的,和有线备份一样,即使是合盖休眠情况下也会自动创建备份。

Time Machine是可以同时备份到多个位置的,因此可以同时使用磁盘备份和无线备份,这样数据安全性也更好。如果再配合zerotier异地组网,设置一个异地的NAS作为无线Time Machine,那么你就得到了一个符合“3-2-1原则”的,完全无感、自动化、无需干涉的备份系统。且Time Machine本身也有加密,无惧因备份介质遗失导致数据泄漏的风险。

考虑到传输速度,无线的Time Machine用于找回过去的文件没有问题,但不太适合做整机恢复,如果是换机这种操作,还是建议使用磁盘备份节省时间。

更建议使用 docker-timemachine 镜像进行配置,可以在搜索 docker-timemachine 然后进行配置。

参考链接


Linux使用SMB给macOS做无线Time Machine备份

商用密码技术最佳实践白皮书

密码算法库是操作系统的基础组件,在系统安全领域的作用不言而喻。操作系统默认已经内置了大量的密码学库,比如OpenSSL,libgcrypt,gnulib,nettle 是被默认集成到基础操作系统的,它们有一些重复的功能,但也各有侧重的领域,是操作系统不可或缺的安全基石。

本小节会介绍一些支持国密算法的、非常主流的密码算法库,提供给开发者和用户更多的选择。

OpenSSL

官网:https://www.openssl.org

OpenSSL 是一个通用的、强大的、商业级的、功能齐全的工具包,用于通用加密和安全通信。

OpenSSL 的重要性众所周知,这里重点强调一下版本问题。

🟢 1.1.1 稳定版

目前主流发行版使用的仍然是 1.1.1 版本,这个版本在国密的支持上有一些固有的缺陷:

  • 不支持 SM2 的签名验签,因为基于可辨别用户ID的Za值计算在这个版本中未实现
  • 国外主流的发行版的包默认没有编译国密 SM2、SM4 模块,CentOS上就是如此

由于技术上和兼容性的原因,这个版本目前很难升级到最新的社区版本,因此在主流的发行版本中基本是无缘使用国密的。

🟢 3.0.x 稳定版

社区最新的稳定版本是 3.0,这个版本对国密的支持已经比较完善,并且支持了国密的指令集优化。用户如果自行编译可以完整使能国密的能力。

🟢 龙蜥社区 1.1.1 版本

从目前情况来看,对于一个操作系统发行版,要完全从 1.1.1 切换到 3.0 还需要较长的时间,因此龙蜥社区在 1.1.1 版本的基础上,在保证兼容性和稳定性的前提下,补全了国密能力上的缺陷,并且做为操作系统默认库在 Anolis OS 8.8 中集成发布,详细信息可参考Anolis OS 国密开发指南

libgcrypt

官网:https://www.gnupg.org/software/libgcrypt/index.html

不像 OpenSSL 还包括了安全协议,libgcrypt 是一个纯粹的密码算法库,就国密算法的性能来说,libgcrypt 的国密算法优化是做的比较充分的,Linux 内核国密算法的部分优化也是先在 libgcrypt 实现后才移植到内核的。

Libgcrypt 是一个通用密码库,最初基于 GnuPG 的代码。 它为几乎所有的密码提供支持:

  • 对称密码算法 (AES、Arcfour、Blowfish、Camellia、CAST5、ChaCha20 DES、GOST28147、Salsa20、SEED、Serpent、Twofish、SM4)
  • 模式 (ECB、CFB、CBC、OFB、CTR、CCM) ,GCM,OCB,POLY1305,AESWRAP)
  • 哈希算法 (MD2, MD4, MD5, GOST R 34.11, RIPE-MD160, SHA-1, SHA2-224, SHA2-256, SHA2-384, SHA2-512, SHA3-224 , SHA3-256, SHA3-384, SHA3-512, SHAKE-128, SHAKE-256, TIGER-192, Whirlpool, SM3)
  • MAC (HMAC 用于所有哈希算法, CMAC 用于所有密码算法, GMAC-AES, GMAC-CAMELLIA, GMAC-TWOFISH、GMAC-SERPENT、GMAC-SEED、Poly1305、Poly1305-AES、Poly1305-CAMELLIA、Poly1305-TWOFISH、Poly1305-SERPENT、Poly1305-SEED)
  • 公钥算法 (RSA、Elgamal、DSA、ECDSA、EdDSA、ECDH、SM2)
  • 大整数函数、随机数和大量的支持函数

libgcrypt 是很多基础组件依赖的密码库,比如 gpg,systemd,qemu,postgresql,还有许多桌面环境的库,音视频组件,蓝牙都依赖于 libgcrypt 提供的密码安全机制,还有部分会选择依赖libgcrypt,比如 curl,cryptsetup 等会选择依赖 OpenSSL,libgcrypt 算法库,用户需要自行构建来选择不同的密码库。

libgcrypt 从 1.9.0 版本开始陆续支持了国密算法和国密的指令集优化。

GmSSL

项目地址:https://github.com/guanzhi/GmSSL

GmSSL 是一个开源密码工具包,为 GM/T 系列标准中规定的中国国家密码算法和协议提供一级支持。 作为 OpenSSL 项目的一个分支,GmSSL 提供了与 OpenSSL 的 API 级兼容性并保持了所有的功能。 现有项目(例如 Apache Web 服务器)可以轻松地移植到 GmSSL,只需进行少量修改和简单的重建。

自2014年底首次发布以来,GmSSL已入选开源中国六大推荐密码项目之一,并获得2015年中国Linux软件大奖。

该密码库的特点:

  • 支持中国GM/T密码标准。
  • 支持中国厂商的硬件密码模块。
  • 具有商业友好的开源许可证。
  • 由北京大学密码学研究组维护。

GmSSL 将支持以下所有 GM/T 加密算法:

  • SM3 (GM/T 0004-2012):具有 256 位摘要长度的密码哈希函数。
  • SM4(GM/T 0002-2012):密钥长度为128位,块大小为128位的块密码,也称为SMS4。
  • SM2(GM/T 0003-2012):椭圆曲线密码方案,包括数字签名方案、公钥加密、(认证)密钥交换协议和一种推荐的256位素数域曲线sm2p256v1。
  • SM9(GM/T 0044-2016):基于配对的密码方案,包括基于身份的数字签名、加密、(认证)密钥交换协议和一条256位推荐BN曲线。
  • ZUC(GM/T 0001-2012):流密码,采用128-EEA3加密算法和128-EIA3完整性算法。
  • SM1和SSF33:密钥长度为128位,块大小为128位的块密码,没有公开说明,只随芯片提供。

GmSSL 支持许多有用的加密算法和方案:

  • 公钥方案:Paillier、ECIES(椭圆曲线集成加密方案)
  • 基于配对的密码学:BF-IBE、BB1-IBE
  • 块密码和模式:Serpent、Speck
  • 块密码模式:FPE(格式保护加密)
  • 基于SM3/SM4的OTP(一次性密码)(GM/T 0021-2012)
  • 编码:Base58

ECDSA、RSA、AES、SHA-1 等 OpenSSL 算法在 GmSSL 中仍然可用。

nettle

官网:http://www.lysator.liu.se/~nisse/nettle

Nettle 是一个相对低层的加密库,旨在轻松适应各种工具包和应用程序。它开始于2001年的lsh的低级加密函数的集合。自2009年6月以来,Nettle 成为 GNU 软件包。

Nettle 的定位跟 libgcrypt 有点类似,是很多基础组件选择依赖的一个密码学库。

从提供的 API 上来看,Nettle 没有对算法做更高层次的抽象,每个不同的算法都有一套更易理解的接口,开发者也会更容易上手。

Nettle 从 3.8 版本开始支持了 SM3 算法,最新的开发分支已经合入了 SM4 算法,会在下一个 release 版本发布。

gnulib

官网:https://www.gnu.org/software/gnulib

从名字可以看出,gnulib 并不是一个纯密码算法的库,它的定位是 GNU 的公共代码库,旨在 GNU 包的源代码级别之间共享。

之所以在这里提 gnulib,是因为这个库里面实现了常用的哈希算法,也包括SM3算法,gnulib 里的 哈希算法主要是为 coreutils 包里的 sha*sum, md5sum 系列工具提供支持的,当然开发者也可以基于 gnulib 构建自己的程序。

gnulib 是在 2017 年 10 月支持了 SM3 算法,由阿里巴巴张佳贡献。

coreutils

coreutils 支持了大量的计算哈希的工具,比如 cksum,md5sum,b2sum,sha*sum 等,这些工具是紧密依赖于 gnulib 库的。

2017 年 10 月,在 gnulib 库支持了 SM3 之后,我们便向 coreutils 社区提交了 sm3sum 工具的支持,coreutils 社区却迟迟不愿接收,因为 SM3 算法的IV向量没有明确的来历说明,社区对算法的安全性有质疑,虽然彼时 SM3 已经是 ISO 的国际标准算法。社区人员认为 SM3 在 gnulib 中作为库提供给开发者是没有问题的,因为开发者具备也应该具备判断一个算法是否安全的能力,但是在 coreutils 中提供一个 sm3sum 的工具提供给终端用户会引起用户的误导,用户可能误认为算法安全性是得到保证的,尤其是在 SM3 算法安全性被质疑的前提下。

直到四年后的 2021 年 9 月,在包括Linux 内核,libgcrypt,OpenSSL 等主流的密码算法社区都支持了SM3算法后,在龙蜥的几次推动下,coreutils 社区终于不再质疑 SM3 的安全性问题,但是社区也不愿意再多引入一个工具,应该把这个哈希算法整合为一个工具,因为类似 *sum 的工具太多了。

因此,社区提出一个 cksum -a [algo] 的方案,通过给 cksum 工具添加一个算法参数,整合了目前 coreutils 中支持的所有哈希算法,为了兼容考虑,之前的 *sum 工具也继续保留了,SM3 是唯一仅在 cksum 工具中支持的算法,当然这并不是优点,使用习惯上也会有一些差异,用户需要通过 cksum -a sm3 来计算 SM3 哈希,除这个区别外,其它用法跟 md5sum 类似。

coreutils 从 9.0 版本开始支持 SM3 的哈希计算。

RustCrypto

这是一个纯 Rust 编写的密码算法库,供 Rust 开发者使用。

该项目维护着数十个流行的 crate,都提供密码算法的纯 Rust 实现,主要包括以下算法:

  • 非对称加密:椭圆曲线、rsa
  • 加密编码格式:const-oid、der、pem-rfc7468、pkcs8
  • 数字签名:dsa、ecdsa、ed25519、rsa
  • 椭圆曲线:k256、p256、p384
  • 哈希函数:blake2、sha2、sha3、sm3
  • 密钥派生函数:hkdf、pbkdf2
  • 消息认证码:hmac
  • 密码哈希:argon2、pbkdf2、scrypt
  • Sponge 函数:ascon、keccak
  • 对称加密:aes-gcm、aes-gcm-siv、chacha20poly1305、sm4
  • Traits:aead、密码、摘要、密码哈希、签名

该算法库目前支持 SM3 和 SM4 算法。

Intel IPP

项目地址:https://github.com/intel/ipp-crypto

Intel Integrated Performance Primitives (Intel IPP) Cryptography 是一个安全、快速且轻量级的密码学库,针对各种 Intel CPU 进行了高度优化。

该库提供了一套全面的常用于加密操作的函数,包括:

  • 对称密码学原语函数:

    • AES(ECB、CBC、CTR、OFB、CFB、XTS、GCM、CCM、SIV)
    • SM4(ECB、CBC、CTR、OFB、CFB、CCM)
    • TDES(ECB、CBC、CTR、OFB、CFB)
    • RC4
  • 单向哈希原语:

    • SHA-1、SHA-224、SHA-256、SHA-384、SHA-512
    • MD5
    • SM3
  • 数据认证原语函数:

    • HMAC
    • AES-CMAC
  • 公钥加密函数:

    • RSA、RSA-OAEP、RSA-PKCS_v15、RSA-PSS
    • DLP、DLP-DSA、DLP-DH
    • ECC(NIST 曲线)、ECDSA、ECDH、EC-SM2
  • 多缓冲区 RSA、ECDSA、SM3、x25519

  • 有限域算术函数

  • 大整数算术函数

  • PRNG/TRNG 和质数生成

使用英特尔 IPP 密码库的原因:

  • 安全性(秘密处理功能的恒定时间执行)
  • 专为小尺寸设计
  • 针对不同的 Intel CPU 和指令集架构进行了优化(包括硬件加密指令 SSE 和 AVX 的支持)
  • 可配置的 CPU 分配以获得最佳性能
  • 内核模式兼容性
  • 线程安全设计

参考链接


关于Ubuntu下ZRAM的配置和使用

当前自己使用的服务器内存比较小,需要扩容,只是服务器比较老了,内存扩展困难,但是服务器的负载并不高。因此可以尝试使用内存压缩的方式来适当扩展内存。

安装

软件包封装好了对应的东西(甚至是systemctl服务),并不需要自己写脚本

重启之后,一个内存大小一半的 ZRAM 就出现了。

参考链接