Musl libc:为什么我们会需要另一个 libc?

如果你是一个 Linux 用户,那你一定至少听说过 Glibc 的鼎鼎大名,或者甚至在日常使用中碰到不少关于它的问题,例如 Glibc 版本不匹配等问题。而本文的主角—— Musl libc 与之相比就要默默无闻的多,毕竟绝大多数的 Linux 发行版使用的 libc 库都是 Glibc,只有 Alpine Linux 等极少数 Linux 发行版才会使用 Musl libc,并且还会遇到诸如闭源 JDK 无法正常使用等问题的困扰,那么,为什么我们会需要另一个 libc 函数库呢?

Glibc 并不完美

Glibc 作为目前使用最广泛的 libc 函数库,虽然拥有最广泛的发行版支持和用户群体,并且在兼容性和性能方面也存在一些优势,但它并不完美,有三个问题严重困扰着它。

代码库陈旧

Glibc 拥有悠久的历史——对于软件而言这可能并不一定是一句赞誉,尤其是当你需要处理上世纪九十年代就存在的代码库时。三十多年来,程序员们编写 C 程序的方式并不是一成不变的,某些在那个年代被认为是好习惯或者是必须的编程方式在今天看来可能完全不合时宜,诸如叠床架屋的宏等等。这些问题严重拖累了 Glibc 的源码可读性,例如下面这一段源代码,摘自 Glibc 的 fopen 函数。

作为对比,下面是 Musl 库中同样对 fopen 函数的实现

两者的可读性差距不言自明,笔者在这里并非是要批判 Glibc 的代码风格,但对于初次上手阅读源码的人来说,显然是 Musl 的风格更加友好,更便于理解。

体积过大

由于 Glibc 相较体积更加关注性能,因此其链接生成的二进制文件相较于 Musl uClibc 等专注于嵌入式等场合的库来说要大很多,而这些场合往往非常关注几百 K 大小的区别,因为 SRAM 的大小往往关乎整体开发板的成本。

下面这张表展示了不同 libc 库编译的文件大小。

尺寸对比 musl uClibc dietlibc glibc
.a 426k 500k 120k 2.0M
.so 527k 560k 185k 7.9M
静态最小 1.8k 5k 0.2k 662k
静态输出 13k 70k 6k 662k

可以看出 Glibc 在程序大小上明显大于其他 libc 库,此外,这里的静态也是有水分的,这就引出了 Glibc 的下一个问题,也是最重要的问题之一:静态链接。

对静态链接支持不佳

理论上来说,Glibc 是支持静态链接的。但,这也仅仅是从理论上来说,由于一些历史遗留问题(当然,也包括对功能实现的考虑)Glibc 的静态链接并不是真正的静态链接:如果你的程序中使用了某些不支持静态链接的特性(这一点在大型软件中非常常见),那么即便你在链接时选择静态链接,生成出来的程序实际上仍然是依赖于 Glibc 动态库的,一旦你尝试删除掉它,你立马就会发现这些“静态”链接的程序统统罢工不干了。

谁在用 Musl?

在发行版中,主要是 Alpine Linux,作为最特立独行的 Linux 发行版之一,它选用了 Musl + Busybox 的组合,而非通常的 Glibc + Coreutils,这使得它的最小安装可以控制在惊人的 5 MB 之内!相比之下,普通的 CentOS 最小安装则需要 200 MB 左右,这一点使得它在嵌入式等对内存占用极为敏感的场合占据了相当的优势。

此外,Musl 从设计之初就很关注静态链接的可用性,因此它完全可以被静态链接进其他程序中,不存在 Glibc 对动态库的依赖问题,这一点也有助于缓解不同版本 libc 之间的兼容性问题——只要我把万物都静态链接进去,就不存在版本问题了。当然,这种做法也会带来体积膨胀等问题,所以并不是一个太好的解决方案。

Musl 的问题与未来

虽然拥有诸多优点,但 Musl 在性能方面逊于 Glibc 也是不争的事实,毕竟简化实现的代价就包括牺牲性能,不过这一点并非不可拯救,通过使用开源的 malloc 实现(诸如微软的实现)替换这些对性能影响较大的热点函数,就可以在很大程度上解决性能方面的问题。

同时,试图取代或至少部分取代 Glibc 的库也并不止 Musl 一个, 例如用于 AOSP 项目上的 bionic,以及广泛应用于各种嵌入式开发中的 uClibc 等。与它们相比,Musl 背后既没有 Google 这样的大公司撑腰,在压缩体积方面做的也不够极致,相较之下就没有那么受到开发者们的青睐,在新功能和新特性跟进上也不是非常积极。

