在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&amp; R语法?
最重要的是,为什么 __send
这样定义?
答案 0 :(得分:6)
这里有几件事情。
首先,如this answer中所述,glibc本身不能定义标准C不保留的随机标识符,因为标准C允许程序自己定义这些标识符。以双下划线开头的名称是为实现保留的,因此这里的实现定义了函数__send()
。弱别名允许使用名称send()
来引用它,但也允许该引用被其他地方的强定义覆盖。
其次,正如in the glibc documentation所解释的,为了便于移植glibc,需要任何特定于机器的函数具有相应的泛型函数。如果可以编写相应的泛型函数,那么它应该是,但如果不是,那么泛型函数应该是“存根函数”,它基本上只是将errno
设置为ENOSYS
(未实现)并返回一个错误。如果提供了特定于机器的功能,则将使用该功能代替存根功能。由于send()
需要系统调用,显然它不能以与机器无关的方式编写,因此这里有一个存根函数。因此,您应该能够找到__send()
的机器特定实现(或其中的各种实现),例如,在glibc源代码树中的/sysdeps/unix/sysv/linux/x86_64/send.c
。
顺便说一句,由于send()
确实是一个系统调用,所以你通常会在glibc中看到的是一个简短的汇编语言例程,它会调用系统。实际做的东西的代码将存在于内核中。
答案 1 :(得分:3)
通常,send
,bind
和socket
等套接字函数是系统调用,这意味着它们直接由操作系统提供。请注意,在unix系统上,您将man 2 socket
与man 3 fopen
进行比较。前者是系统调用,后者是库函数。所以glibc没有提供任何功能,这就是为什么你在glibc源代码中找不到实现的原因。
关于__send
和weak_alias
,正在发生的是glibc正在从send
到__send
创建弱约束这基本上意味着如果你调用send并且没有其他可用的实现,你将得到glibc的__send
。这只是返回ENOSYS
,因为没有可用的实现。如果有真正的实现,它将覆盖弱绑定。
sysdeps
目录是完成包装系统调用所有脏工作的地方。例如,查看sysdeps/unix/sysv/linux/x86_64/syscalls.list
,您将看到从C函数调用到该操作系统和体系结构的OS系统调用的映射。查看同一目录中的syscall.S
,您将看到系统调用是如何实际制作的。简而言之,没有真正的用户级代码实现系统调用,libc实现将与系统调用关联的数字放在寄存器中,然后执行syscall
指令。所有真正的实现都在内核中。