为什么 malloc() 被认为是库调用而不是系统调用?

Why is malloc() considered a library call and not a system call?

为什么 malloc() 被认为是标准 C 库函数而不是系统调用?似乎 OS 负责处理所有内存分配请求。

Why is malloc() considered a standard C library function and not a system call?

因为它是 C 标准库的一部分。

It seems like the OS is responsible for handling all memory allocation requests.

不是。操作系统通常会为给定的进程分配一些内存 space,但之后如何使用内存取决于进程。将标准库用于诸如内存分配之类的事情可以将您的代码与任何给定操作系统的细节隔离开来,这使您的代码更具可移植性。 malloc 的给定实现可能最终会进行系统调用以获取内存,但它是否执行或有时是否执行是一个实现细节。

It seems like the OS is responsible for handling all memory allocation requests.

好吧,是和否

它实际上更多地取决于您的特定系统而不是 C。

大多数 OS 在一定大小的主干中分配内存。通常称为页面。页面大小可能不同。并且在特定系统上可能有几个受支持 page-sizes。 4K 是许多系统上的典型 page-size,但可能支持更大的大页面。

但是是的...归根结底只有一个实体可以分配内存。 OS。除非你在 bare-metal 其他代码可以处理它的地方 - 如果甚至支持的话。

Why is malloc() considered a standard C library function and not a system call?

简短的回答是:因为 malloc 不是 OS/systemcall。期间.

再详细一点。一个 malloc 调用可能会导致系统调用,但下一个 malloc 可能不会。

例如:您使用 malloc 请求 100 个字节。 malloc 可能决定调用 OS。 OS 给你 4K。在您的下一个 malloc 中,您请求 500 字节。然后“介于两者之间的层”可以从先前系统调用已经提供的主干中提供 500 个字节。

所以不会...通过 malloc 进行内存分配可能不会导致任何系统调用以分配更多内存。

这完全取决于您的特定系统。而 C 标准并不关心。

但是 malloc 不是系统调用。 malloc 在需要时使用其他系统调用。

It seems like the OS is responsible for handling all memory allocation requests.

出于性能原因,每次程序需要内存时都向 OS 请求内存并不是一个好主意。这有几个原因:

  1. OS以页面为单位管理内存。页面通常有 4096 字节长。 (但某些体系结构或操作系统使用 larger 页。)OS 无法将内存分配给小于页的块中的进程。

    假设您需要 10 个字节来存储一个字符串。分配 4096 字节并且只使用前 10 个字节是非常浪费的。内存分配器可以向 OS 请求一个页面,然后将该页面分成更小的分配。

  2. 系统调用需要上下文切换。相对于在同一程序中调用一个函数,上下文切换是昂贵的(在 x86 系统上约为 100 ns)。同样,最好请求更大的内存块,并且 re-use 它用于许多分配。

Why is malloc() considered a library call and not a system call?

对于一些库调用,比如read(),库中的实现非常简单:调用同名的系统调用。对库函数 read() 的一次调用会产生对 read() 的一次系统调用。将read()描述为系统调用是合理的,因为所有的工作都在内核中完成。

malloc()的故事更复杂。没有名为 malloc() 的系统调用,库调用 malloc() 实际上将使用系统调用 sbrk()brk()mmap(),具体取决于您的分配和您正在使用的实现。很多时候,它根本不进行系统调用!

如何实现有很多不同的选择malloc()。因此,您会看到许多不同的竞争实现,例如 jemalloc, or tcmalloc.

可能mallocfree 实现为系统调用,但很少有人这样做。

系统调用是对 OS 内核的调用。例如,在 POSIX 系统(Linux、UNIX、...)上,readwrite 是系统调用。当 C 程序调用 read 时,它可能正在调用一个包装器,该包装器执行向内核发出请求所需的一切,然后 return 将结果返回给调用者。

事实证明,最有效的内存管理方法是使用lower-level系统调用(参见brksbrk)扩展当前进程的数据段,并且然后使用库调用(mallocfree 等)来管理该段内的内存。该管理不需要与内核进行任何交互;这只是在当前进程中执行的指针操作。如果 malloc 函数需要比当前可用内存更多的内存,它将调用 brksbrk 等系统调用,但许多 malloc 调用不需要与根本没有内核。

以上是 Linux/POSIX/UNIX 系统所特有的。例如,Windows 的细节会有所不同,但整体设计可能相似。

请注意,某些 C 标准库函数 通常直接作为系统调用实现。 time 是一个示例(但正如 Nick ODell 在评论中指出的那样,通常可以在不与内核交互的情况下执行 time 调用)。