出于这些原因,也许 Musl 在今后的很长一段时间内会继续保持这种“小而美”的特点,但这对于我们来说并非就是一件坏事,能够看到与 GNU Glibc 风格截然不同的另一种实现,对于 Linux 社区的多样性,以及对于我们这些学习者来说,何尝不是一件美事?

参考链接


Box86/Box64 vs QEMU vs FEX (vs Rosetta2)

Comparing performances

I decided to compare the performances of the OpenSource Linux Userspace Emulator that allows you to run x86/x86_64 apps on ARM linux. There are QEMU-userFEX-emu and Box86/Box64.

How to bench Linux userspace emulator

Test will consist of the bench I already used a couple of time, and that can be run as native or emulated:

  • 7-zip integrated benchmark, that contains mostly integer code (no x87 or SSE), and can be used as a baseline to see the pure x86 code translation efficiency. The version 16 present in Ubuntu was used for those tests.
  • dav1d, an opensource video transcoding tool, that includes hand-optimized SSE assembly code (SSE3 or more).
  • glmark2 that is GL limited and should run at mostly native speed (as long as GL is hardware accelerated). I couldn’t install the armhf version of glmark2 on Ubuntu, so only the native 64bits version was benchmarked.
  • openarena, that contains x87 code, and a JIT, and, in that config, is very much GPU limited, and so should be running very close to the native speed (again, as long as GL is still hardware accelerated).

The 7z, dav1d and glmark2 bench are described here, and the openarena one there.

I’ll also do some quick bench not available natively. The fps will simply be measured with HUD_GALLIUM=fps on a stable and reproducible moment in the game:

  • WorldOfGoo: The game has simple graphics, it should run fine. Measures will be done on the Title screen.
  • FTL: that I added to the bench after doing the QEMU measures. Measures will be done on the 1st Tutorial screen, while the game is paused.
  • CINEBENCH r15: A benchmark based on a raytracing engine. Lots of SSE (SSE2 and more) code here. Use multi-core also. Does include a CPU bench and OpenGL bench, but only the CPU one is used here. It provides a simple number indicating the performance (the higher, the better).

To install WorldOfGoo, the “uname” trick will be used, as this allows to choose x86 or x86_64 installation (without the trick, the installer doesn’t recognise “aarch64” platform and fallback to x86). WorldOfGoo will run at 1920×1080 fullscreen.

CINEBENCH r15. This one needs Wine, and a 64bits version of it. It’s a benchmark with the CINEMA 4D Engine.

After some some testing, I realized that both openarena and WorldOfGoo mainly use x87 code, at least for the 32bits version of WorldOfGoo. Both QEMU and FEX seem to use use Softfloat for it, to keep the 80bits precision, while box uses hardware float (with some tricks to keep 80bits when needed, like on some data copy used by old games), so I decided to also check the menu page of FTL, that I know use SSE code. But I didn’t test on QEMU (it’s not hardware accelerated anyway, so it would be too slow). FTL will run at default resolution of 1280×720 windowed. I’ll launch the tutorial, answer to the 1st dialog box and mesure the fps at this point.

继续阅读Box86/Box64 vs QEMU vs FEX (vs Rosetta2)

iPhone/iPad设备型号对应常用名称列表(2023更新至iPhone 15 Pro Max | iPad Air 5 | iPad10 | iPad Pro 12.9-inch 6)

