【问题标题】:Where's glibc's socket implementation at?glibc 的套接字实现在哪里?
【发布时间】:2016-04-02 19:59:23
【问题描述】:

在glibc 2.22中,在/socket目录下,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 是一个系统调用,所以你不会在 glibc 中看到太多,除了围绕该调用的一个薄包装器。真正做事的代码将在内核中。另请参阅this question 接受的答案,了解为什么使用这两个名称,以及this document 了解存根函数的一些解释。
  • sourceware.org/glibc/wiki/SyscallWrappers 这是第一种。您需要寻找内核附带的特定于系统的实现。

标签: c sockets posix glibc


【解决方案1】:

这里发生了几件事。

首先,正如this answer 中所解释的,glibc 本身不能定义标准 C 不保留的随机标识符,因为标准 C 允许程序自己定义这些标识符。双下划线开头的名字是为实现保留的,所以这里的实现定义了函数__send()。然后,弱别名允许使用名称 send() 来引用它,但也允许该引用被其他地方的强定义覆盖。

其次,正如in the glibc documentation 所解释的,为了便于移植 glibc 要求任何特定于机器的函数都具有相应的通用函数。如果可以编写相应的泛型函数,那么它应该是,但如果不是,那么泛型应该是“存根函数”,它本质上只是将errno 设置为ENOSYS(未实现)并返回错误。如果提供了特定于机器的功能,则将使用该功能代替存根功能。由于send()需要系统调用,显然不能用机器无关的方式编写,所以这里有一个stub函数。因此,您应该能够找到__send() 的特定于机器的实现(或多种实现),例如,在 glibc 源代码树中的 /sysdeps/unix/sysv/linux/x86_64/send.c

顺便说一句,由于send() 确实是一个系统调用,所以您通常会在 glibc 中看到一个用于进行系统调用的简短汇编语言例程。实际做事的代码将存在于内核中。

【讨论】:

  • 好的,我现在知道了,谢谢。但是例如setsockopt 也只是一个存根。它真的依赖于机器吗?你有没有机会知道在哪里可以找到套接字函数的那些“更强大”的实现?
  • 您可以在 sysdeps 下找到特定于平台的内容,但 IIRC 大多数系统调用实际上是自动生成的。在所有用户模式的东西归结为在几个寄存器中复制参数之后,将系统调用 id 移动到一个寄存器并在内核中捕获(通过 x86 上的int 80h 和 x86_64 上的sysenter)。同样,实际的套接字实现在内核中,你不会在 glibc 中找到任何有趣的东西。
  • @cad:请参阅编辑以回答有关在哪里可以找到 x86_64 Linux 实际实现的示例。是的,它确实是依赖于机器的,因为根据定义进行系统调用是特定于架构的,你不能在纯标准 C 中做到这一点。
  • @PaulGriffiths 糟糕,不知道 setsockopt 是系统调用。
  • 实际上它们是系统相关的。换句话说,它取决于操作系统公开的实际代码。对于 Linux,您可以尝试在 filippo.io/linux-syscall-table 的帮助下挖掘源代码树
【解决方案2】:

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

至于__sendweak_alias,glibc 正在创建一个从send__send弱绑定,这基本上意味着如果你调用send并且没有其他可用的实现,您将获得 glibc 的__send。这只是返回 ENOSYS 正是因为没有可用的实现。如果有真正的实现,它将覆盖弱绑定。

sysdeps 目录是完成所有脏活以包装系统调用的地方。例如,查看sysdeps/unix/sysv/linux/x86_64/syscalls.list,您将看到从 C 函数调用到该操作系统和体系结构的操作系统系统调用的映射。查看同一目录中的syscall.S,您将看到系统调用实际上是如何进行的。简而言之,没有真正的用户级代码实现系统调用,libc 实现将与系统调用关联的数字放入寄存器中,然后执行syscall 指令。所有真正的实现都在内核中。

【讨论】:

  • 好的,但是这些系统调用必须被封装到库函数某处。那么acceptaccept4setsockopt 等呢?但我明白这一切现在应该如何运作,谢谢。我的问题是现在在哪里可以找到实际的函数实现。
  • @cad 我在回答中添加了一些关于系统调用如何工作的注释。这能回答你的问题吗?
  • 是的,我认为确实如此。谢谢。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-26
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多