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 这样定义?

一般像sendbindsocket这样的socket函数都是系统调用,也就是操作系统直接提供的。请注意,在 unix 系统上,您将键入 man 2 socket 而不是 man 3 fopen。前者是系统调用,后者是库函数。所以 glibc 在这里没有提供任何功能,这就是为什么你在 glibc 源代码中找不到实现的原因。

至于 __sendweak_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 中看到的只是一个进行系统调用的简短汇编语言例程。实际执行任务的代码将存在于内核中。