对未来非常重要的技术将开始从研发实验室毕业并进入市场。更多的自动驾驶汽车将穿越道路。增强现实眼镜甚至可能开始出现在公众面前。美国政府可能会开始在反垄断和隐私等领域对大型科技公司进行监管。业界将继续谈论,在某些情况下甚至开始打造元宇宙。支持Web3的一些基础技术可能会开始站稳脚跟。
本文邀请创业公司创始人、大型科技公司高管、风险投资人、学者和其他专家在他们感兴趣的领域预测明年重大科技趋势。总计收集了40 多个关于 2022 年的预测。
预训练的语言模型将支持新的应用程序和体验,而开发人员的工作量比以前少得多。我们将看到更多人在推特和博客上发布关于低代码和无代码框架的信息,这些框架可以减少开发人员在创建复杂的人工智能应用程序时的摩擦。第二个趋势是我认为的人工智能中的“自我时代”。人工智能自我意识和自主学习的进步,例如可教人工智能,将使最终用户能够根据他们的特定需求和偏好直接定制应用程序,生成创意内容,甚至可能对人工智能及时主动的建议和行动感到惊喜助手。
麦肯锡数字全球领导者罗德尼·泽梅尔(Rodney Zemmel)
量子计算仍处于其发展的相对早期阶段,但我们看到了技术的真正进步,我们已经到了高管需要考虑其业务影响的地步。2022年,我们将继续看到一个快速发展的生态系统,增加投资,加速研究突破。仅在 2021 年,已宣布对量子计算初创企业的投资已超过 17 亿美元,是 2020 年筹集金额的两倍多。我们预计,随着量子计算商业化的发展,私人资金将在 2022 年及以后继续显着增加。
在2022 年发布众多 [混合现实眼镜] 产品。许多公司将利用今年为他们的第一个消费产品的市场推出做准备。2023 年将开始小批量生产,然后在次年进行批量生产。任何 AR 设备最重要的组件都是可以开发应用程序的良好硬件和软件平台。然而,有人可以争辩说,消费者参与开发的能力同样重要。要做到这一点,必须首先引入有吸引力、舒适和时尚的产品来激发兴趣。
电子前沿基金会国际言论自由主任Jillian York
我认为我们应该考虑与AR 和 VR 空间相关的道德和人权问题,以及与隐私和内容审核相关的技术平台的透明度。前一点:这个空间正在真正塑造成一个狂野的西部,我们不应该低估人们进入现实虚拟空间时出现的非常现实的问题。作为消费者,我们必须关注日益现实的虚拟世界所带来的非常真实的威胁,这一点至关重要。. . 请记住,在我们享受它的发展过程中,并不是每个人都可以同样地使用它。至于后者,我们已经看到了公司未能对内容进行节制以及过度节制所带来的危害,尤其是在他们缺乏能力的文化背景下。更好的透明度并不能解决所有这些问题,
工业化生物技术将是人类历史上的下一次工业革命。COVID-19 向世界展示了医疗保健工程的前景。看看我们是如何开发 mRNA 疫苗的,以及开发速度有多快。但我们正处于一场革命的开端,人工智能推动了生物制药和医疗保健领域的工业化,将以前的手动操作变成了可重复、可预测和可扩展的东西。期望看到拥有完整的端到端产品或服务的全栈公司的崛起,这些公司将生物技术带入制药和健康之外的巨大市场,并进入制造、建筑和耐用品——并且以对抗而不是加剧的方式,气候变化。
Frederike Kaltheuner,欧洲人工智能基金主任,欧盟委员会副主席特别顾问
紧急情况下的技术将成为基础设施——无论好坏。新冠疫情正在拖累经济,为快速响应最初感觉像是短期异常状态而建立的系统也在拖延。从更明确的流行技术——接触者追踪应用程序和数字疫苗证书——到工作场所的快节奏数字化,以及世界各地的公共部门,看起来技术“scaffolding”将在许多情况下成为更永久架构的一部分. 这有好有坏:好的,因为早就应该进行升级,也不好,因为要么实施马虎,要么以进一步将权力从人民手中转移到强大的公司或政府的方式。
二、WEB3 和区块链
2022 将正式结束关于区块链和 Web3 是否是未来技术重要支柱的辩论。我们将看到区块链在 P2P 支付、国际汇款......以及其他常见和有用的应用程序中的主流采用和接受,这些应用程序将开始跨越从货币投机者到想过上更好生活的人们之间的鸿沟。
到2022 年,我们将看到企业区块链提供的价值变得越来越清晰,并且在许多行业(例如金融服务行业)中的生产部署将强劲加速。我们将看到更广泛的企业区块链在金融服务行业和供应链用例中的应用,我们将看到第一批金融机构在区块链技术上投入生产。还要寻找去中心化金融(DeFi)范式进入主流企业:企业代币将成为下一代 B2C 网络中的支付媒介,央行数字货币将获得发展动力。
欢迎咨询报考特许全球金融科技师证书
欢迎加入陆家嘴金科创新俱乐部
添加微信:DDKJZX5实名审核后邀请进群!
友情提示:本章内容最好的参考书籍推荐---《程序员的自我修养—链接、装载与库》。
毫无疑问,本章的内容是在不借助任何Windos工具的情况下,如何在Linux下进行操作系统的开发。其实,对于初学者来说,上一章的Windows环境是最好的方式,因为Linux对大多数人来说,都太过陌生了,光在Linux操作系统下进行开发环境的配置都会让你怀疑人生,更别说如何进入C语言内核了。
但是,有人觉得《30天》那本书上的工具太繁琐了,所以问我如果不想用那一套工具,该怎么办?还能怎么办?只能在Linux操作系统下用GCC那一套编译链接工具啊。
另外,由于我们是正在自制Linux操作系统,因此很有必要体验一下Linux的强大。什么?自制Linux操作系统,还要在Linux下进行?这个不奇怪,在计算机领域,这种“自举”行为比比皆是,计算机科学就是一个不断搭积木,越来越庞大和强大的过程。
完善gcc编译安装环境:
10.Ubuntu下bochs的安装配置-自己动手写操作系统环境搭建
11.配置bochs:在任意工作目录下。
(3) 将二进制机器码启动文件写入软盘镜像a.img
不带任何参数的Bochs并执行,Bochs将在当前目录顺序寻找以下文件作为默认配置文件:
(5) 用命令bochs即可启动虚拟机调试操作系统
记住:安装bochs的目的纯粹是为了调试我们的自制操作系统。
安装后启动VMware可能会出现以下错误:
此主机不支持"Intel EPT"硬件辅助的MMU虚拟化。
此主机似乎在禁用了VHV的虚拟机中运行。请确保在虚拟机
配置文件中启用了VHV.
黑苹果中嵌套虚拟机/开启VT(虚拟机嵌套安装)
安装Vmware的目的是用来验证我们的自制操作系统。
二、内核程序编译、链接和装载过程
首先看,我们的操作系统整体程序结构:
Linux下编译C语言编译工具是GCC,标准输出格式是ELF,因此,这次我们的核心问题是:操作系统如何从纯二进制的机器代码boot_setup.bin中顺利过渡到ELF32格式的kernel.bin中。
本章我不是解读ELF的理论内容,只说我的理解和实现过程。
(一) ELF格式标准
elf 文件格式的核心思想就是头中嵌头,是种层次化结构的格式。
ELF header 是个用来描述程序头和节头的“头”。
多个程序头就组成程序头表:Program header table,多个节头就组成节头表:Section header table。程序头表和节头表是程序头和节头的集合,因此相当于是个数组,数组的元素就是程序头或节头数据机构。
节就是指定具体的某些代码、数据或符号了,见图中的section n。
它们之间的逻辑指向关系,用下图来描述更合适:
理论很枯燥,我用C语言写个简单的打印字符串的程序来分析ELF格式。如果这个C程序能成功启动,就证明进入内核没有问题。
/*打印一个字符,参数:字符,行号,列号,字符颜色*/ /*打印一个字符串,参数:字符串首地址,开始行号,开始列号,字符颜色*/
通过编译、链接后生成ELF32(详细命令后面会贴出来)格式的可执行文件kernel.bin,通过elf标准识别命令来读取该文件的内容:
打开整个文件,可以看到很多的节:section,程序中最重要的两个节: .text和.rodata,我们姑且把它叫做代码节和数据节,代码节的长度是0xaf,而数据节的长度是7(即字符串”kernel\0”)。所有的section从section 0---section 8数量一共是9个,这个和ELF header中是一致的:
按我们操作系统的设计,ELF格式的kernel.bin文件是已经被装载到内存中了的,现在又在kernel.bin中找到了代码节。按道理,我们只需要直接跳转到ELF文件中的代码节就可以继续运行了。可是内核程序运行过程中,还需要访问数据,虽然数据节也在kernel.bin文件也能找到,但是由于访问这些数据的机器代码指令是GCC编译器输出的,GCC编译链接源程序的时候,是不可能知道用户会把数据放在内存什么位置的,所以GCC会把每个数据节都指定一个内存地址:最终就体现在了每个节section对应的Addr字段。因此,我们为了能正常访问内核数据节,还需要做一个重要的事情:
把GCC编译链接出的ELF格式文件中的数据节全部挪动(复制)到相应的内存位置。这个和我们上一章《如何进入C语言内核程序(Windows开发环境)》中“必须要提前将可执行文件中的所有变量从文件区挪动到可执行文件中规定的堆区”是一个道理。
既然数据节采用这种机制,那代码节也可以采用这种机制,因此ELF文件采用相同的管理方式也会给每个代码节指定一个内存地址。当用户想运行代码节的时候,只需要把代码节的全部内容挪动(复制)到相应的内存位置,然后跳转到目标内存地址就能运行目标代码了。
故为了完成无论是代码节、数据节的数据复制,在ELF文件中,每个节section都指定了它在文件中的偏移(off)、它应该复制到的内存地址(Addr)以及它的长度(size)。具体到本例,代码节和数据节它们的文件偏移值很明确,上图中分别是:0x500和0x5af,由于代码节的长度是0xaf,可以看出数据节是紧挨着代码节的,中间没有间隙。由于数据节的长度是7,因此代码节和数据节在文件中的偏移位置就是:0x500---0x5b6。
关键是上面这些代码节和数据节的数据结构从哪里能获取到呢?首先,我们需要定位到section heaer n,通过section heaer n就可以定位到section n,定位过程如下图:
因此,我们通过以上过程,分析ELF文件中的section header是可以实现C程序内核加载的。
用上面的方法虽然可以完成ELF文件的加载和执行,但是用得更多的却是通过是Program heaer的方式。这是因为ELF文件在节的基础上,又做了二次包装:program。可以把program理解成“段”,而描述每个“段”的数据结构是program header,具体到我们这个kernel.bin,它共有3个program header:
我们具体深入分析第一个program header 0,它的文件偏移值是0,长度是0x64c。这个长度已经跨过了之前的代码节和数据节:0x500---0x5b6。故可得出结论:program(段)是由节来组成的,多个节经过链接之后就被合并成一个段了。具体哪些节合并成了哪个段,可以通过elf文件的mapping信息部分看出:
而图中的实际长度是0x64c,和0x14A差得有点远。别着急,我们再仔细看,它的offset字段偏移值是0,就是说它是从ELF文件第一个字节开始截取的,因此截取内容除了以上三个节之外,还有ELF header,program header talbe等内容。
那我们怎么计算它多截取的长度呢?很简单,其实就是.text的第一条指令相对于EELF header装载后的开始地址偏移值。.text的第一条指令就是程序入口点地址,这个地址是0x0051500,而ELF header装载后的开始地址是0x0051000,所以,这个偏移值是:0xxx500。
基于上面段是由节合并而成的原理,现在我们只需要取出program(段)数据就已经完全覆盖了程序的所有代码和数据了,所以用program header的方式更直接简单。
至于后面两个program header 1和program header 2,对本次程序没什么用,可能是用于别的用途。保守起见,一般是遍历所有的program header,把全部数据都复制到的内存相应的位置。
这两个参数在elf文件的偏移处参考其定义标准,偏移值分别是:28和42。
那每个program header本身数据结构,是怎么样的呢?可以从ELF32的标准中得到答案:
其中,我们需要的就是标红的3个字段,这3个字段的偏移值分别是:4,8和16,分别代表的是数据源地址、数据目的地址和数据大小。这样,我们就能顺利完成这个工作:将所有program header数据结构描述的数据内容从ELF文件区复制指定的内存区。
内核映像的装载其实就是:将所有program header数据结构描述的数据内容从ELF文件区复制指定的内存区,它的本质就是数据复制。分析完上面的原理,具体到本次我们的操作系统,最终的ELF内核映像装载原理总结为下图:
说明:操作系统内核文件kernel.bin被装载进内存之后,我们的ELF文件物理上是直接贴在head.bin后面的,由于head.bin是system最开始的部分,system全部代码是被整体挪到了内存0地址的,因此ELF文件:kernel.bin放置的物理地址实际就是程序head.asm中最后一行的偏移,我们给这个物理地址起个专用名词:KERNEL_BIN_BASE_ADDR,所有数据的复制工作源地址都是基于基址:KERNEL_BIN_BASE_ADDR进行寻址的。
那么,内核映像的目的装载地址是怎么确定的呢?这个很简单,就是你想把内核映像代码放在什么位置自定义即可。我们还是借用上一章的思路,把内核映像的起始地址定义在0x00000---0xA0000的中间位置附近:0x51000,也即324KB处。故,我们用下面的GCC链接指令实现这个地址规划:
要点1:关键字-Ttext是链接器ld用来指定最终代码段.text存放的内存地址。我们知道,在Linux下,链接器ld生成ELF可执行程序默认的内存地址是0x,0x是一个虚拟地址,Linux操作系统最终会跳转到这个虚拟地址从而启动ELF可执行程序。但是现在,我们是在自制操作系统,就必须要自己指定一个代码段.text的最终存放地址。
要点2:Linux下GCC编译器默认的入口地址是_start,由于我们这次工程的入口地址是C程序中的main(),因此还必须手动指定这个入口地址。当然也可以在C程序中把main函数换成start名称,但是我们还是保持一般习惯就好。
可以看到,内核映像虽然是复制到0x51000处,但是C程序main()真正的入口地址却是0x51500,我们给这个入口地址取了个名称:KERNEL_ENTRY_POINT。如前所述,这两个地址之间0x500的长度,其实就是包含了ELF header和program header等相关的数据。
具体到要将所有program header数据结构描述的数据内容从ELF文件区复制指定的内存区,编写的汇编程序如下:
另外,我们最好重新规划栈区。和上一章一样,我们把栈设置在内核映像0x51000的下面,最后跳转到内核映像的main()入口地址0x51500(KERNEL_ENTRY_POINT)即可。
在上一章中,我们当时的实验有一个很重要的环节就是:C程序打印字符串“kernel”传递字符串位置指针参数问题。当时的实现方法是:链接器将编译输出的目标文件中的机器指令"68 []"最终变成了可执行程序中的机器指令"68 []",由于“kernel”字符串已经被我们提前复制到了堆区0x100000,因此最终实现成功打印。
那么,这次GCC编译器是不是也采用同样的套路呢?我们来跟踪分析一下。
2. 我们通过命令,把kernel.bin的代码节和数据节的机器代码单独提取出来,生产一个新的文件:code.out:
可以用二进制工具查看code.out具体的内容:
3.现在,我们对把机器代码code.out做一个反汇编,并打开汇编语言程序:
上图圈红的地方就是打印字符串“kernel”的调用过程,可以看到,传递字符串“kernel”的内存地址指针参数,是通过eax入栈来实现的!
现在,我们来单独分析“kernel”字符串地址指针参数的值究竟是多少。
程序第一条指令是call 0x8d,我们打开反汇编后的文件,看看0x8d的具体内容:
指令:mov (%esp),%eax的作用就是:把内存[ss:esp]的内容赋值到eax。这个语句设计得有点巧妙,具体到这里的上下文信息,它的作用是:通过EAX返回当前指令运行结束时所在的内存地址!过程:当开始执行函数调用call 0x8d指令时,当前EIP值入栈,进入函数体之后,马上就把栈顶的数据传递给eax,故eax就取到了函数调用时的EIP之值。那进入函数体之时,EIP之值是多少呢?显然是内核映像的起始地址+call 0x8d指令本身的长度:因为call 0x8d是我们的操作系统是跳转到内核映像入口地址:KERNEL_ENTRY_POINT之后,运行的第一条指令。由于操作系统CS指向的基地址是0,故执行到call
接着往下看,和字符串指针参数eax有关的指令只有3句,我们单独摘取出来分析:
那么“0x515af“就是字符串指针参数eax最终的入栈值,也既是C语言内核最终的机器代码就是通过这个内存地址----[0x515af]来访问字符串“kernel”的,那这个地址能成功访问到吗?请看下图:
可见,这个地址值刚刚好!因为我们之前已按要求通过program header 0早已把"kernel"字符串数据装载到内核映像所在的这个内存地址了。
这就是C语言内核程序能实现数据访问的机制和原理,可以看出,GCC编译器的实现过程比我们上一章的实现过程复杂多了,这也是不同编译器之间的差异之处吧!
行了,我们最后再将内核C程序完善一下:加入一个汇编程序kernela.asm进行链接。这是因为在内核开发中,汇编程序肯定会和C程序之间有相互调用关系,我们在这提前把环境配置好,我们整体测试方法是:在C程序里调用汇编程序里面的函数:asmfunc,在汇编程序里调用C程序里面的printstr函数,两次调用都支持参数传递。如果成功,则打印字符串“C->A->C”。
这里有个细节需要注意下:有些编译器(如上一章的Windows环境)要求汇编程序里面的函数标号必须要带下标符号"_"才能与C程序里面的对应函数相链接,而我这次Ubuntu的GCC编译器则没有这个要求,两边保持相同名称即可。
对其编译、链接(详细makefile见后),我们的操作系统启动之后,在Linux的Vmware下运行结果如下:
至此,历经千辛万苦,本次自制Linux操作系统的C语言内核已经成功装载,开发环境已经全部搭建完毕!我们可以用C语言进行大刀阔斧的开发操作系统内核了。