系统调用与函数调用

系统调用和函数调用有什么区别? fopen()是系统调用还是函数调用?

系统调用是对内核代码的调用,通常通过执行中断来执行。 中断导致内核接管并执行请求的操作,然后将控制权交还给应用程序。 此模式切换是系统调用执行速度比等效应用程序级函数慢的原因。

fopen是C库中的一个函数,它在内部执行一个或多个系统调用。 通常,作为C程序员,您很少需要使用系统调用,因为C库会为您包装它们。

fopen是一个函数调用。

系统调用与底层操作系统交互,后者管理资源。 它的数量级比函数调用更昂贵,因为必须采取许多步骤来保持进行系统调用的进程的状态。

在* nix系统上,fopen打开,这使得系统调用(open是系统调用的C – 包装器)。 fread / read,fwrite / write等也会发生同样的情况。

这里有一个很好的描述unix系统调用执行的任务。

实际上,系统调用与函数调用无关。 这两种机制中唯一常见的是它们都为呼叫者提供服务。

  • 从线程执行的角度来看系统调用:

    系统调用是应用程序模式程序的function,用于请求由下划线OS提供的服务。 系统调用将运行线程从用户模式带入内核模式,执行系统调用处理函数,然后返回用户模式。

  • 系统调用参数:

    系统调用的参数是(系统调用号,参数…)。 params的含义和格式取决于系统调用号。

  • 从提供给用户程序的系统调用库的视图:

    用户模式程序通常调用glibc的库来调用系统调用。 例如,glibc中的open()函数:

    1. 将系统调用号SYS_OPEN放入eax寄存器
    2. 通过调用软件中断或sys_enter指令请求系统调用

如果您使用的是Linux,则可以监视应用程序执行的系统调用:

strace appname …

它的输出可以让你很好地了解libc中发生了什么,以及哪些函数实际上是系统调用。

系统调用实际上调用了由内核空间执行的API。 假设所有相关成本(请参阅Wiki或此链接以获取详细信息)

函数调用是对用户空间中的一段代码的调用。

但请注意,一个函数调用可能是一个函数,它在执行过程中会进行系统调用 – “fopen”就是其中一个例子。 因此,虽然对fopen本身的调用是对函数的调用,但并不意味着系统调用不会碰巧处理实际的IO。

添加到这个讨论中的一个观点是,在最乐观的情况下,函数调用在x86中具有几个8位指令(平均4-10个)的开销。

系统调用具有以下属性。

  1. 它执行更多的指令,它必须冻结进程而不是简单的堆栈状态。
  2. 所涉及的时间主要是非确定性的。
  3. 它通常是一个调度点,调度程序可能会选择重新安排。

由于这三个原始原因(可能还有更多),应尽可能减少系统调用量 – 例如,联网系统软件保持套接字句柄(以及连接使用的其他应用程序特定的内部数据结构)以分配给新的连接,为什么要打扰内核?

请记住,软件的构建就像一个颠倒的金字塔。 系统调用是基础。

fopen是一个函数调用,但它有时可能被称为系统调用,因为它最终由“系统”(OS)处理。 fopen内置于C运行时库中 。

只是为了完成其他人提供的图片, fopen 通常被实现为open的包装器,它也是一个用户可访问的function。 从某种意义上说, fopenopen更高级,因为它返回的FILE*结构为用户封装了东西。 有些用户直接使用open来满足特殊需求。 因此,以任何方式将fopen称为“系统调用”是不对的。 它也不直接执行系统调用,因为open也是用户可调用的函数。

这个问题已经有了很好的答案,但我认为我可以添加一些内容(来自ostep的一个部分尚未出现在其他答案中)

有时系统调用和函数调用具有相同的签名,例如open()

open()系统调用

 --- ~/Documents » man open(2) OPEN(2) Linux Programmer's Manual OPEN(2) int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); ... 

open()函数调用

 $ man open(3) --- ~/Documents » OPEN(3P) POSIX Programmer's Manual OPEN(3P) ... int open(const char *path, int oflag, ...); ... 

引用OSTEP表格

您可能想知道为什么对系统调用的调用 (例如open()read()看起来与C中的典型过程调用完全相同; 也就是说,如果它看起来就像一个过程调用 ,系统如何知道它是一个系统调用 ,并做了所有正确的事情? 原因很简单:它是一个过程调用 ,但隐藏在过程调用内部的是着名的陷阱指令 。 更具体地说,当您调用open() (例如)时,您正在执行对C库的过程调用 。 其中,无论是对于open()还是提供的任何其他系统调用 ,库都使用与内核达成一致的调用约定来将参数置于众所周知的位置(例如, 堆栈或特定寄存器中) ),将系统调用号放入一个众所周知的位置(再次,放到堆栈寄存器中 ),然后执行上述陷阱指令陷阱解压缩返回值后,库中的代码将控制权返回给发出系统调用的程序。 因此,进行系统调用的C库部分是在汇编中手工编码的,因为它们需要仔细遵循约定,以便正确处理参数和返回值,以及执行特定于硬件的陷阱指令 。 现在你知道为什么你个人不必编写汇编代码来陷入操作系统; 有人已经为你写了那个集会。

系统调用在kernet级别执行,而不是在用户spce中执行,因为它需要一些权限才能访问硬件。

因此,当在用户空间中进行编程并在C语言中进行一些普通的函数调用时,libc通常将此函数包装到特定的代码中,在该代码中生成中断以从用户空间切换到内核空间,然后在内核空间中将所需的系统调用在硬件级别执行函数调用的function将在内核空间中执行。