Linux 系统调用与 C 库函数
Linux system calls vs C lib functions
我对这两个问题有点困惑,所以这是我的问题;
Linux 手册页项目列出了所有这些函数:
https://www.kernel.org/doc/man-pages/
以 recvfrom
为例,此函数既作为 Linux 系统调用也作为 C 库函数存在。他们的文档看起来不同,但都可以使用 #include <sys/socket.h>
访问。
我不明白他们的区别?
我还认为系统调用是使用十六进制值定义的,可以直接在汇编中实现,它们的列表在这里:
https://syscalls.kernelgrok.com/
但是我在上面的 link 中找不到 recvfrom
。在这一点上,我对 Linux 系统调用与 C 库函数之间有点困惑!
编辑:为了补充问题,很多功能都在 (3) 但不在 (2) 下,即 clean
。这是否意味着这些是由 C 运行时直接完成的,而不依赖于系统调用和底层 OS?
首先,(2)
部分列出的函数是函数。它们与(3)
节中的函数不同,因为后面总是有一个系统调用。
这些函数通常会做额外的工作,使它们的行为像 POSIX 函数(将返回值转换为 -1
和 errno
),或者只是使它们可用(clone
系统调用需要 libc 集成才有用)。有时参数传递给系统调用的方式与函数原型建议的不同,例如,它们可以打包到结构中,指向该结构的指针可以通过寄存器传递。
有时会添加新的系统调用来修复旧系统调用的一些问题。在这种情况下,可以使用新的系统调用透明地实现一个函数(参见 mmap
与 mmap2
、sys_select
与 sys_old_select
)。
至于recvfrom
,与套接字相关的功能由their respective syscalls或遗留sys_socketcall
实现。例如 musl 仍然有这个代码:
#ifdef SYS_socket
#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_##nm, a, b, c, d, e, f)
#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_##nm, a, b, c, d, e, f)
#else
#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_socketcall, __SC_##nm, \
((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f }))
#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_socketcall, __SC_##nm, \
((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f }))
#endif
如果可用,它会尝试使用适当的系统调用,否则会退回到 socketcall
。
exists both under the linux system call
用户space 程序与内核通信的方式是使用syscall 函数。 syscall()
所做的就是将一些数字压入特定的寄存器,然后执行一条特殊的中断指令。在中断上,执行转移到内核,然后内核使用特殊寄存器从用户space读取数据。
每个系统调用都有一个编号和不同的参数。用户 space 程序需要 "find out" 每个系统调用的参数,例如检查文档。
Linux 系统调用只是一个数字,例如 __NR_recvfrom
在 x86-64 架构上等于 231。
C Lib function
C库函数是由C库实现实现的函数。因此,例如 glibc 将 recvfrom 实现为 syscall(__NR_recvfrom, ...)
的简单包装器。这是库为程序员提供访问内核相关函数的 C 接口。所以 C 程序员不需要阅读每个系统调用的文档,也不需要有很好的 C 接口来调用内核。
However I cannot find recvfrom in the above link.
那时候不要使用 link。最多检查 uapi
目录下的内核源代码。
首先要明白C函数和系统调用是完全不同的两个东西.
未包含在 C 库 中的系统调用必须 通过 syscall
函数调用。这种调用的一个例子是 gettid
.
要使用 syscall
创建一个 gettid
系统调用包装器,请执行以下操作:
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
pid_t gettid(void)
{
pid_t tid = (pid_t)syscall(SYS_gettid);
return tid;
}
这是手册页的注释部分的摘录,其中明确指出此函数未在 C 库中定义:
NOTES
Glibc does not provide a wrapper for this system call; call it using syscall(2).
recvfrom
是一个围绕系统调用的 C 库包装器。
第 (2) 节中的所有内容都是系统调用。第 (3) 节中的所有内容都不是。第 (3) 节中的所有内容(除了一些值得注意的例外,例如 getumask
)在 C 库中都有定义。第 (2) 节中的大约一半内容在 C 库中没有定义(或包装器)(POSIX 强制要求的函数除外,以及一些其他扩展,它们都有),如 gettid
.
在 C 中调用 recvfrom
时,C 库调用内核来执行系统调用。
syscall
函数是将系统调用号放入%eax
寄存器的函数,使用int [=21=]x80
.
您在 https://syscalls.kernelgrok.com/ is because https://syscalls.kernelgrok.com/ 中看不到 recvfrom
的原因非常非常不完整。
之所以 (3) 中有许多函数在 (2) 中看不到,是因为 (3) 中的许多函数没有系统调用。他们可能依赖也可能不依赖系统调用,他们只是没有支持他们的特定名称的系统调用。
Looking at recvfrom
as an example, this function exists both as a Linux system call as well as a C library function.
我找到了 recvfrom
的 2 个页面:
通常,Linux 页面还会说明函数的 Linux 版本与 POSIX 版本有何不同。
They are different from functions in section (3) in that there is always a system call behind [section 2].
不一定。第 2 部分是针对用户 space 应用程序的 Linux 特定 API。 Linus Torvalds 坚持认为用户 space 应用程序绝不能因为 Linux 内核 API 更改而中断。 glibc
或其他库正常实现功能,以保持稳定的用户-space API 并委托给内核。
我对这两个问题有点困惑,所以这是我的问题;
Linux 手册页项目列出了所有这些函数: https://www.kernel.org/doc/man-pages/
以 recvfrom
为例,此函数既作为 Linux 系统调用也作为 C 库函数存在。他们的文档看起来不同,但都可以使用 #include <sys/socket.h>
访问。
我不明白他们的区别?
我还认为系统调用是使用十六进制值定义的,可以直接在汇编中实现,它们的列表在这里: https://syscalls.kernelgrok.com/
但是我在上面的 link 中找不到 recvfrom
。在这一点上,我对 Linux 系统调用与 C 库函数之间有点困惑!
编辑:为了补充问题,很多功能都在 (3) 但不在 (2) 下,即 clean
。这是否意味着这些是由 C 运行时直接完成的,而不依赖于系统调用和底层 OS?
首先,(2)
部分列出的函数是函数。它们与(3)
节中的函数不同,因为后面总是有一个系统调用。
这些函数通常会做额外的工作,使它们的行为像 POSIX 函数(将返回值转换为 -1
和 errno
),或者只是使它们可用(clone
系统调用需要 libc 集成才有用)。有时参数传递给系统调用的方式与函数原型建议的不同,例如,它们可以打包到结构中,指向该结构的指针可以通过寄存器传递。
有时会添加新的系统调用来修复旧系统调用的一些问题。在这种情况下,可以使用新的系统调用透明地实现一个函数(参见 mmap
与 mmap2
、sys_select
与 sys_old_select
)。
至于recvfrom
,与套接字相关的功能由their respective syscalls或遗留sys_socketcall
实现。例如 musl 仍然有这个代码:
#ifdef SYS_socket
#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_##nm, a, b, c, d, e, f)
#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_##nm, a, b, c, d, e, f)
#else
#define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_socketcall, __SC_##nm, \
((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f }))
#define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_socketcall, __SC_##nm, \
((long [6]){ (long)a, (long)b, (long)c, (long)d, (long)e, (long)f }))
#endif
如果可用,它会尝试使用适当的系统调用,否则会退回到 socketcall
。
exists both under the linux system call
用户space 程序与内核通信的方式是使用syscall 函数。 syscall()
所做的就是将一些数字压入特定的寄存器,然后执行一条特殊的中断指令。在中断上,执行转移到内核,然后内核使用特殊寄存器从用户space读取数据。
每个系统调用都有一个编号和不同的参数。用户 space 程序需要 "find out" 每个系统调用的参数,例如检查文档。
Linux 系统调用只是一个数字,例如 __NR_recvfrom
在 x86-64 架构上等于 231。
C Lib function
C库函数是由C库实现实现的函数。因此,例如 glibc 将 recvfrom 实现为 syscall(__NR_recvfrom, ...)
的简单包装器。这是库为程序员提供访问内核相关函数的 C 接口。所以 C 程序员不需要阅读每个系统调用的文档,也不需要有很好的 C 接口来调用内核。
However I cannot find recvfrom in the above link.
那时候不要使用 link。最多检查 uapi
目录下的内核源代码。
首先要明白C函数和系统调用是完全不同的两个东西.
未包含在 C 库 中的系统调用必须 通过 syscall
函数调用。这种调用的一个例子是 gettid
.
要使用 syscall
创建一个 gettid
系统调用包装器,请执行以下操作:
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
pid_t gettid(void)
{
pid_t tid = (pid_t)syscall(SYS_gettid);
return tid;
}
这是手册页的注释部分的摘录,其中明确指出此函数未在 C 库中定义:
NOTES
Glibc does not provide a wrapper for this system call; call it using syscall(2).
recvfrom
是一个围绕系统调用的 C 库包装器。
第 (2) 节中的所有内容都是系统调用。第 (3) 节中的所有内容都不是。第 (3) 节中的所有内容(除了一些值得注意的例外,例如 getumask
)在 C 库中都有定义。第 (2) 节中的大约一半内容在 C 库中没有定义(或包装器)(POSIX 强制要求的函数除外,以及一些其他扩展,它们都有),如 gettid
.
在 C 中调用 recvfrom
时,C 库调用内核来执行系统调用。
syscall
函数是将系统调用号放入%eax
寄存器的函数,使用int [=21=]x80
.
您在 https://syscalls.kernelgrok.com/ is because https://syscalls.kernelgrok.com/ 中看不到 recvfrom
的原因非常非常不完整。
之所以 (3) 中有许多函数在 (2) 中看不到,是因为 (3) 中的许多函数没有系统调用。他们可能依赖也可能不依赖系统调用,他们只是没有支持他们的特定名称的系统调用。
Looking at
recvfrom
as an example, this function exists both as a Linux system call as well as a C library function.
我找到了 recvfrom
的 2 个页面:
通常,Linux 页面还会说明函数的 Linux 版本与 POSIX 版本有何不同。
They are different from functions in section (3) in that there is always a system call behind [section 2].
不一定。第 2 部分是针对用户 space 应用程序的 Linux 特定 API。 Linus Torvalds 坚持认为用户 space 应用程序绝不能因为 Linux 内核 API 更改而中断。 glibc
或其他库正常实现功能,以保持稳定的用户-space API 并委托给内核。