我有几个假设,很可能其中一些是不正确的。请纠正我的错误。
我们可以将用C编写的程序中的函数分类如下:
发送到动态加载库的函数:
在代码中定义的函数或静态编译库的一部分:所有内容与上面的类别相同,但是:
标准C函数的函数:它们将被发送到libc,它将以与上面相同的方式与内核通信,并且1将返回到程序
系统调用的函数:它们不会直接发送到内核,但必须传递给libc,尽管它不会做任何额外的工作。
安全检查(权限,写入未分配的内存,...)总是由内核完成,尽管上面的libc和库也可能先检查它。
所有符合POSIX标准的系统都遵循这些规则
答案 0 :(得分:2)
在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
的系统调用。这是不寻常的,但已经完成(例如在BusyBox或Bones中)。
因此libc
的{{1}}代码正在做一些额外的工作(传递参数,处理失败& write
和成功案例)。
一些系统调用(可能是errno
& getpid
)避免了clock_gettime
机器指令(以及用户模式 - >内核模式切换)的开销,这要归功于{ {3}}
答案 1 :(得分:1)
不,你不能对这样的事情进行分类。当你用C语言编程时(但几乎所有其他语言都没有区别),只有函数,无论这些函数的实际状态是什么,你都可以用完全相同的方式调用它们。这由ABI(如何传递参数,获取返回值等)定义并由编译器/链接器强制执行。当然,一些功能只是存根。例如,存根到共享库函数(存根可能需要加载库,动态链接到实际函数等)或系统调用(这更具技术性,因内核而异)。但是从你的程序的角度看,一切都是一样的(这就是为什么在开始时很难理解fread
和read
之间的区别:你用同样的方式称它们,它们几乎完全相同的工作,有什么区别?)。
POSIX并没有说出关于内核的单一内容......它只是用最少的语义(加上一些命令,工具等)列出了一组函数的C(和以前的ADA)API。这些的实施完全免费。