操作系统是用啥样的 C 语言编写的?
Posted
技术标签:
【中文标题】操作系统是用啥样的 C 语言编写的?【英文标题】:What kind of C is an operating system written in?操作系统是用什么样的 C 语言编写的? 【发布时间】:2010-11-08 23:04:46 【问题描述】:像操作系统这样的东西会用 C 语言编写是有道理的。但是其中有多少,以及什么样的 C 语言?我的意思是,在 C 中,如果你需要一些堆内存,你会调用 malloc。但是,操作系统甚至 有 堆吗?据我所知,malloc 向操作系统请求内存,然后将其添加到链表、二叉树或其他东西中。调用堆栈呢?操作系统负责设置其他应用程序使用的所有这些stuff,但它是如何做到的呢?当您想在 C 中打开或创建文件时,相应的函数向操作系统询问该文件。所以......那个电话的另一边是什么样的C?还是在内存分配的另一端?
另外,实际上有多少操作系统是用 C 编写的?所有的?依赖于架构的代码呢?更高层次的抽象又如何——用更高级的语言(如 C++)编写过吗?
我的意思是,我只是出于好奇而问这个问题。我现在正在下载最新的 linux 内核,但它需要永远。我不确定我是否最终能够遵循代码 - 或者我是否会陷入我从未见过的无法避免的复杂网络中。
【问题讨论】:
也许这很有趣:http://***.com/questions/43180/how-to-get-started-in-operating-system-development 投票结束时过于宽泛或基于意见。如果它是“OS XXX 是用什么写的”,那将是可以回答的。对于任何操作系统,任何带有asm()
的编译语言都可以使用。
“6 年前活跃”:/
【参考方案1】:
很好的问题,所有。答案是:用于编写操作系统的 C 语言“方言”中几乎没有标准 C 库。例如,在 Linux 内核中,标准的内存分配函数 malloc、nmalloc、free 等被特殊的内核内部内存分配函数 kmalloc 和 kfree 所取代,它们的使用有特殊的限制。操作系统必须提供自己的“堆”——在 Linux 内核中,分配给内核使用的物理内存页面必须是不可分页的,并且通常在物理上是连续的。见This linux journal article on kmalloc and kfree。同样,操作系统内核维护自己的特殊调用堆栈,其使用需要来自内存的 GCC 编译器的特殊支持。
另外,实际上有多少操作系统是用 C 编写的?所有的 是吗?
据我所知,操作系统绝大多数是用 C 编写的。一些特定于体系结构的功能是用汇编程序编写的,但通常很少提高可移植性和可维护性:Linux 内核有一些汇编程序,但试图将其最小化越多越好。
架构依赖呢? 代码?更高级别的呢? 抽象——有没有得到 用高级语言编写, 喜欢 C++?
通常内核是用纯 C 编写的,但有时更高级别的框架和 API 是用更高级的语言编写的。例如,MacOS 上的 Cocoa 框架/API 是用 Objective C 编写的,而 BeOS 更高级别的 API 是用 C++ 编写的。 Microsoft 的大部分 .NET 框架都是用 C# 编写的,其中“公共语言运行时”是用 C++ 和汇编程序混合编写的。 Linux 上最常用的 QT 小部件集是用 C++ 编写的。当然,这引入了关于什么是“操作系统”的哲学问题。
Linux 内核在这方面绝对值得一看,但必须说,它对于任何人从头开始阅读来说都是巨大且令人生畏的。
【讨论】:
当您说内核通常是用 C 编写时,这里省略了一个关键的操作系统,即 Windows,它是 C 和 C++ 的混合内核;执行 (NT) 部分是用 C 编写的,但内核的 Win32 部分(主要位于 win32k.sys 中,负责字体、窗口、图形、directX 等)主要是用 C++ 编写的。【参考方案2】:什么样的C?
主要是 ANSI C,花大量时间查看它生成的机器代码。
但是,操作系统甚至有堆吗?
Malloc 向操作系统请求一个指向它允许使用的内存的指针。如果在操作系统(用户模式)上运行的程序试图访问它不拥有的内存,它将给出分段错误。允许操作系统直接访问系统上的所有物理内存,不需要 malloc,任何存在的地址都没有 seg-faults。
调用堆栈呢?
调用堆栈实际上通常在硬件级别使用链接寄存器。
对于文件访问,操作系统需要访问磁盘驱动程序,它需要知道如何读取磁盘上的文件系统(有很多不同的类型)有时操作系统内置了一个,但我认为更常见的是,引导加载程序将其交给一个开始,然后加载另一个(更大的)一个。磁盘驱动程序可以访问物理磁盘的硬件 IO,并以此为基础进行构建。
【讨论】:
【参考方案3】:C 是一种非常低级的语言,你可以直接做很多事情。任何 C 库方法(如 malloc、printf、crlscr 等)都需要首先实现,才能从 C 中调用它们(例如,看看 libc 概念)。下面我举个例子。
让我们看看 C 库方法是如何在底层实现的。我们将使用 clrscr 示例。当您实现这些方法时,您将直接访问系统设备。例如,对于 clrscr(清屏),我们知道显存位于 0xB8000。因此,要写入屏幕或清除屏幕,我们首先要分配一个指向该位置的指针。
在 video.c 中
void clrscr()
unsigned char *vidmem = (unsigned char *)0xB8000;
const long size = 80*25;
long loop;
for (loop=0; loop<size; loop++)
*vidmem++ = 0;
*vidmem++ = 0xF;
现在让我们编写我们的迷你内核。当控制权从引导加载程序移交给我们的“内核”时,这将清除屏幕。在 main.c
void main()
clrscr();
for(;;);
要编译我们的“内核”,您可以使用 gcc 将其编译为纯 bin 格式。
gcc -ffreestanding -c main.c -o main.o
gcc -c video.c -o video.o
ld -e _main -Ttext 0x1000 -o kernel.o main.o video.o
ld -i -e _main -Ttext 0x1000 -o kernel.o main.o video.o
objcopy -R .note -R .comment -S -O binary kernel.o kernel.bin
如果您注意到上面的 ld 参数,您会看到我们将内核的默认加载位置指定为 0x1000。现在,您需要创建一个引导加载程序。从您的引导加载程序逻辑中,您可能希望将控制权传递给您的内核,例如
jump 08h:01000h
您通常在 Asm 中编写引导加载程序逻辑。甚至在此之前,您可能需要看看 PC 是如何启动的 - Click Here。
最好从更小的操作系统开始探索。请参阅此 Roll Your Own OS 教程
http://www.acm.uiuc.edu/sigops/roll_your_own/
【讨论】:
【参考方案4】:但是有多少,什么类型的 C?
有些部分必须用汇编语言编写
我的意思是,在 C 中,如果你需要一些堆内存,你会调用 malloc。但是,操作系统甚至有堆吗?据我所知,malloc 向操作系统请求内存,然后将其添加到链表、二叉树等。
一些操作系统有一个堆。在最低级别,它们是被分配的称为页面的内存板。然后,您的 C 库使用 malloc 以可变大小的方式使用自己的方案进行分区。您应该了解虚拟内存,这是现代操作系统中常见的内存方案。
当您想在 C 中打开或创建文件时,相应的函数会向操作系统询问该文件。所以... 调用的另一端是什么类型的 C?
您调用汇编例程,使用 IN 和 OUT 等指令查询硬件。通过原始内存访问,有时您会拥有专门用于与硬件进行通信的内存区域。这称为 DMA。
我不确定我是否最终能够遵循代码 - 或者我是否会陷入我从未见过的无法避免的复杂网络中。
是的,你会的。你应该先买一本关于硬件和操作系统的书。
【讨论】:
【参考方案5】:我的意思是,在 C 语言中,如果你需要一些堆内存,你会调用 malloc。但是,操作系统甚至有堆吗?据我所知,malloc 向操作系统请求内存,然后将其添加到链表、二叉树或其他东西中。调用堆栈呢?
您在问题中所说的很多内容实际上是由用户空间中的运行时库完成的。
操作系统需要做的就是将程序加载到内存中并跳转到它的入口点,之后的大部分细节都可以由用户空间程序完成。堆和堆栈只是进程虚拟内存的区域。 Stack只是cpu中的一个指针寄存器。
分配物理内存是在操作系统级别完成的。操作系统通常分配固定大小的页面,然后映射到用户空间进程。
【讨论】:
【参考方案6】:您应该阅读Linux Device Drivers 3。它很好地解释了 linux 内核的内部结构。
【讨论】:
【参考方案7】:我不会开始阅读 Linux 内核,对于初学者来说太复杂了。
Osdev 是开始阅读的好地方。 我用来自 Osdev 的信息为学校科目做了一点操作系统。它在 vmware、bochs 和 qemu 上运行,因此很容易对其进行测试。 Here is the source code.
【讨论】:
stanford 的 PintOs 也是一个很好的起点...(替换 nachos)【参考方案8】:传统上,由于与硬件的交互,内核和设备驱动程序大多需要 C。但是,entire operating system 可以使用 C++ 和 Java 等语言
有关更多信息,我发现 Andrew Tannenbaum 的操作系统设计和实现对大量代码示例特别有用。
【讨论】:
解释你将如何编译没有内存地址概念的java来使用硬件设备?您如何将其编译为机器代码,而无需中间步骤将某些内容编译为本地语言? Java 绝对可以用于用户级 API ——但是,由于各种原因,包括缺乏像“原始”内存指针这样的低级结构及其垃圾收集性质,它使得实现最低级别 Java 虚拟机的糟糕选择。有趣的是,Sun 的 JVM 是用 C++ 编写的。据说maxine.dev.java.net/design.html maxine 项目使用纯 java 和一些汇编程序,并建议现在可以使用 Java 5 的功能进行系统编程,但我怀疑需要一些扩展和hackery。 Java 是一个完整的生态系统。当人们谈论用 Java 编写操作系统时,他们只是指语言语法,而不是所有其他部分。没有 JVM,没有类加载器,既没有字节码解释器,也没有 JIT 编译器,没有可供操作系统使用的标准 Java 类库。相反,Java 源代码必须由主机系统编译为机器代码,并且主机编译器将负责诸如将某些变量强制到某些地址之类的事情。将整个操作系统编译为机器代码还是仅编译为引导加载程序、字节码解释器和硬件抽象,这会有所不同。【参考方案9】:malloc 和内存管理函数不是 C 中的关键字。这是标准 OS 库的函数。我不知道这个标准的名称(它不太可能是 POSIX 标准 - 我没有找到任何提及),但它存在 - 您在大多数平台上的 C 应用程序中使用 malloc。
如果您想了解 Linux 内核的工作原理,我建议您阅读这本书 http://oreilly.com/catalog/9780596005658/。我认为插入一些 C 代码是很好的解释:)。
【讨论】:
malloc 是 ANSI C 标准库的一部分,需要作为 C 运行时库的一部分发布。大多数操作系统选择放弃整个 CRL 并构建自己的,因为没有“背后”来分配内存。在 Linux 中,堆分配函数是 kalloc。在 Windows 中,它是 ExAllocatePool。以上是关于操作系统是用啥样的 C 语言编写的?的主要内容,如果未能解决你的问题,请参考以下文章
C语言的printf函数在windows平台的系统调用过程是啥样的?