设备型号 名称
iPhone3,1 iPhone 4
iPhone3,2 iPhone 4
iPhone3,3 iPhone 4
iPhone4,1 iPhone 4S
iPhone5,1 iPhone 5
iPhone5,2 iPhone 5
iPhone5,3 iPhone 5c
iPhone5,4 iPhone 5c
iPhone6,1 iPhone 5s
iPhone6,2 iPhone 5s
iPhone7,1 iPhone 6 Plus
iPhone7,2 iPhone 6
iPhone8,1 iPhone 6s
iPhone8,2 iPhone 6s Plus
iPhone8,4 iPhone SE
iPhone9,1 iPhone 7
iPhone9,2 iPhone 7 Plus
iPhone9,3 iPhone 7
iPhone9,4 iPhone 7 Plus
iPhone10,1 iPhone 8
iPhone10,2 iPhone 8 Plus
iPhone10,4 iPhone 8
iPhone10,5 iPhone 8 Plus
iPhone10,3 iPhone X
iPhone10,6 iPhone X
iPhone11,2 iPhone XS
iPhone11,4 iPhone XS Max
iPhone11,6 iPhone XS Max
iPhone11,8 iPhone XR
iPhone12,1 iPhone 11
iPhone12,3 iPhone 11 Pro
iPhone12,5 iPhone 11 Pro Max
iPhone12,8 iPhone SE 2
iPhone13,1 iPhone 12 mini
iPhone13,2 iPhone 12
iPhone13,3 iPhone 12 Pro
iPhone13,4 iPhone 12 Pro Max
iPhone14,4 iPhone 13 mini
iPhone14,5 iPhone 13
iPhone14,2 iPhone 13 Pro
iPhone14,3 iPhone 13 Pro Max
iPhone14,6 iPhone SE 3
iPhone14,7 iPhone 14
iPhone14,8 iPhone 14 Plus
iPhone15,2 iPhone 14 Pro
iPhone15,3 iPhone 14 Pro Max
iPhone15,4 iPhone 15
iPhone15,5 iPhone 15 Plus
iPhone16,1 iPhone 15 Pro
iPhone16,2 iPhone 15 Pro Max
i386 Simulator
x86_64 Simulator
iPod1,1 iPod Touch 1G
iPod2,1 iPod Touch 2G
iPod3,1 iPod Touch 3G
iPod4,1 iPod Touch 4G
iPod5,1 iPod Touch 5G
iPod7,1 iPod Touch 6G
iPod9,1 iPod Touch 7G
iPad1,1 iPad
iPad1,2 iPad 3G
iPad2,1 iPad 2
iPad2,2 iPad 2
iPad2,3 iPad 2
iPad2,4 iPad 2
iPad2,5 iPad Mini
iPad2,6 iPad Mini
iPad2,7 iPad Mini
iPad3,1 iPad 3
iPad3,2 iPad 3
iPad3,3 iPad 3
iPad3,4 iPad 4
iPad3,5 iPad 4
iPad3,6 iPad 4
iPad4,1 iPad Air
iPad4,2 iPad Air
iPad4,3 iPad Air
iPad4,4 iPad Mini 2
iPad4,5 iPad Mini 2
iPad4,6 iPad Mini 2
iPad4,7 iPad Mini 3
iPad4,8 iPad Mini 3
iPad4,9 iPad Mini 3
iPad5,1 iPad Mini 4
iPad5,2 iPad Mini 4
iPad5,3 iPad Air 2
iPad5,4 iPad Air 2
iPad6,3 iPad Pro 9.7
iPad6,4 iPad Pro 9.7
iPad6,7 iPad Pro 12.9
iPad6,8 iPad Pro 12.9
iPad6,11 iPad 5
iPad6,12 iPad 5
iPad7,1 iPad Pro 12.9 inch 2nd gen
iPad7,2 iPad Pro 12.9 inch 2nd gen
iPad7,3 iPad Pro 10.5 inch
iPad7,4 iPad Pro 10.5 inch
iPad7,5 iPad 6
iPad7,6 iPad 6
iPad7,11 iPad 7
iPad7,12 iPad 7
iPad8,1 ~ 8,4 iPad Pro 11-inch
iPad8,5 ~ 8,8 iPad Pro 12.9-inch 3rd gen
iPad8,9 ~ 8,10 iPad Pro 11-inch 2nd gen
iPad8,11 ~ 8,12 iPad Pro 12.9-inch 4th gen
iPad11,1 iPad Mini 5
iPad11,2 iPad Mini 5
iPad11,3 iPad Air 3
iPad11,4 iPad Air 3
iPad11,6 iPad 8
iPad11,7 iPad 8
iPad13,1 iPad Air 4
iPad13,2 iPad Air 4
iPad12,1 iPad 9
iPad12,2 iPad 9
iPad14,1 iPad Mini 6
iPad14,2 iPad Mini 6
iPad13,4 ~ 13,7 iPad Pro 11-inch 3nd gen
iPad13,8 ~ 13,11 iPad Pro 12.9-inch 5th gen
iPad13,16 iPad Air 5
iPad13,17 iPad Air 5
iPad13,18 iPad 10
iPad13,19 iPad 10
iPad14,3 ~ 14,4 iPad Pro 11-inch 4th gen
iPad14,5 ~ 14,6 iPad Pro 12.9-inch 6th gen

