南京大学操作系统课程学习4

计算机安全

在操作系统API上,我们可以构建命令行工具、编译器、数据库,浏览器等丰富应用。

在8086时代,每次的访问都是直接映射到地址空间中,任何程序可以访问任何硬件。百度可以复制自己,然后感染更多的计算机程序。

我们希望计算机有confidentiality,integrity,availability。

有一些软件硬件的协同设计。

  • 分页机制和进程隔离
  • 系统调用和访问控制
  • 鉴权和授权
  • 加密
  • 审计与日志
  • 机密计算(TEE)

理论上应用程序如果不信任操作系统,硬件也提供了特权来执行应用程序的代码。

访问控制

进程+虚拟内存已经实现了隔离。进程只能以ELF规定的权限访问自己的虚拟空间地址。系统是唯一访问操作系统对象的途径。(前提是内核没有被漏洞攻破)

操作系统还有一些虚拟化机制、容器。

访问控制:限制程序对操作系统对象的访问。拒绝越权访问和修改。

理论上可以直接用一张表来做访问控制。然而表可能特别大。unix用整数表示身份

uid=0就是root用户,gid是完全自由的,虽然一般0是root,mode规定了rwx的权限。

操作系统没有用户名的概念,操作系统也不负责验证用户名。有个程序叫做login负责进行密码的验证。

现在系统通常使用shadow文件存储密码的hash。chsh,passwd只是直接修改了文件。操作系统只管uid不管如何解读用户。

如今的操作系统有很多uid:

  • real uid(ruid)
  • saved uid(suid)
  • effective uid(euid)
  • filesystem uid(uid) 如今已经没人用了。

操作系统还有非常多的访问控制的方法

  • access control list(ACL)
  • SELinux、AppArmor
  • capabilities

攻防

假设我们运行这样一段程序

会发生段异常。这个叫做undefined behavior。

容器、虚拟机、微服务

进程一直以来都是操作系统中的核心抽象。作为应用程序的主题,运行它的方式在多年的发展中经历了许多变化。

操作系统理论上是可以无限套娃的,比程序完成整个操作系统。取指令,编译代码,执行

但是它的性能,不及原生的10%。full system emulation

1997年 brings back an idea popular in the 1970s: virtual machine monitor

VMware在1998年把这个技术做成了产品。VMware会把应该在系统内部调用的程序放在操作系统直接运行。但是系统调用会被VMware程序劫持变成一个软件模拟的系统调用。

wsl在windows下模拟了linux地址空间,如果发现systemcall来自linux,就用linux子系统完成。

之后intel提供了许多虚拟化指令集。

VT-x 2005->VT-d 2006 ->EPT 2008

  • 一些特权指令没法直接执行,比如关闭中断。可以让虚拟机来控制中断。
  • VT-d提供了堪比物理机一样的高效的IO:IO虚拟化。
  • EPT提供页表,8级页表其中4级给操作系统用,4级给虚拟机用。

Intel 的 EPT(Extended Page Tables,扩展页表) 是硬件辅助虚拟化技术的一部分,主要用于优化虚拟机(VM)中客户机物理地址(GPA)到宿主机物理地址(HPA)的转换。

在没有 EPT 的情况下,虚拟机监控器(VMM/Hypervisor)需要通过软件模拟页表,处理客户机的内存访问请求(GPA → HPA),导致大量性能开销(称为“影子页表”问题)。

EPT 在硬件层面提供第二层页表(EPT 页表),直接由 CPU 的 MMU(内存管理单元)完成 GPA 到 HPA 的转换,避免了 Hypervisor 的软件介入,大幅降低内存访问延迟。

最早的时候通过虚拟机进行超卖。

虚拟机导致更容易的管理状态了。

最早的系统的版本休眠是可以掉电的。掉电之后打开还是可以继续之前的状态的。但是在物理机上实现这个功能是很复杂的,需要把所有进程都停下来。遍历设备程序驱动。dump到磁盘里。但是虚拟机很容易实现这个功能,因为虚拟机就是一个进程,直接做一个core dump就可以了。

另一个技术叫做空间转移的技术:optimizing migration of virtual computers

还可以使用虚拟化技术做操作系统内核的热更新。

不同虚拟化技术的实现差异

全虚拟化(Full Virtualization)

使用二进制翻译(如早期VMware)动态替换特权指令,或依赖硬件虚拟化扩展。

Guest OS无需修改,但性能开销较大。

半虚拟化(Paravirtualization)

Guest OS被修改,主动调用Hypervisor提供的API(如Xen的hypercall)代替特权指令,避免陷入开销。

容器(Container)

共享主机内核,通过命名空间(namespace)和cgroups隔离资源,直接调用主机系统调用,无虚拟化层。

容器

但理论上操作系统自己就能虚拟化自己。操作系统理论上可以假装自己在虚拟机中执行一个系统调用。

pid可以不再是整个系统唯一的,给每一个进程增加一个osid,增加系统调用vos(fs_root),这个系统调用会创建一个新的虚拟机。新的树的osid就是2。但是os2的getpid就可以由操作系统控制。然后准备俩个不一样的文件系统。

  • 创建一个新的osid
  • pid从1开始分配
  • fork()继承父进程的osid

