
在各种各样的开放源代码软件中闲逛,我会定期发现各种有趣的东西:有时候这只是一个有趣的评论,有时从更广泛的意义上讲是一种机智。 类似的集合会定期出现在“全球互联网”和Habré上-例如,在StackOverflow上有一个关于代码注释的著名问题,最近在这里发布了一些有趣的法人名称和地名。 我将尝试进行整理,并规划出逐渐积累的内容。 根据削减,来自QEMU,Linux内核等的报价正等着您。
Linux内核
我认为,对于许多人来说,Linux内核邮件列表中的信件会周期性地出现在引号中已经不是什么秘密了。 因此,让我们更好地看一下代码。 立刻,内核汇编系统给我们带来了一个惊喜:如您所知,由Autoconf构建的项目具有一个Makefile,其具有两个标准的清理目标: clean
和distclean
。 自然地,内核不是使用Autoconf构建的,只有menuconfig
才有价值,因此这里还有更多目标: clean
, distclean
和mrproper
是的,是的,Proper先生, 核心清洁器的两倍快 。
说到配置系统: allnoconfig
,除了allnoconfig
和allyesconfig
类的清晰命令外,我惊讶地发现它(我怀疑可以编译一些非常调试的东西,所以现在我不会冒着在真实硬件上下载它的风险.. )和allmodconfig
到神秘的目标allrandconfig
。 我想:“他们在嘲笑吗?”然后我告诉我的朋友关于此观察的信息,他回答说这可能是完全有意义的命令,但不是用于真正的汇编,而是用于测试选项之间的依存关系安排的正确性-正如我所说的现在,会有一种模糊的配置参数。
但是,组装系统之外的核心是生命:文档有时不仅是技术上的,而且具有某种艺术价值。 假设您想警告睡眠模式用户其脆弱性以及如果不遵循某些规则的话可能会丢失数据。 我会很伤心地写道,请注意:<替换几个最无聊的行> 。 但是编写此代码的开发人员做了一些不同的事情 :
Some warnings, first. * BIG FAT WARNING ********************************************************* * * If you touch anything on disk between suspend and resume... * ...kiss your data goodbye. * * If you do resume from initrd after your filesystems are mounted... * ...bye bye root partition. * [this is actually same case as above] * * ...
小技巧
并非所有代码都可以使用优化进行编译就不足为奇了:当我试图强制为所有目标文件打开代码时,我自然会遇到一些熵的来源,或者如果启用#error
优化,就会出现类似#error
。 好吧,密码学就是这样。 但是,如果要关闭所有优化,内联等功能,是否要编写不会汇编的代码? 这怎么可能? 这是一个静态的断言:
显然,假定对于任何使用常量参数的情况,此函数将仅扩展到一个switch
分支,并且当与有效参数一起使用时,该分支将不是default:
在未优化的形式下,此功能几乎会因设计原因而导致链接错误...
你知道吗
- ...内核在用户模式下有字节码JIT编译器? 该技术称为eBPF,可用于路由,跟踪等。 顺便说一句,如果您不害怕实验性的“核”工具,请查看bpftools软件包。
- ...内核可以使用大约五分钟的处理器时间? 有一个
sendfile
系统调用,它将字节从一个文件描述符复制到另一个文件描述符。 如果您告诉他相同的描述符并在文件中设置正确的偏移量,他将倒带相同的数据,直到他复制2 GB。 - … 用户程序执行的休眠工作方式有所不同-如果您也可以将其保存到网络存储中,我也不会感到惊讶。
量化宽松
通常,当我阅读Robert Love的有关Linux内核设备的信息,然后进入QEMU的源代码时,我有某种似曾相识的感觉。 有一些按值嵌入结构中的列表(并不是像他们在最初的编程课程中那样通过指针学习),还有一个特定的RCU子系统(虽然我仍然不完全了解,但它也存在于内核中),并且,可能更相似。
一个整洁的人想要从事一个项目以了解的第一件事是什么? 可能具有编码风格。 在此上面,也许有人会说说文件,我们看到:
1. Whitespace Of course, the most important aspect in any coding style is whitespace. Crusty old coders who have trouble spotting the glasses on their noses can tell the difference between a tab and eight spaces from a distance of approximately fifteen parsecs. Many a flamewar has been fought and lost on this issue.
这是关于最大行长的永恒问题:
Lines should be 80 characters; try not to make them longer. ... Rationale: - Some people like to tile their 24" screens with a 6x4 matrix of 80x24 xterms and use vi in all of them. The best way to punish them is to let them keep doing it. ...
(嗯……在每个轴上它的大小是我有时使用的两倍。这是Linux HD吗?)
仍然有很多有趣的内容 。
再来一招
他们说C是一种低级语言。 但是,如果变态很好,那么您可以展示编译时代码生成的奇迹,而无需任何Scala甚至C ++。
例如,softmmu_template.h文件被softmmu_template.h
在QEMU代码库中。 当我看到这个名称时,我以为应该将其复制到我的TCG后端实现中,并进行调整,直到出现正确的TLB实现。 无论如何! 这是正确使用它的方法:
accel / tcg / cputlb.h:
define DATA_SIZE 1 #include "softmmu_template.h" #define DATA_SIZE 2 #include "softmmu_template.h" #define DATA_SIZE 4 #include "softmmu_template.h" #define DATA_SIZE 8 #include "softmmu_template.h"
如您所见,手巧而且没有C ++。 但这是一个非常简单的示例。 那么更复杂的事情呢?
有一个这样的文件: tcg / tcg-opc.h 。 它的内容相当神秘,看起来像这样:
... DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(movi_i32, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(setcond_i32, 1, 2, 1, 0) DEF(movcond_i32, 1, 4, 1, IMPL(TCG_TARGET_HAS_movcond_i32)) DEF(ld8u_i32, 1, 1, 1, 0) DEF(ld8s_i32, 1, 1, 1, 0) DEF(ld16u_i32, 1, 1, 1, 0) DEF(ld16s_i32, 1, 1, 1, 0) ...
实际上,一切都很简单-用法如下:
tcg / tcg.h:
typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg-opc.h" #undef DEF NB_OPS, } TCGOpcode;
大概:
tcg / tcg-common.c:
TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg-opc.h" #undef DEF };
甚至奇怪的是,在其他情况下没有发现使用情况。 请注意,在这种情况下,没有用于代码生成的棘手脚本-仅C,仅铁杆。
你知道吗
- ... QEMU不仅可以在完整系统的仿真模式下工作,而且还可以为与主机内核通信的另一体系结构运行单独的进程?
Java,JVM和所有功能
我对Linux有什么了解? 让我们谈谈跨平台的东西。 例如,关于JVM。 好吧,关于GraalVM,也许这个生态系统中的许多开发人员已经听说过。 如果您没有听过,那么简而言之:这是史诗般的。 因此,在谈到Graal之后,让我们继续使用旧的JVM。
有时JVM需要停止所有托管线程-垃圾收集阶段是如此的吸引人或其他事情-但问题是,您只能在所谓的安全点上停止线程。 如此处所述 ,对全局变量的常规检查要花费很多时间,包括带有内存屏障的某种萨满教。 开发人员做了什么? 他们将自己限制在一个变量上。
几乎像HQ9 +有这样的漫画语言-HQ9 + 。 它被创建为“非常方便的教育编程语言”,也就是说,执行学生要求的典型任务非常简单:
- 命令“ H”解释器将打印Hello,World!
- 在命令“ Q”处打印程序本身的文本(quine)
- 在“ 9”上,他打印了99瓶啤酒的歌词
- 通过“ i”将变量i加1
- 他无能为力,但是为什么呢?
JVM如何通过一条指令实现目标? 但这很简单-如果有必要停止,它将使用该变量删除内存页面的显示-线程落在SIGSEGV上,并且JVM将其停泊并在“维护”结束时暂停它们。 我记得在采访中被问到StackOverflow时, 如何使JVM崩溃? 回答:
JNI。 实际上,对于JNI,崩溃是默认的操作模式。 您必须付出更多的努力才能避免崩溃。
开个玩笑,有时甚至是在JVM中。
好吧,既然我提到了Scala中的代码生成,而现在我们只是在谈论这个生态系统,那么这对您来说是一个有趣的事实:Scala中的代码生成(具有宏的代码)的结构如下:您使用API在Scala中编写代码编译,然后对其进行编译。 然后,在编译器的下一个启动时,只需将生成的代码生成器传递给编译器本身的类路径,然后看到一个特殊的指令就调用它,并传递在调用过程中接收到的语法树。 作为响应,他收到一个AST,必须在呼叫位置将其替换。
许可意识形态的特征
我喜欢自由软件的思想,但它也有一些有趣的功能。
大约十年前,有一次,我更新了Debian稳定版,并考虑了一些命令的语法,习惯性地键入了man <>
,该man <>
收到了详尽的描述,例如“ [程序名]是一个程序,其文件经许可分发具有不可变节的GNU GFDL,它不是免费的DFSG。” 他们说这个程序是由一些FSF的邪恶所有者编写的... (现在的讨论是google。)
某些发行版认为某些小型但重要的库是非自由软件,因为作者写信给标准的许可许可证,认为该程序应用于善而不恶 。 笑声,笑声和我也可能会害怕在生产中采用这种东西-您永远都不知道作者对善与恶的看法。
任何杂项
摩尔定律期间国际编译器构建的特征
苛刻的LLVM开发人员限制了支持的对齐方式:
最大对齐方式是1 << 29。
正如他们所说,它会让您先笑起来,然后再想 :第一个想法-但谁需要在512 MiB对齐。 然后,我了解了Rust中内核的开发情况 ,他们提出了使“页表”结构对齐4096字节的建议。 以及您如何阅读 Wikipedia,因此一般来说:
整个48位空间的4 KB页面的完整映射层次结构将占用512 GB以上的内存(约占256 TB虚拟空间的0.195%)。
一旦我决定弄清楚为什么导出不能在一个程序中起作用, 但事实证明它起作用了 ……还是不?
手动启动后端命令后,我意识到,原则上一切都井井有条,只是版本应该以“ 2.0”的形式发送,而只有“ 2”的形式发送。 通过编辑字符串常量来预期微不足道的校正,我找到了double getVersion()
函数-但是,主要是什么,次要是,甚至还有一个点! 但是,最终,一切都没有比预期的复杂得多,我 只是提高了输出精度 转发了数据类型并转发了行。
关于理论家和实践者之间的区别
我认为,在Habré上的某个地方,我已经看到一篇文章的翻译,该文章介绍了启动时发生的最小崩溃是什么,但仍然是C编译程序? int main;
-有一个main
符号,从技术上讲 ,您可以将控制权转移给它。 sirikid正确地注意到,即使int
字节在这里也是多余的。 通常,即使谈论大小为9个字节的程序,最好不要分散声称它是最小的程序。确实,该程序会崩溃,但这完全符合规则。
因此,我们知道如何删除应该工作的东西,但是启动不启动的东西又如何呢?
$ ldd /bin/ls linux-vdso.so.1 (0x00007fff93ffa000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f0b27664000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0b2747a000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f0b27406000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0b27400000) /lib64/ld-linux-x86-64.so.2 (0x00007f0b278e9000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0b273df000) $ /lib/x86_64-linux-gnu/libc.so.6
...把他藏起来 人类的声音 :
GNU C Library (Ubuntu GLIBC 2.28-0ubuntu1) stable release version 2.28. Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 8.2.0. libc ABIs: UNIQUE IFUNC ABSOLUTE For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
程序员打高尔夫球
StackExchange上有一个专门致力于Code Golf的整个网站,其竞赛形式是“根据源代码的大小,以最小的代价解决此问题”。 格式本身涉及非常复杂的解决方案,但有时它们会变得非常复杂。 因此,在其中一个问题中 ,收集了一组标准的禁止漏洞。 我特别喜欢这个:
使用MetaGolfScript
MetaGolfScript是一系列编程语言。 例如,MetaGolfScript-209180605381204854470575575573749277224中的空程序将打印“ Hello,World!”。
一行
导致程序崩溃的未初始化布尔 ,或导致未定义行为的地方
...顺便说一下,这是魔术。 LLVM中的某人在4月1日开玩笑,另一个人对此做了一个报告: 我的小优化程序:未定义的行为是魔术
还记得在跟踪器中赞美开源软件开发人员的模棱两可的吸引力吗? 在Binaryen,它的实现简单而高雅 :
问题:这个项目太神奇了
解决方法: wontfix,可按预期工作
在2009年2月,一个人在StackOverflow上问了一个问题 :“如何做到这一点以及该跨浏览器?或者,好吧, 三种运行良好的浏览器很棒,而且谁也使用Chrome? ”
最后,文章标题从何而来? 这是Emscripten的emcc
编译器输出的一个解释:
$ emcc --help ... emcc: supported targets: llvm bitcode, javascript, NOT elf (autoconf likes to see elf above to enable shared object support)