参考链接


OpenSSL通过OCSP手动验证证书

这篇文章主要用来说明如何借助ocsp服务器来验证证书。ocsp(The Online Certificate Status Protocol)是一种验证证书状态的一种方式,也是CRL(certificate revocation list)证书吊销的一种替代方式。

与传统的CRL比较有以下特点:

  • 由于相对于传统的CRL,一个ocsp响应包含的信息更少,故ocsp能够更有效利用网络和客户资源
  • 用OCSP,客户无需自己解析CRL证书吊销列表,但是客户需要存储状态信息,而由于客户侧需要维护存储缓存,故导致存储信息很复杂。在实际使用中,这点带来的影响却很小,由于第三库提供的相关接口已经帮我们完成此类工作
  • OCSP通过专用网络、专用证书、在特定的时间公开其服务。OCSP不强制加密,故可能带来信息泄露的风险。

此文章中用到的openssl的版本为:OpenSSL 1.0.1g 7 Apr 2014

1、获取证书用于ocsp验证

首先,我们将从一个网站上获取一个证书,这里我们用Wikipedia作为样例来进行。我们获取证书通过如下命令:

过该命令可以获取wikipedia.org的客户端证书

保存这个输出到wikipedia.pem文件中

 现在,检查整个证书中是否包含ocsp网址

若执行正确则输出 http://ocsp.digicert.com ,否则你就不能通过ocsp验证这个证书

2、获取证书链

由于这个证书认证是一级一级逐层进行,故需要获得与这个证书相关的证书链。利用openssl s_client -showcerts 选项,能够查看到在该信任链上的所有相关证书

如你所见,输出能够看到两个证书,number 1 和number 0,其中number 0就是我们刚刚获取的那个证书。如果你的网站有更多证书在认证链中,那么你将看到更多证书。为了发送证书,需要保存证书链中所有证书到一个文件chain.pem,按照刚刚命令输出的证书顺序,根证书总是在文件结尾。

3、发送ocsp认证请求

现在我们有ocsp认证请求的所有信息,使用下面命令发送ocsp认证请求。

其结果如下 

如果你需要更简略的输出,去掉-text 选项,该选项一般用于调试

4、吊销证书

如果你有一个吊销的证书,你也可以测试该证书按照上述步骤,所得的响应如下:

5、其他错误

如果证书和ocsp服务不匹配,验证将错误,使用-text选项可以查看具体错误。

参考链接


Flutter 3.16中WillPopScope过期使用PopScope来代替

Flutter 3.16WillPopScope 过期了,需要使用 PopScope 来代替。

针对 PopScopecanPop 参数,官方文档解释如下:

canPopfalse,则执行系统返回时会被拦截,并且调用 onPopInvoked 方法,同时 didPopfalse,此时进行逻辑判断,如果需要返回则执行 Navigator.of(context).pop();

注意此时 onPopInvoked 又会被调用,并且 didPoptrue

参考Demo: github.com

示例代码如下:

修改之前的代码( WillPopScope )如下:

修改之后的代码( PopScope )如下:

参考链接


Flutter的Don't use BuildContext's across async gaps警告解决方法

问题

Flutter开发中遇到Don't use BuildContext's across async gaps警告

有问题的源码

问题原因

“不要在异步间隙(async gaps)中使用 BuildContext” 是一个Flutter中的常见警告消息,通常表示你正在尝试在异步操作中访问 BuildContext,这是一个不推荐的做法,因为它可能引发不确定的行为或错误。

如果在将上下文传递给AlertDialog后导航堆栈发生更改,并且尝试使用旧上下文再次导航,则会出现错误。

问题分析

Context的含义

Flutter中的 BuildContext 和 Context 是相同的,BuildContext 是 Context 的别名。这两个术语用来表示小部件树中的位置信息和上下文环境,用于在构建小部件树和访问资源(例如主题、本地化、导航等)时提供上下文信息。

在Flutter中,BuildContext 或 Context 表示的是一个由小部件树组成的层次结构中的位置。每个小部件都有一个与之相关的 BuildContext,这个上下文包含有关小部件的信息,例如其位置、父级小部件、主题数据等等。

尽管 Context 和 BuildContext 是相同的类型,但通常我们更倾向于使用 BuildContext 这个术语,因为它更明确地表示它是与构建过程相关的上下文。

BuildContext的作用

