glibc 的套接字实现在哪里?
Where's glibc's socket implementation at?
在 glibc 2.22 中,在 /socket
目录中,套接字库实现位于。
但是,当打开这些文件中的任何一个时,我所看到的只是一个错误设置函数,它下面有一些宏。
这是一个示例文件 (/socket/send.c
):
#include <errno.h>
#include <sys/socket.h>
/* Send N bytes of BUF to socket FD. Returns the number sent or -1. */
ssize_t
__send (fd, buf, n, flags)
int fd;
const __ptr_t buf;
size_t n;
int flags;
{
__set_errno (ENOSYS);
return -1;
}
libc_hidden_def (__send)
weak_alias (__send, send)
stub_warning (send)
(已删除许可证的评论。)
send
weak_alias
宏的参数在哪里?这些宏定义在哪里?
我认为这是由于与一些蹩脚的旧编译器的兼容性,但为什么他们仍然使用 K&R 语法?
最重要的是,为什么 __send
这样定义?
一般像send
、bind
、socket
这样的socket函数都是系统调用,也就是操作系统直接提供的。请注意,在 unix 系统上,您将键入 man 2 socket
而不是 man 3 fopen
。前者是系统调用,后者是库函数。所以 glibc 在这里没有提供任何功能,这就是为什么你在 glibc 源代码中找不到实现的原因。
至于 __send
和 weak_alias
,glibc 正在从 send
创建一个 弱绑定 到__send
这基本上意味着如果您调用 send 并且没有其他可用的实现,您将获得 glibc 的 __send
。这只是 returns ENOSYS
正是因为没有可用的实现。如果有真正的实现,它将覆盖弱绑定。
sysdeps
目录是完成所有包装系统调用的脏工作的地方。例如,查看 sysdeps/unix/sysv/linux/x86_64/syscalls.list
,您将看到从 C 函数调用到该 OS 和体系结构的 OS 系统调用的映射。查看同一目录中的 syscall.S
,您将看到系统调用实际上是如何进行的。简而言之,没有真正的 user-level 代码实现系统调用,libc 实现将与系统调用关联的数字放在寄存器中,然后执行 syscall
指令。所有真正的实现都在内核中。
这里发生了几件事。
首先,如this answer所述,glibc本身不能定义标准C不保留的随机标识符,因为标准C允许程序自己定义这些标识符。双下划线开头的名称是为实现保留的,所以这里的实现定义了函数__send()
。弱别名允许使用名称 send()
来引用它,但也允许该引用被其他地方的强定义覆盖。
其次,如 in the glibc documentation 所述,为了便于移植 glibc,要求任何 machine-specific 函数都具有相应的通用函数。如果可以编写相应的泛型函数,那么它应该是,但如果不能,泛型函数应该是 "stub function",本质上只是将 errno
设置为 ENOSYS
(未实现)和returns 一个错误。如果提供了 machine-specific 函数,将使用该函数代替存根函数。由于send()
需要系统调用,显然不能写成machine-independent的方式,所以这里有一个存根函数。因此,您应该能够找到 __send()
的 machine-specific 实现(或多种实现),例如,在 glibc 源代码树中的 /sysdeps/unix/sysv/linux/x86_64/send.c
。
顺便说一下,由于 send()
确实是一个系统调用,您通常会在 glibc 中看到的只是一个进行系统调用的简短汇编语言例程。实际执行任务的代码将存在于内核中。
在 glibc 2.22 中,在 /socket
目录中,套接字库实现位于。
但是,当打开这些文件中的任何一个时,我所看到的只是一个错误设置函数,它下面有一些宏。
这是一个示例文件 (/socket/send.c
):
#include <errno.h>
#include <sys/socket.h>
/* Send N bytes of BUF to socket FD. Returns the number sent or -1. */
ssize_t
__send (fd, buf, n, flags)
int fd;
const __ptr_t buf;
size_t n;
int flags;
{
__set_errno (ENOSYS);
return -1;
}
libc_hidden_def (__send)
weak_alias (__send, send)
stub_warning (send)
(已删除许可证的评论。)
send
weak_alias
宏的参数在哪里?这些宏定义在哪里?
我认为这是由于与一些蹩脚的旧编译器的兼容性,但为什么他们仍然使用 K&R 语法?
最重要的是,为什么 __send
这样定义?
一般像send
、bind
、socket
这样的socket函数都是系统调用,也就是操作系统直接提供的。请注意,在 unix 系统上,您将键入 man 2 socket
而不是 man 3 fopen
。前者是系统调用,后者是库函数。所以 glibc 在这里没有提供任何功能,这就是为什么你在 glibc 源代码中找不到实现的原因。
至于 __send
和 weak_alias
,glibc 正在从 send
创建一个 弱绑定 到__send
这基本上意味着如果您调用 send 并且没有其他可用的实现,您将获得 glibc 的 __send
。这只是 returns ENOSYS
正是因为没有可用的实现。如果有真正的实现,它将覆盖弱绑定。
sysdeps
目录是完成所有包装系统调用的脏工作的地方。例如,查看 sysdeps/unix/sysv/linux/x86_64/syscalls.list
,您将看到从 C 函数调用到该 OS 和体系结构的 OS 系统调用的映射。查看同一目录中的 syscall.S
,您将看到系统调用实际上是如何进行的。简而言之,没有真正的 user-level 代码实现系统调用,libc 实现将与系统调用关联的数字放在寄存器中,然后执行 syscall
指令。所有真正的实现都在内核中。
这里发生了几件事。
首先,如this answer所述,glibc本身不能定义标准C不保留的随机标识符,因为标准C允许程序自己定义这些标识符。双下划线开头的名称是为实现保留的,所以这里的实现定义了函数__send()
。弱别名允许使用名称 send()
来引用它,但也允许该引用被其他地方的强定义覆盖。
其次,如 in the glibc documentation 所述,为了便于移植 glibc,要求任何 machine-specific 函数都具有相应的通用函数。如果可以编写相应的泛型函数,那么它应该是,但如果不能,泛型函数应该是 "stub function",本质上只是将 errno
设置为 ENOSYS
(未实现)和returns 一个错误。如果提供了 machine-specific 函数,将使用该函数代替存根函数。由于send()
需要系统调用,显然不能写成machine-independent的方式,所以这里有一个存根函数。因此,您应该能够找到 __send()
的 machine-specific 实现(或多种实现),例如,在 glibc 源代码树中的 /sysdeps/unix/sysv/linux/x86_64/send.c
。
顺便说一下,由于 send()
确实是一个系统调用,您通常会在 glibc 中看到的只是一个进行系统调用的简短汇编语言例程。实际执行任务的代码将存在于内核中。