我有几个假设,很可能其中一些是不正确的。请纠正我哪里错了。
我们可以将用C编写的程序中的函数分类如下:
发送到动态加载库的函数:
它们被发送到库中,库将它们转换为多个标准c函数
库将它们传递给libc,在那里它们被转换成多个系统调用。
libc将它们传递到内核,在那里执行它们,并将返回信息发送回libc。
libc将收集returs,按c-function对它们进行分组,并使用它们为每个c-function创建一个返回。这些返回将发送回动态加载的库。
此库将收集所有返回,并使用它们创建1个返回,发送回原始程序。
在代码中或部分静态编译库中定义的函数:所有内容都与上述类别相同,但:
它们的程序已经将它们转换成已知的标准c函数,或者在另一种情况下转换成调用动态加载库的函数。
标准的c函数被发送到libc,其他的被发送到动态加载的库(在那里它们将被如上所述处理)。
程序将根据两种类型函数的返回创建1个final return
标准的c函数:它们将被发送到libc,libc将以与上面相同的方式与内核通信,并向程序发送一个return
作为系统调用的函数:它们不是直接发送到内核,而是必须传递给libc,尽管它不做任何额外的工作。
安全检查(权限、写入未分配的mem,…)总是由内核完成,尽管libc和上面的库也可能首先检查它。
所有符合POSIX的系统都遵循以下规则

最佳答案

在linux和其他posix系统(比如freebsd)上可能不一样。
在Linux上,ABI定义了系统调用的方式。阅读Linux kernel interfaces。系统调用在syscalls(2)中列出(但也请参见/usr/include/asm*/unistd.h…)。同时阅读vdso(7)assembler HowTo解释了更多细节,但仅限于32位i686。
大多数linux libc都是免费软件,你可以研究它们的源代码。我知道musl-libc的源代码可读性很强。
为了简化一点,大多数系统调用(例如write(2))都是libc中的小c函数,它:
使用SYSENTER机器指令调用内核(并注意使用内核约定传递系统调用号及其参数,这不是通常的c abi)。内核认为的系统调用只是机器指令(以及有关它的约定)。
处理失败案例,将其传递给errno(3)并返回-1
(iirc,如果失败,则在内核从SYSENTER返回时设置进位标志位,或者可能设置溢出标志位;但我可能在细节上出错)
通过返回结果来处理成功案例。
您可以使用一些汇编程序代码调用不带libc的系统调用。这是不寻常的,但已经完成(例如在BusyBoxBones中)。
因此libcwrite代码正在做一些微小的额外工作(传递参数、处理失败和成功案例)。
由于vDSO,一些系统调用(可能errno&getpid)避免了clock_gettime机器指令(以及用户模式->内核模式开关)的开销。

关于c - 系统调用是否直接发送到内核?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48767018/

10-16 19:03