BuildContext 类型通常用于以下操作:

  • 访问父级小部件:你可以使用 BuildContext 访问小部件树中的父级小部件,这对于在小部件之间传递数据和状态非常有用。
  • 获取主题数据:通过 BuildContext 可以访问当前主题的数据,如颜色、字体、间距等。
  • 获取本地化信息:你可以使用 BuildContext 获取本地化信息,以根据用户的语言偏好来显示文本。
  • 导航:BuildContext 通常用于导航操作,如推送新路由或弹出对话框。
  • 构建小部件:BuildContext 是在小部件的 build 方法中传递的,它告诉小部件在小部件树中的位置。

BuildContext 和 Context 都代表了小部件树中的位置和上下文信息,它们在构建和交互中扮演着关键的角色,但它们实际上是相同的概念的不同表达方式。因此,你可以放心地将它们视为等同的,使用其中一个作为标识符,以便更清晰地表示其作用。

特殊情况

然而,在某些情况下,你可能需要在异步操作中访问 BuildContext,例如在异步回调中执行 UI 操作。这通常是不安全的,因为异步操作可能会在 BuildContext 不再有效的情况下执行,从而引发错误。

解决方法


使用

不要在异步间隙中直接使用 BuildContext,因为它可能会导致不安全的操作。使用提供的方法来安全地查找小部件并在异步操作中访问它们的上下文。这可以帮助你避免潜在的问题和错误。

官方说明如下:

Details

DON’T use BuildContext across asynchronous gaps.

Storing BuildContext for later usage can easily lead to difficult to diagnose crashes. Asynchronous gaps are implicitly storing BuildContext and are some of the easiest to overlook when writing code.

When a BuildContext is used, its mounted property must be checked after an asynchronous gap.

BAD:

GOOD:

GOOD:

参考链接


性能有坑 | 慎用 Java 8 ConcurrentHashMap 的 computeIfAbsent

前言

我们先看一段代码,代码中使用 Map 的时候,有可能会这么写:

Java 8 的 java.util.Map 里面有个方法 computeIfAbsent,能够简化以上代码:

以上这种写法除了简洁,如果使用的是 java.util.concurrent.ConcurrentHashMap,还能够在并发调用的情况下确保 calculateValue 方法不会被重复调用,保证原子性。

不过,前段时间对 Apache ShardingSphere-Proxy 做压测时遇到一个问题,当 BenchmarkSQL 连接 ShardingSphere Proxy 的 Terminal 数量比较高时,其中一条很简单的插入 SQL 执行延迟增加了很多。借助 Async Profiler 发现 Java 8 ConcurrentHashMap 的 computeIfAbsent 在性能上有坑。

不了解 Apache ShardingSphere 的读者可以参考 https://github.com/apache/shardingsphere

继续阅读性能有坑 | 慎用 Java 8 ConcurrentHashMap 的 computeIfAbsent

解决Windows 10备份和还原遇到的0x80070544问题

随着主力计算机设备年限越来越近,对数据保护的重视度也越来越高,尤其之前遭遇过数据损失。目前采用的备份方案是使用文件历史记录功能对 OneDrive 等经常会访问和编辑的目录进行备份。对于其他归档用途的目录则使用备份和还原功能,定期执行一次备份。而备份的位置建议是额外的磁盘或 NAS 提供的 iSCSI,gOxiA 为了图方便和节省 NAS 空间,则使用的是共享文件夹的方式。在实际配置过程中如果使用共享文件夹这样的网络位置,则可能会遇到 0x80070544 故障问题,提示为“请求的验证信息类无效”。

继续阅读解决Windows 10备份和还原遇到的0x80070544问题

Android:Installed Build Tools revision 33.0.2 is corrupted.

使用33.0.2及以上版本的build-tools编译Android应用时。

有些人会按照提示去SDK Manager中重新安装build tools,然后发现这样做是无用的

编译时会收到:

Windows:

Linux/macOS:

解决方案:

更改批处理文件名称

Windows系统:

  1. 找到build tools目录中的d8.bat,将文件名修改为dx.bat。
  2. 找到build tools目录中的lib/d8.jar,将文件名修改为dx.jar。
  3. 回到Android Studio重新打包。

Linux/macOS系统:

  1. 找到build tools目录中的d8,创建软链接 ln -s d8 dx
  2. 找到build tools目录中的lib/d8.jar,创建软链接 ln -s d8.jar dx.jar
  3. 回到Android Studio重新打包。

参考链接


Android:Installed Build Tools revision 33.0.2 is corrupted.