自定义操作系统中的一般保护
我计划为Intel 64位架构编写自己的操作系统,并且我需要实现通用保护机制。我不会使用虚拟内存或分页,而是使用物理寻址。我还不知道如何具体实现它,但我有一个总体想法,它是这样的:
- 启动时,内核将被加载,假设它将启动第一个名为“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’进程的数据段。 “文件管理器”将确认请求,新段将添加到编辑器的数据段列表中,以便它可以合法写入。问题又是一样的,如何在“编辑器”内的多个段之间切换,以对应用程序透明且对整个系统安全的方式?
注意:我不会在我的操作系统中使用分页,所有内存寻址都将是物理的,并且将处于 64 模式。
I am planning to write my own OS for Intel 64bit architecture, and I need to implement General Protection mechanism. I will not be using virtual memory or paging, but physical addressing. I do not know yet how exactly to implement it, but I have a general idea and it goes something like this:
- On startup, the kernel will be loaded and lets suppose it will launch the first userland process called 'filemanager' which will save some data to disk on some signal received from another process.
- The filemanager will have a code segment, a stack segment and a data segment.
- During the normal operation the kernel will launch another processes, which will take the memory from the available physical memory heap, managing which the kernel takes care of.
- Lets suppose, the 'filemanager' process needs to get more ram because its data segment became full. It sends a syscall to the kernel to ask for another data segment. The kernel gives it the requested memory chunk which is located physically in different place and it is not contigous in relation to the first segment.
Now here is the question, how does the application ('filemanager') will handle 2 segments ? Because the General Protection mechanism implemented by Intel, as I understand it, can only check for the limits of the current segment which is stored in 'DS'. But in case of filemanager, we have 2 segments now. Do I have to switch manually from one segment to another inside the application? It would be a pain to code an application like this
because you would have to write something like this:
__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();
}
This is only for 2 segments, but imagine the application will have to ask the kernel for 1000 or more data segments, how do you implement General Protection mechanism so it can be fast and easy to program for the developer? One way to do this would be to modify the GCC and it would have to insert the assembly code to load the corresponding data segment into DS register before accesing each data segment, but that looks like a lot of work. Are there some other ways?
Continuing...
5. Shared memory. Lets say another process 'editor' needs to save some data through the 'filemanager' process. A shared memory mechanism must be provided by the OS. The 'editor' process will request the kernel to add the data segment of the 'filemanager' process. The 'filemanager' will confirm the request and the new segment will be added to editor's data segment list so it could legally write to it. The question again, is the same, how do I switch between multiple segments inside 'editor' the way it is transparent for the application and secure for the overall system?
Note: I will not use paging in my OS, all the memory addressing will be physical, and it will be in 64 mode.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您处于 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 次缓存未命中,而不是每次访问 1 次,因此如果没有 MMU,整个过程会快三倍——但这确实是一种边缘情况)。另一方面,MMU 使应用程序开发人员的生活更加轻松,特别是因为它允许每个应用程序拥有自己的标准化地址空间(例如,您可以提前编译和链接 C 代码)。
If you are in 64-bit mode than there is no segment register at all: the contents of CS, DS... are simply ignored. Addresses are 64-bit values in a "flat" address space. Segment registers are used in 16-bit and 32-bit modes, although most 32-bit OS set all segments to begin at address 0 and span over 4 GB, so that they may forget them forever. If you use segment registers (such that application code may use several "segments") then, at the C level, the C compiler must know about them, and a "pointer" will include both segment and offset value. Setting a segment register is expensive, so if you can do without segments, performance is better. Which explains why 32-bit OS like Linux or Windows do not use segments, and, correspondingly, CPU vendors have adapted and simply omitted segment support in 64-bit mode.
The real question is then: do you want memory protection between applications ? Memory protection is about blocking a process from reading or writing memory that it should not read/write, namely memory used by another process or the kernel. You can do without memory protected memory if application programmers never write code with bugs (mmh... how plausible is that ?) or if applications are written in a language which includes systematic checks which prohibit invalid memory accesses (e.g. Java)(the JNode project was about an OS in Java, but I do not know if it is still alive). To get memory protection, you use the MMU. The MMU works by "pages": the address space is split into fixed-size pages (4 kB each on x86) and the OS installs tables in RAM which tell the MMU where each page physically is. Paging, disk-based virtual memory... use the MMU. But you can also use the MMU "just" for memory protection (you set it up so that each page is either present at a virtual address which is equal to its physical address, or marked "absent" if access to that page is not currently allowed).
Not using the MMU at all can give a performance boost in some very specific situations (I know someone who was doing that for sieving, as part of a big cryptographic-oriented computational effort: sieving was almost 100% random-looking memory accesses in a 1 GB array; the MMU was implying three cache misses instead of one per access, so going without the MMU was making the whole thing three times faster -- but that's really an edge case). On the other hand, the MMU makes life easier for application developers, especially since it allows every application to have its own normalized address space (e.g. you can compile and link C code in advance).
从另一个角度来看,对于一个想要访问你的内存的 C 程序员来说,这会是什么样子呢? Kernel.AllocateMemory 返回什么?它不能返回 64 位值,因为它不足以同时表示段和偏移量(实际上,x64 机器现在访问的内存少于 2^64 字节,因此您可以将段号存储在某些未使用的位,但我认为您不想这样做)。
听起来您正在寻找的是一个“远指针”(来自过去的爆炸 - 请参阅例如 http://www.daniweb.com/software-development/c/threads/345287)。
如果您不想手动更改段,那么我不知道如何逃避支持远指针的编译器修改。请注意,如果您只有两个段,那么您可以使用 DS 和 ES 来寻址数据,但即使这也需要不同的汇编代码,具体取决于您要使用哪个段。必须有人决定生成此代码,这个人要么是你,要么是编译器。
Looking at it from another angle, how will this look to, say, a C programmer who wants to access your memory? What does Kernel.AllocateMemory return? It can't return a 64-bit value, because it is not enough to represent both a segment and an offset (in practice, x64 machines today access less than 2^64 bytes of memory, so you could store a segment number in some of the unused bits, but I don't think you want to do this).
It sounds like what you're looking for is a "far pointer" (blast from the past - see e.g. http://www.daniweb.com/software-development/c/threads/345287).
If you don't want to change segments manually, then I don't see how you can escape a compiler modification which will support a far pointer. Note that if you only have two segments, then you can address data using both DS and ES, but even this requires different assembly code depending on which segment you want to use. Someone will have to make the decision to generate this code, and it will be either you or the compiler.