C

共 58 篇文章。

errno 的实现

• Kernel

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 分钟 )

C++ 中的 main 定义

• Development

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

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

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

新的 arc4random_uniform 实现

• Kernel

本月初, Robert Clausecker 替换了 FreeBSD的 arc4random_uniform(3)。

arc4random_uniform(3) 是 arc4random(3) 之上封装的一个生成一个较小范围伪随机数的函数。arc4random(3) 采用密码学安全的伪随机数生成一个在 32-bit 范围,即 [0, 232−1][0,\ 2^{32}-1] 内均匀分布的伪随机整数,此处的随机分布是依靠对称加密算法(目前采用的是 Chacha20)中用于实现加密的伪随机置换(Pseudorandom Permutation)来保证的。

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

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

• Development, Kernel

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

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

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

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

C main() 的 exit() 和 return

• Development

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

值得注意的是,在实践上,从 main 中 return 和调用 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));
}
阅读全文… ( 本文约 967 字,阅读大致需要 2 分钟 )

Moving toward a standarized qsort_r(3)

• Development

History of qsort_r(3)

First introduced in Research Unix V3 and eventually standardized as part of C Standard Library as of ANSI/ISO C89, qsort(3) provided an abstract interface where programmers can perform sort operation over an array of objects, by supplying a pointer to that array, the total number of the member objects, the size of individual member object,as well as a compare function. The compare function is expected to take two parameters, both pointers to a member object, and it shall return a negative number, 0, or a positive number, if the first object is considered smaller, equal, or greater respectively.

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

记录一下之前对 fsck_msdosfs(8) 的改进

• Development

疫情之前,娃在周末会去某个才艺班,上课的时间我觉得实在是比较无聊,于是就带上笔记本坐在星巴克做一些较小规模的代码清理工作。最终,我利用这些碎块化的时间完成了对 FreeBSD 的 fsck_msdosfs(8) 的核心代码的算法进行了改进,使其需要的内存用量变成了原先的 11281 \over 128,这里稍微记一下当时的一些思路。

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

clang优化器的一个问题

• Development

📜 历史文件已不具备现实意义

今天的一个偶然的发现。FreeBSD clang version 3.6.1 (tags/RELEASE_361/final 237755) 20150525。clang 3.8 2015/07/20 的版本同样有此问题。

之前, FreeBSD 上 strndup(3) 的实现是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
char *
strndup(const char *str, size_t n)
{
	size_t len;
	char *copy;

	len = strnlen(str, n);
	if ((copy = malloc(len + 1)) == NULL)
		return (NULL);
	memcpy(copy, str, len);
	copy[len] = '\0';
	return (copy);
}

而 OpenBSD 上的实现,则是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
char *
strndup(const char *str, size_t maxlen)
{
	char *copy;
	size_t len;

	len = strnlen(str, maxlen);
	copy = malloc(len + 1);
	if (copy != NULL) {
		(void)memcpy(copy, str, len);
		copy[len] = '\0';
	}

	return copy;
}
阅读全文… ( 本文约 304 字,阅读大致需要 1 分钟 )

FreeBSD 的 strlen(3)

• Kernel

之前只有一篇关于较早版本的 strlen(3) 实现的笔记,这里补上我在 2010 年做的新增改进。

与 Pascal 等语言不同,C 的字符串并不保存串的长度,而是在字符串末尾以 nul 字符(’\0’)来表示字符串结束。这个设计决策是上世纪 60 年代作出的,有都市传说是为了省几个字节的空间,不过我个人认为也可能是因为汇编里面到处都是判断是否碰到了 0 的操作。不管怎么说,这个设计令 strlen 变成了一个 O(n) 的操作。

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

Dennis Ritchie病逝

• Tribute

C语言和 UNIX 之父、K&R 的"R",Dennis Ritchie,于2011年10月12日在家中病逝。

Dennis Ritchie 在 Bell 实验室对 Ken Thompson 设计的 B 语言进行了扩展,并以此为基础设计了 C 语言。 C 语言与当时设计操作系统常用的汇编语言相比,提供了更好的可移植性,并成为了 UNIX 系统高可移植性的基础。Dennis Ritchie 做的这些工作是开创性的。这些工作使得我们能够使用包括数据结构、函数等高级语言特性的语言书写程序,并可以很容易地移植到不同类型的硬件上,同时又保持与为不同的硬件手工书写的汇编代码接近的性能。C++ 之父 Bjarne Stroustrup 对于 Ritchie 的工作如此评价:“They said it couldn’t be done, and he did it."。即使在 40 多年后的今天, C 仍然在从嵌入式设备到超级计算机在内的各种计算环境中广泛使用。

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