有意义的是像操作系统这样的东西会用C编写。但它有多少,以及什么样的C?我的意思是,在C中,如果你需要一些堆内存,你会调用malloc。但是,操作系统甚至有堆?据我所知,malloc 向操作系统询问内存,然后将其添加到链表,二叉树或其他内容。调用堆栈怎么样?操作系统负责设置其他应用程序使用的所有东西,但它是如何做到的?如果要在C中打开或创建文件,相应的函数会向操作系统询问该文件。所以...那个电话的另一边有什么样的C?或者在内存分配的另一端?
另外,实际上用C编写了多少操作系统?所有的?架构相关代码怎么样?更高级别的抽象怎么样?是否用高级语言编写,比如C ++?
我的意思是,我只是出于纯粹的好奇而问这个问题。我现在正在下载最新的Linux内核,但这需要永远。我不确定我是否能够遵循这些代码 - 或者我是否会陷入一个我以前从未见过的不可避免的复杂网络中。
答案 0 :(得分:47)
非常好的问题。答案是:用于编写操作系统的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内核绝对值得关注,但必须说,对于任何人来说,从头开始阅读都是巨大且令人生畏的。
答案 1 :(得分:33)
什么样的C?
主要是ANSI C,很多时候都在查看它生成的机器代码。
但是,操作系统甚至有堆吗?
Malloc向操作系统询问指向允许使用的某些内存的指针。如果在OS(用户模式)上运行的程序试图访问它不拥有的内存,则会产生分段错误。允许操作系统直接访问系统上的所有物理内存,不需要malloc,不存在任何地址上的seg-fault。
调用堆栈怎么样?
调用堆栈实际上通常在硬件级别工作,带有链接寄存器。
对于文件访问,操作系统需要访问磁盘驱动程序,磁盘驱动程序需要知道如何读取磁盘上的文件系统(有很多种类)有时操作系统内置了一个,但我认为更常见的情况是,引导加载程序将其置于一个开始,并且它加载另一个(更大的)加载器。磁盘驱动程序可以访问物理磁盘的硬件IO,并从中构建。
答案 2 :(得分:24)
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 Boots的方式 - Click Here。
最好从一个更小的操作系统开始探索。请参阅此自行操作系统教程
答案 3 :(得分:5)
但它有多少,以及什么样的C?
某些部分必须用汇编语言编写
我的意思是,在C中,如果你需要一些堆内存,你可以调用malloc。但是,操作系统甚至有堆吗?据我所知,malloc向操作系统询问内存,然后将其添加到链表,二叉树或其他内容。
有些操作系统有堆。在最低级别,它们是被称为页面的内存块。然后,您的C库使用malloc以可变大小的方式使用自己的方案进行分区。您应该了解虚拟内存,这是现代操作系统中常见的内存方案。
如果要在C中打开或创建文件,相应的函数会向操作系统询问该文件。所以...那个电话的另一边有什么样的C?
您调用汇编例程,使用IN和OUT等指令查询硬件。对于原始内存访问,有时您会有专门用于与硬件进行通信的内存区域。这称为DMA。
我不确定我是否能够遵循这些代码 - 或者我是否会陷入一个我从未见过的不可避免的复杂网络中。
是的,你会的。你应该先拿起一本关于硬件和操作系统的书。
答案 4 :(得分:2)
我的意思是,在C中,如果你需要一些堆内存,你可以调用malloc。但是,操作系统甚至有堆吗?据我所知,malloc向操作系统询问内存,然后将其添加到链表,二叉树或其他内容。调用堆栈怎么样?
您在问题中说的很多内容实际上是由用户空间中的运行时库完成的。
操作系统需要做的就是将程序加载到内存中并跳转到它的入口点,之后的大部分细节都可以由用户空间程序完成。堆和堆栈只是进程虚拟内存的一部分。堆栈只是cpu中的指针寄存器。
分配物理内存是在操作系统级别完成的。操作系统通常会分配固定大小的页面,然后映射到用户空间进程。
答案 5 :(得分:2)
您应该阅读Linux Device Drivers 3。它很好地解释了linux内核的内部结构。
答案 6 :(得分:1)
我不会开始阅读Linux内核,对于初学者来说太复杂了。
Osdev是一个开始阅读的好地方。 我做了一个小小的操作系统,提供了来自Osdev的学校主题信息。它运行在vmware,bochs和qemu上,所以很容易测试它。 Here is the source code
答案 7 :(得分:0)
传统上,由于与硬件的交互,内核和设备驱动程序通常需要C语言。但是,C ++和Java等语言可用于entire operating system
有关更多信息,我发现Andrew Tannenbaum的操作系统设计和实现对于很多代码示例特别有用。
答案 8 :(得分:0)
malloc和内存管理函数不是C中的关键字。这是标准OS库的功能。我不知道这个标准的名称(它不太可能是POSIX标准 - 我没有发现任何提及),但它存在 - 你在大多数平台上的C应用程序中使用malloc。
如果你想知道Linux内核是如何工作的,我建议这本书http://oreilly.com/catalog/9780596005658/。我认为插入一些C代码是很好的解释:)。