【问题标题】:general protection in a custom OS自定义操作系统中的一般保护
【发布时间】:2011-12-11 19:37:46
【问题描述】:

我打算为英特尔 64 位架构编写自己的操作系统,我需要实现通用保护机制。我不会使用虚拟内存或分页,而是使用物理寻址。我还不知道如何准确地实现它,但我有一个大概的想法,它是这样的:

  1. 在启动时,内核将被加载,假设它会启动名为“filemanager”的第一个用户级进程,该进程将根据从另一个进程接收到的某些信号将一些数据保存到磁盘。
  2. 文件管理器将有一个代码段、一个堆栈段和一个数据段。
  3. 在正常操作期间,内核将启动另一个进程,该进程将从可用的物理内存堆中获取内存,并管理内核负责处理的内存。
  4. 让我们假设,“文件管理器”进程需要获得更多内存,因为它的数据段已满。它向内核发送一个系统调用以请求另一个数据段。内核将请求的内存块提供给它,该内存块物理上位于不同的位置,并且与第一段不连续。

现在的问题是,应用程序('filemanager')将如何处理 2 个段?因为据我了解,英特尔实施的一般保护机制只能检查存储在“DS”中的当前段的限制。但是对于文件管理器,我们现在有 2 个段。我是否必须在应用程序内手动从一个段切换到另一个段?编写这样的应用程序会很痛苦 因为你必须写这样的东西:

    __asm('set DS register to the address and length of segment 1');
    for (i=0;i<size_of_segment1;i++) {
        segment1_data[i]= some_processing();
    }
    __asm('set DS register to the address and length of segment 2');
    for (i=0;i<size_of_segment2;i++) {
        segment2_data[i]= some_processing();
    }

这仅适用于 2 个段,但假设应用程序必须向内核请求 1000 个或更多数据段,您如何实现通用保护机制以便开发人员可以快速轻松地进行编程?一种方法是修改 GCC,它必须在访问每个数据段之前插入汇编代码以将相应的数据段加载到 DS 寄存器中,但这看起来工作量很大。还有其他方法吗?

继续... 5.共享内存。假设另一个进程“编辑器”需要通过“文件管理器”进程保存一些数据。操作系统必须提供共享内存机制。 “editor”进程将请求内核添加“filemanager”进程的数据段。 'filemanager' 将确认请求,并且新段将被添加到编辑器的数据段列表中,以便它可以合法地写入它。同样的问题,我如何在“编辑器”内的多个段之间切换,它对应用程序是透明的,对整个系统来说是安全的?

注意:我不会在我的操作系统中使用分页,所有的内存寻址都是物理的,并且是 64 位模式。

【问题讨论】:

  • 实现受保护内存的方式是通过虚拟内存。你为什么不想使用它?

标签: assembly operating-system x86 x86-64


【解决方案1】:

如果您处于 64 位模式,则根本没有段寄存器:CS、DS... 的内容将被忽略。地址是“平面”地址空间中的 64 位值。段寄存器用于 16 位和 32 位模式,尽管大多数 32 位操作系统将所有段设置为从地址 0 开始并跨越 4 GB,以便它们可能永远忘记它们。如果您使用段寄存器(这样应用程序代码可能会使用多个“段”),那么在 C 级别,C 编译器必须知道它们,并且“指针”将包括段和偏移值。设置段寄存器是昂贵的,所以如果你可以不使用段,性能会更好。这就解释了为什么像 Linux 或 Windows 这样的 32 位操作系统不使用分段,相应地,CPU 供应商已经适应并简单地省略了 64 位模式下的分段支持。

那么真正的问题是:您想要应用程序之间的内存保护吗?内存保护是关于阻止一个进程读取或写入它不应该读/写的内存,即另一个进程或内核使用的内存。如果应用程序程序员从不编写带有错误的代码(嗯……这有多合理?)如果应用程序是用一种包括禁止无效内存访问的系统检查的语言编写的,那么你可以不用受内存保护的内存(例如Java)(JNode 项目是关于Java 的操作系统,但我不知道它是否还活着)。要获得内存保护,请使用MMU。 MMU 通过“页面”工作:地址空间被分成固定大小的页面(x86 上每个 4 kB),操作系统在 RAM 中安装表格,告诉 MMU 每个页面的物理位置。分页、基于磁盘的虚拟内存……使用 MMU。但是您也可以“仅”使用 MMU 进行内存保护(您将其设置为每个页面都存在于与其物理地址相等的虚拟地址处,或者如果当前不访问该页面,则标记为“不存在”允许)。

完全不使用 MMU 可以在某些非常特殊的情况下提高性能(我知道有人这样做是为了筛选,作为面向加密的大型计算工作的一部分:筛选几乎是 100% 随机外观的内存在 1 GB 数组中访问;MMU 意味着 3 次缓存未命中而不是每次访问一次,因此不使用 MMU 会使整个事情快三倍——但这确实是一个边缘情况)。另一方面,MMU 让应用程序开发人员的工作更轻松,特别是因为它允许每个应用程序拥有自己的规范化地址空间(例如,您可以提前编译和链接 C 代码)。

【讨论】:

  • 谢谢!我不熟悉“筛分”,这是某种编程技术吗?我用谷歌搜索发现“Sieve”是 Codeplay 发布的 C++ 编译器。如果您对此有更多信息,将不胜感激。再次感谢!
  • “筛分”是一种在给定范围内寻找平滑整数的方法的通用术语——平滑整数是一个素因数都是“小”的整数。这是分解大整数和求解离散对数的算法的一大步。关键是:这是一个非常特殊的用法,具有不寻常的内存访问模式,它与 MMU 的交互非常糟糕。对于大多数任务,MMU 是“便宜的”,这是一件好事,因为常见的操作系统都使用 MMU。
【解决方案2】:

从另一个角度来看,对于想要访问您的内存的 C 程序员来说,这会是什么感觉? Kernel.AllocateMemory 返回什么?它不能返回 64 位值,因为它不足以同时表示段和偏移量(实际上,今天的 x64 机器访问的内存少于 2^64 字节,因此您可以将段号存储在一些未使用的位,但我认为您不想这样做)。

听起来您正在寻找的是“远指针”(过去的爆炸 - 参见例如http://www.daniweb.com/software-development/c/threads/345287)。

如果您不想手动更改段,那么我看不出如何逃避支持远指针的编译器修改。请注意,如果您只有两个段,则可以使用 DS 和 ES 来寻址数据,但即使这样也需要不同的汇编代码,具体取决于您要使用的段。必须有人决定生成此代码,这将是您或编译器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-15
    • 2011-07-08
    • 2011-04-20
    • 1970-01-01
    相关资源
    最近更新 更多