tar*_*nom 6 c function libc system-calls
C 程序中可以进行系统调用吗?考虑一下:
int main()
{
int f = open("/tmp/test.txt", O_CREAT | O_RDWR, 0666);
write(f, "hello world", 11);
close(f);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在此示例代码中,open、write、 和close是库函数。在我的搜索过程中,我得出的结论是它们是函数而不是系统调用。这些函数(open、write和close)中的每一个都会进行系统调用。
问题
write和系统调用,而如果我们使用不同的选项编译它,则它会调用库函数?read根据维基百科,系统调用是一种“计算机程序向其执行所在的操作系统内核请求服务的编程方式”。
理解系统调用的另一种方式是,用户空间程序向操作系统内核发出请求,以代表用户空间程序执行某些任务。内核提供的全套系统调用类似于(在某些方面)内核向用户空间提供的 API。
由于系统调用是内核的低级接口,因此正确提供其参数可能容易出错甚至危险。由于这些原因,C 库作者为内核系统调用集的很大一部分提供了更简单、更安全的包装函数。
这些包装函数采用简化的参数集,然后导出适当的值以传递给内核,以便可以执行系统调用。
注意:此示例基于gcc在 Linux 上编译和运行 C 程序。系统调用、库函数和输出在其他 POSIX 或非 POSIX 操作系统上可能有所不同。
我将尝试通过一个简单的示例来展示如何查看何时进行系统调用。
#include <stdio.h>
int main() {
write(1, "Hello world!\n", 13);
}
Run Code Online (Sandbox Code Playgroud)
上面我们有一个非常简单的 C 程序,它将字符串Hello world!\n写入stdout. 如果我们编译然后使用 执行该程序strace,我们会看到以下内容(请注意,输出在其他计算机上可能看起来不同):
$ strace ./hello > /dev/null
execve("./hello", ["./hello"], 0x7fff083a0630 /* 58 vars */) = 0
<a bunch of output we aren't interested in>
write(1, "Hello world!\n", 13) = 13
exit_group(0) = ?
+++ exited with 0 +++
Run Code Online (Sandbox Code Playgroud)
strace是一个 Linux 程序,它拦截并显示程序发出的所有系统调用,以及提供给系统调用的参数及其返回值。
我们可以在这里看到,正如预期的那样,write系统调用是使用预期的参数进行的。还没有什么奇怪的。
另一个 Linux 跟踪程序是ltrace,它拦截程序进行的动态库调用,并显示它们的参数和返回值。
如果我们使用 运行相同的程序ltrace,我们会看到:
$ ltrace ./hello > /dev/null
write(1, "Hello world!\n", 13) = 13
+++ exited (status 0) +++
Run Code Online (Sandbox Code Playgroud)
这告诉我们write库函数已被执行。这意味着 C 代码首先调用write库函数,然后库函数又调用write系统调用。
现在假设我们想要显式地进行write系统调用而不调用write库函数。(这在正常使用中是不可取的,但对于说明很有用。)
这是新代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main() {
syscall(SYS_write, 1, "Hello world!\n", 13);
}
Run Code Online (Sandbox Code Playgroud)
这里我们直接调用syscall库函数,告诉它我们要执行write系统调用。
重新编译后,输出如下strace:
$ strace ./hello > /dev/null
execve("./hello", ["./hello"], 0x7ffe3790a660 /* 58 vars */) = 0
<a bunch of output we aren't interested in>
write(1, "Hello world!\n", 13) = 13
exit_group(0) = ?
+++ exited with 0 +++
Run Code Online (Sandbox Code Playgroud)
我们可以看到write系统调用按照预期进行了。
如果我们运行ltrace我们会看到以下内容:
$ ltrace ./hello > /dev/null
syscall(1, 1, 0x560b30e4d704, 13) = 13
+++ exited (status 0) +++
Run Code Online (Sandbox Code Playgroud)
所以write库函数不再被调用,但我们仍在进行库函数调用。现在我们正在调用syscall库函数而不是write库函数。
可能有一种方法可以直接从用户空间C程序进行系统调用,而不需要调用任何库函数,如果有一种方法我相信那将是非常先进的。
一般来说,几乎每个重要的 C 程序都至少进行一次系统调用。这是因为用户空间无法直接访问内核内存或计算机硬件。用户空间程序可以通过系统调用间接访问内核内存和硬件。
要识别已编译的 C 程序(或 Linux 上的任何其他程序)是否进行系统调用,以及识别它进行了哪些系统调用,只需使用strace.
您可以使用该选项编译您的 C 程序(假设您正在使用gcc)-nostdlib。这将阻止在生成可执行文件时链接 C 标准库。但是,您需要编写自己的代码来进行系统调用。
| 归档时间: |
|
| 查看次数: |
2196 次 |
| 最近记录: |