Development

errno 的实现

| Development | #FreeBSD | #POSIX | #C | #Threading

IEEE Std 1003.1-2024 (POSIX) 中对于 errno定义如下

The lvalue to which the macro errno expands is used by many functions to return error values.

在更早期的 POSIX(Issue 5 及以前)以及 X/Open 文档中,曾经规定 errno 是一个外部变量(extern int errno),但这使得 errno 无法实现线程安全,因为所有线程共享同一个全局变量, 一个线程的系统调用返回的错误码会覆盖另一个线程的值。因此,POSIX Issue 6(即 SUSv3 / IEEE Std 1003.1-2001) 将这一要求删除,改为现在的定义:只要求 errno 是一个展开为 int 类型的可修改左值(modifiable lvalue)的宏。 这为实现者提供了足够的自由度,以支持线程安全的 errno

ISO C 标准在 C90/C89 时期已经不再要求 errno 是外部变量。

阅读全文…( 本文约 1554 字,阅读大致需要 4 分钟 )

cron 的 PAM 支持

| Development | #FreeBSD | #cron | #PAM | #系统管理

在上一家公司的时候曾经有很多关于 cron(8) 的想法,但一直没有付诸实施,很多东西放在本地慢慢生锈了。

去年年底的时候我开始着手去修了一些 cron(8) 的 bug,其中包括为 cron 之前不够完整的 PAM 支持进行了改进。

背景:PAM

Pluggable Authentication Module (PAM) 为一系列身份验证相关的操作提供了一组通用的接口。 在没有 PAM 之前,应用程序需要实现大量重复的代码来完成类似的操作,例如要求用户输入密码并进行验证等。 这么做的问题在于缺乏灵活性,例如,管理员可能希望将用户数据保存到 LDAP 目录中, 或是使用指纹等新式验证方式。PAM 使这些相关操作可以通过插件来完成, 应用程序不再需要关心「如何验证用户身份」(用户名、密码等等是不是正确),而只需要向 PAM 询问 「该用户的身份是否合法?」即可。这层抽象简化了应用程序开发者的工作, 并且赋予了系统管理员更大的灵活性。

PAM 将系统的认证工作分成了四组基本操作:

阅读全文…( 本文约 974 字,阅读大致需要 2 分钟 )

CSS 中的标点悬挂及其现状

| Development | #CSS | #Typography | #排版

什么是标点悬挂

标点悬挂 (Hanging punctuation) 是一种排版微调技术。当一行文字以标点开头或结尾时,标点可以「悬出」段落的对齐边界, 使正文文字的视觉边缘保持整齐。虽然差别细微,但在大段文字的排版中, 这种整齐的边缘能在一定程度上提升阅读体验。

阅读全文…( 本文约 1104 字,阅读大致需要 3 分钟 )

写给未来的blog:关于引用、结构、导航与 AI

| Development | #Blogging | #Web | #AI | #互联网 | #博客 | #写作

早年,车东 提出过一个愿景:「让互联网:良好引用,良好结构,良好导航」, 这句话一直让我印象深刻。一直以来,对互联网内容的彼此可以有效引用、结构清晰、可以被(人类和机器) 理解和持续访问而不是被时间与系统更新湮灭,是我和许多 blog 作者的理想。在我自己的 blog 中, 我也一直在尝试实现这三个目标。

阅读全文…( 本文约 1460 字,阅读大致需要 3 分钟 )

Hugo blog 主题模板重构

借着 Vibe Coding 的东风,我为 blog 重新设计了一套模板,希望解决一些此前用的基于 ink 模板中存在的一些问题,并彻底清理掉一些历史包袱。

这件事还要从 Search Console 的 Core Web Vitals 报告说起。报告显示,旧主题使用的 Noto Serif SC 网页字体加载缓慢, 并在渲染过程中导致了显著的累积布局偏移(CLS)问题。

在调整网站 CSS 的过程中,我逐渐意识到原有主题模板存在不少结构性问题;与其零碎修补,倒不如直接推倒重来。 我的想法是,首先用 Hugo 新建一个最新版的最小模板,然后逐渐在上面添加我需要的功能,于是我列了一个需求 wishlist 清单:

阅读全文…( 本文约 1367 字,阅读大致需要 3 分钟 )

git submodule 与 subtree 的异同

| Development | #git | #版本控制 | #submodule | #subtree

前几天有小伙伴在整理某个代码仓库的时候,希望把仓库里的代码和数据分离以便于管理。 由于他使用的是 git,所以很快大语言模型便引导他用上了包括 filter-repo 在内的一系列禁术, 其间就有了一段关于是应该使用 git submodule 还是 git subtree 的讨论。