每一次systemcall都是在当前的osid下执行,对其他osid屏蔽。

但是由于增加了osid,要确认操作系统的哪些资源是可以被虚拟化的。

  • pid
  • user:用户和组
  • mnt:文件系统和设备
  • ipc:信号量、消息队列、共享内存
  • net:网络设备、协议栈、端口(localhost:5000)
  • time:系统时间和时区
  • uts主机名和域名.

在操作系统中可以看到自己的namespace,linux: /proc/[pid]/ns

在这个基础上我们还想进一步实现资源的调度,圈一些进程,使用特定的资源策略

Control Groups (cgroups) 是 Linux 内核提供的一种机制,用于限制、记录和隔离进程组的资源使用情况(如 CPU、内存、磁盘 I/O、网络等)

Cgroup是于2.6内核由Google公司主导引入的,它是Linux内核实现资源虚拟化的技术基石,LXC(Linux Containers)和docker容器所用到的资源隔离技术,正是Cgroup。

那么你发明了cgroups,这是一个和namespaces正交的机制。

共同使用,你就得到了容器。例子:只有busybox的系统中的系统

到这里你就发明了docker(2013)。

在2008年就有了Linux containers,通过整合内核的 cgroups(控制资源)和 namespaces(隔离进程、网络等),首次在 Linux 上实现了完整的容器功能。但配置复杂,需手动管理。由IBM工程师开发,得到了linux社区的广泛贡献。

Google 的 Borg 系统(2004年内部使用,2015年发布)​​:大规模使用容器技术(基于 cgroups)管理集群资源,但未开源,仅限于 Google 内部。

如果只需要linux,容器就和虚拟机一样。开销比虚拟机低很多,安全性略低。如果操作系统有资源没有很好的被namespace隔离开,就可能产生安全问题。比如机器的总端口可能是有限的。

Kubernetes:容器编排,跨主机、弹性自动伸缩。这是云厂商最爱看到的。

每个容器可能非常小,比如rust写的,几十mb就能跑起来了。

云原生,serverless

直接将函数最为服务,进行按量付费。serverless连容器保活都不需要了。function as a service。

嵌入式和移动操作系统

embedded计算机系统是嵌入到设备中的。人造卫星,工业控制、家用电器、医疗器械、穿戴设备。

体积小、计算第、可靠性高,操作系统通常更简单(领域固定);但linux也是可以的

例如PLC(programmable logic controller)和工控。

通常有实时保障。在严格的时间内对外部事件做出可预测响应。

比如我们希望某个程序一毫秒执行一次,但是linux就难以做到这样的事情(特别是当程序运行的越来越多的时候)

无论车机多花哨,最终实时控制仍然是MCU。

比如FreeRTOS。支持抢占式调度,按照优先级执行任务,确保高优先级的任务及时响应。内核仅需几KB ROM和RAM,适合资源受限的MCU。

高优先级的任务可以实现更高优先级的时间片分配。但是优先级也会带来麻烦,比如低优先级的任务拿到了锁,导致高优先级的任务被阻塞。高优先级任务只能让出执行时间片给低优先级的任务先执行。这叫做优先级反转(priority inversion)。应用程序的依赖可能形成等待链,导致系统变卡。

到系统出现不稳定时,重启系统总是能解决问题,是因为重启能恢复状态机的状态。

Android

最早的手机不能安装软件,只能收发信息和打电话。甚至只能显式三四行的文字。之后诺基亚的5310可以执行一些复杂的任务。支持彩色的显示,并且能够播放音乐,运行可安装的java程序。j2ME时代,甚至可以用java渲染网页。320x240的屏幕。

之后乔布斯发布了ios。谷歌2005年收购了Android inc。安卓是完全基于linux开发的,Linux+Full JVM+Framework API开放平台。

一开始用java发现程序非常卡。当时是32位的armv6@528mhz(1cpu,TSMC)

塞班系统当时暴毙于C++,windows phone 暴毙于C#。当时java有很大的市场。

现在看起来这是一个很高瞻远瞩的决定,赌摩尔定律生效(同年 i7865,4c8t)

存在一个推理,在未来存在一个临界点,能实现实时推理无处不在。

在安卓5.0的时候支持将所有程序从原来的.class编译到.out

安卓之间应用程序经常需要进行写作。安卓提供了remote.transact()。

这是在优化和易用之间的权衡。

安装也提供了丰富的开发者工具。比如android studio。DDMS。android debug bride(ADB)。还有android shell。

比如可以监听系统的所有事件。简单来说我们可以将硬件操作挂在到一个设备上,和之前提到的一样,读取硬件的操作,然后捕捉对应的事件。我们写一个程序定期拍照,然后将点积事件传回给手机,就有了电脑控制手机的功能(scrcpy)。

甚至可以任意改变APP的行为。

如何杀死一个android进程?android每个app都有独立的uid,遍历进程表,找到属于uid的进程。遍历进程表来杀死进程的时候,可能出现杀不干净。安卓默认间隔5ms,连续杀死40次,防止偶然杀不干净。

Last modification:August 19, 2025
如果觉得我的文章对你有用,请随意赞赏