系统调用和函数调用有什么区别?fopen() 是系统调用还是函数调用?
10 回答
系统调用是对内核代码的调用,通常通过执行中断来执行。中断导致内核接管并执行请求的操作,然后将控制权交还给应用程序。这种模式切换是系统调用比等效的应用程序级函数执行更慢的原因。
fopen
是 C 库中的一个函数,它在内部执行一个或多个系统调用。通常,作为 C 程序员,您很少需要使用系统调用,因为 C 库为您包装了它们。
fopen 是一个函数调用。
系统调用与管理资源的底层操作系统交互。它的数量级比函数调用更昂贵,因为必须采取许多步骤来保留进行系统调用的进程的状态。
在 *nix 系统上, fopen 包装 open,这会进行系统调用(open 是 C - 系统调用的包装器)。fread /read、fwrite / write 等也会发生同样的情况。
这里有一个对 unix 系统调用执行的任务的很好的描述。
实际上,系统调用与函数调用无关。这两种机制的唯一共同点是它们都向调用者提供服务。
从线程执行来看系统调用:
系统调用是应用模式程序请求下划线操作系统提供的服务的功能。系统调用会将运行中的线程从用户态带入内核态,执行系统调用处理函数,然后返回用户态。
系统调用参数:
系统调用的参数是(系统调用号,参数...)。参数的含义和格式取决于系统调用号。
从提供给用户态程序的系统调用库来看:
用户态程序通常调用glibc的库来调用系统调用。例如 glibc 中的 open() 函数:
- 将系统调用号 SYS_OPEN 放入 eax 寄存器
- 通过调用软件中断或 sys_enter 指令请求系统调用
If you're using Linux you can monitor system calls performed by an application via strace:
strace /path/to/app
Its output might give you a good insight on what's going on within libc, and which functions are actually system calls.
这个问题已经有了很好的答案,但我想我可以添加一些东西(来自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()
or )的调用read()
看起来与 C 中的典型过程调用一模一样;也就是说,如果它看起来就像一个过程调用,那么系统如何知道它是一个系统调用,并且做所有正确的事情?原因很简单:它是一个过程调用,但隐藏在这个过程调用内部的是著名的陷阱指令。更具体地说,当您调用open()
(例如)时,您正在执行对 C 库的过程调用。其中,无论是对于其他系统调用open()
还是任何其他系统调用提供,库使用与内核商定的调用约定将要打开的参数放在众所周知的位置(例如,在堆栈上,或在特定的寄存器中),将系统调用号放入众所周知的位置以及(再次,到堆栈或寄存器),然后执行上述陷阱指令。陷阱后库中的代码解包返回值并将控制权返回给发出系统调用的程序。因此,C 库中进行系统调用的部分是在汇编中手工编码的,因为它们需要仔细遵循约定才能正确处理参数和返回值,以及执行特定于硬件的陷阱指令。现在您知道为什么您个人不必编写汇编代码即可陷入操作系统;有人已经为您编写了该程序集。
系统调用实际上调用了由内核空间执行的 API。承担所有相关成本(参见 Wiki,或此链接了解详细信息)
函数调用是对用户空间中的一段代码的调用。
但是,请注意,函数调用可能是针对在其执行过程中执行系统调用的函数——“fopen”就是这样的例子之一。因此,虽然对 fopen 本身的调用是对函数的调用,但这并不意味着系统调用不会发生处理实际的 IO。
添加到这个讨论中的一个观点是,通常在最乐观的情况下,函数调用在 x86 中具有一些 8 位指令(平均 4-10 条)的开销。
系统调用具有以下属性。
- 它执行更多的指令,它必须冻结一个进程而不是简单的堆栈状态。
- 所涉及的时间大多是不确定的。
- 它通常是一个调度点,调度器可能会选择重新调度。
由于这三个基本原因(可能还有更多),应该尽可能减少系统调用的数量——例如,网络系统软件保留套接字句柄(以及连接使用的其他应用程序特定的内部数据结构)以分配给新的连接,为什么要打扰内核?
请记住,软件的构建就像一个倒置的金字塔。系统调用是基础。
Just to complete the picture presented by the others, fopen
is commonly implemented as a wrapper around open
, which is also a user-accessible function. fopen
is, in a sense, higher-level than open
since the FILE*
structure it returns encapsulates stuff for the user. Some users use open
directly for special needs. Therefore it wouldn't be right to call fopen
a "system call" in any way. Nor does it execute system calls directly, since open
is also a function callable by the user.
fopen
是一个函数调用,但它有时可能被称为系统调用,因为它最终由“系统”(操作系统)处理。 fopen
内置于C 运行时库中。
系统调用在内核级别执行,而不是在用户 spce 中执行,因为它需要一些特权才能访问硬件。
因此,当在用户空间编程并像fopen
在 C 语言中进行一些普通函数调用时,libc 通常将此函数包装到特定代码代码中,在该代码代码中生成中断以从用户空间切换到内核空间,然后在内核空间中执行所需的系统调用。硬件级别的函数调用的功能将在内核空间中执行。