先说我的结论,对于绝大多数人,特别是已经动了重写历史这种念头的人来说, 理想的选择是 git submodule。

阅读全文…( 本文约 1857 字,阅读大致需要 4 分钟 )

C++ 中的 main 定义

| Development | #C++ | #C | #Programming | #Linkage | #GCC | #LLVM

新的 C++ 标准中 不允许给 main 指定 linkage-specification 了。

当然,考虑到原本 main() 也是 C 运行环境在开始运行程序的时候调用的, 而 C 运行环境自然也预期 C linkage,即不按照 C++ 的习惯对符号根据参数增加名字前缀, 因此大部分编译器在遇到 C++ 程序定义全局 main() 的时候也会按照习惯采取 C linkage 方式去翻译。这一规则首先被 GCC 采纳,随后 LLVM 也跟进了。

阅读全文…( 本文约 389 字,阅读大致需要 1 分钟 )

FreeBSD 14.0-RELEASE 发布了

上周末抽时间把服务器升级到了 FreeBSD 14.0-RELEASE。 软件的发布中存在许多的工序,大致上,在 releng/ 分支上的代码树会正式命名为 -RELEASE, 同时由一位 Release Engineer 开始最终的 build(对应的文件会发布到 FTP 上, 并在 网站 上提供链接),并在适当的时候将 releng/ 分支上的代码 tag 成 release。此后, Security Team 需要将 Release Engineer 签名的 -RELEASE 放到 freebsd-update builder 上再次 build、签名,并生成二进制更新所需的文件。

由于安装用的 ISO 映像文件都比较大,传统上将这些映像文件分发到全球的镜像站点上需要一些时间。 现时,云服务提供商往往还有自己的 QA 步骤,因此最终宣布 -RELEASE 的时间往往会比 FTP 上出现的时间晚上一周左右。技术上这段时间这个 build 依然只是一个发布候选版本(Release Candidate), 因此普通用户不应使用这些版本,因为在这一周的时间如果遇到一些突发状况的话可能会需要将这个版本撤回 (例如原本应发布为 FreeBSD 4.6.1 的 FreeBSD 4.6.2 就是这样的情况)。

阅读全文…( 本文约 1097 字,阅读大致需要 3 分钟 )

记录一下当年把 FreeBSD 中 zlib 砍到只剩一份的过程

软件项目中,实现同一功能的源代码只保留一份是一项十分重要的最佳实践,这种做法可以带来许多显而易见的好处:

  1. 简化依赖关系管理。 对于 C/C++ 项目来说,如果同一个函数库有不同的版本,意味着必须设法确保其中不包含同一符号的多个变体。
  2. 减少技术债的积累。 只保留一个版本意味着参与项目的所有开发者都使用最新版本的库,或是从一个接近最新版本的库升级到最新版本,这要比把技术债留给后人去解决要容易许多。尽管升级时需要考虑的问题会更多一些,但这也意味着更好的一致性。只留一份版本意味着在升级时必须通盘考虑全局的影响,配合持续集成测试的使用,这也会带来更好的代码品质,并让整个团队能够更快地进行迭代。
  3. 节省各类存储占用
  4. 改善整体安全性。问题只需在一处进行修正。

2009年的时候 kmacy@ 做了 一些初步的工作,但后续没有继续推进。 这之后 考虑重新把这个事给做掉,但苦于平时比较忙因此未能如愿。最终, Yoshihiro Ota 完成了大部分的工作。

阅读全文…( 本文约 2128 字,阅读大致需要 5 分钟 )

C main() 的 exit() 和 return

| Development | #C | #Programming | #exit | #return | #main | #libc | #编程

这里讨论一个犀利而无用的细节问题。事情的缘起是有人在 GitHub 上提了一个 pull request 要求把许多程序的 main() 的终结部分从 exit(X) 改为 return X;,我反对了这一变动。

值得注意的是,在实践上,从 mainreturn 和调用 exit(3) 几乎等效的(此处还是有细微差别, 后面将会讨论),原因是 C 运行环境库的启动部分(这部分会在连接过程中嵌入到可执行文件中, FreeBSD 的实现中,这部分位于 lib/libc/csu/libc_start1.c__libc_start1

1
2
3
4
5
6
7
void
__libc_start1(int argc, char *argv[], char *env[], void (*cleanup)(void),
    int (*mainX)(int, char *[], char *[]))
{
/* ... */
	exit(mainX(argc, argv, env));
}
阅读全文…( 本文约 828 字,阅读大致需要 2 分钟 )