我所说的系统调用是指诸如 之类的函数man 2 brk
,而不是0x80
中断。
如果我明白的话这个线程正确的是,编译后的 C 程序永远不会直接地调用系统调用。它只能调用库调用,这些调用可能是从glibc
.
然而,man 3 brk
回报No manual entry for brk in section 3
。所以我想必须发生以下情况之一才能brk
正确执行:
- 我上面的理解是错误的。程序无需支持即可调用系统调用
glibc
。但如何brk
链接到程序中呢? - 确实有一个
glibc
系统调用的包装器brk
。那么brk
我的时候包含哪些呢#include <unistd.h>
?是一个glibc
还是系统调用一个?如果是glibc
这样,为什么没有记录在案man 3
?在哪里可以找到可用库调用的完整列表?
答案1
对于第 2 节中的手册页中的大多数系统调用,手册页实际上描述了 C 库包装器。通常会明确提及例外情况,例如gettid
@Sergei Kurenkov 在他们的回答中提到:
笔记Glibc 不提供此系统调用的包装器;使用 syscall(2) 调用它。
pivot_root
与(对于一般应用程序没有多大用处)类似, tgkill
(执行 的低级功能pthread_kill
)。然后还有readdir
,其中实际的系统调用与库函数有些不同:
描述这不是您感兴趣的函数。请查看 readdir(3) 以了解符合 POSIX 的 C 库接口。本页记录了裸内核系统调用接口,该接口已被 getdents(2) 取代。
请注意,必须有某种包装器。函数调用是使用 C 调用约定进行的,这与内核接口的调用约定不同。通常的函数调用是使用call
汇编指令(或类似指令)进行的,内核调用是使用syscall
or int 0x80
(这不包括gettimeofday
或getpid
中的内容vdso
)。编译器不(不需要)知道哪些函数调用映射到实际的内核调用。
即使使用“通常的”系统调用,C 库包装器的行为也与裸系统调用略有不同:系统调用将错误代码返回为不同的负值(如果您查看 Linux 内核代码,您会看到很多返回类似return -EPERM;
)。 C 库包装器将所有此类返回值转换为 -1,并将实际错误代码移至errno
。
答案2
编译后的 C 程序从不直接调用系统调用。
这不是真的。以agettid
为例:http://man7.org/linux/man-pages/man2/gettid.2.html。它没有包装器,因此您需要在程序中编写类似的内容(来自 man: Glibc does not provide a wrapper for this system call; call it using syscall(2).
):
#ifndef WIN32
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <unistd.h>
int thread_gettid(void) {
return static_cast<int>(syscall(SYS_gettid));
}
#else
int thread_gettid(void) {
return GetCurrentThreadId();
}
#endif
我上面的理解是错误的。程序可以在没有 glibc 支持的情况下调用系统调用。
确实可以。它用syscall
:http://man7.org/linux/man-pages/man2/syscall.2.html。
那么brk是如何链接到程序中的呢?
看来brk
glibc 确实有一个包装器,因为 man 中有这一行:
The return value described above for brk() is the behavior provided
by the glibc wrapper function for the Linux brk() system call.
如果是 glibc 的话,为什么 man 3 中没有记录呢
我认为这是因为必须malloc
使用内存分配:
避免使用 brk() 和 sbrk():malloc(3) 内存分配包是可移植且舒适的内存分配方式。