读者可以根据图1.1和代码清单1.1所列出的函数,在glibc源码中看到它们都会执行系统调用sys_socketcall函数,函数sys_socketcall 是内核提供给socket通信的总入口,对于这个函数分析我们把重点放在与路线有关的内容上,因此只摘取了sys_socketcall 函数中关键的代码部分,如代码清单1.6所示。
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
unsigned long a[6];
unsigned long a0, a1;
if (copy_from_user(a, args, nargs[call]))
return -EFAULT;
a0 = a[0];
a1 = a[1];
switch(call) {
case SYS_SOCKET;
err = sys_socket(a0, a1, a[2]);
}
}
sys_socketcall 函数是linux内核socket的系统调用入口,前面介绍了函数的两个参数是分别通过寄存器ebx和ecx来传递的,参数call是具体的socjet调用号,ebx寄存器保存值为1,这个值与2028行的宏SYS_SOCKET相同。
参数args 是指针,是ecx寄存器传递的参数数组,由于服务器程序在用户u空间linux内核空间和用户空间 而系统调用函数在内核空间,需要将这些参数从服务器程序复制到内核中,即从用户空间复制到内核空间,复制函数为copy_from_user()
另外,还需要为copy_from_user()函数明确复制的参数个数,这是由nargs[]数组来决定的,以参数call为下标,其传递值为1,从数组中可以找到对应的参数个数。
#define AL(x) ((x) * sizeof(unsigned long))
static const unsigned char nargs[18] = {
};
对应call处的值是AL(3), 再通过宏AL() 的定义得到复制的3个参数的字节总数为12,于是,copy_from_user 函数把args指向3个参数AF_INET,SOCK_STREAM,0从用户空间的服务器程序复制到内核空间数组a中。
可以看到,include /linux/ net.h 中规定了调用号call的详细内容,细心的读者可以对照glibc对这些调用号的定义,目录sysdeps/unix/sysv/linux的socketcall.h 文件中。 可以看出linux内核对这些调用号的定义完全的glibc相同,这也是linux内核对glibc版本严格要求的原因。
代码清单1.8 参数call的对应内容
#define SYS_SOCKET 1
#define SYS_BIND 2
#define SYS_CONNECT 3
#define SYS_LISTEN 4
#define SYS_ACCEPT 5
#define SYS_GETSOCKNAME 6
#define SYS_GETPEERNAME 7
#define SYS_SOCKETPAIR 8
#define SYS_SEND 9
#define SYS_RECV 10
#define SYS_SENDTO 11
#define SYS_RECVFROM 12
#define SYS_SHUTDOWN 13
#define SYS_SETSOCKOPT 14
#define SYS_GETSOCKOPT 15
#define SYS_SNEDMSG 15
#define SYS_RECVMSG 17
根据call的具体定义就可以明确sys_socketcall 函数2027行的switch路线,注意,代码清单1.8 注释中的后缀表示这些定义均是系统调用号。
sys_socketcall函数对于服务器程序和客户端程序来说就是一扇大门,服务器程序和客户端程序调用的函数都要通过这扇门找到各自的对应的函数,代码清单1.1 中调用的socket 重要的部分,为了清楚显示函数的调用过程,本书在以后的代码清单前面用->来表示调用线路。
sys_socketcall()->sys_socket()
asmlinkage long sys_socket(int family, int type, int protocol)
{
retval = sock_create(family, type, protocol, &sock);
retval = sock_map_fd(sock); 与文件系统建立映射关系。
}
从上面函数名称上就可以看出,sock_create 的作用就是创建一个服务器的socket插口,传递了3个参数,family为AF_INET,type为SOCK_STREAM, protocol为0,对这个函数肚饿分析将在下一章进行,1225行调用的sock_map_fd 函数为新建的socket在网络文件系统中申请文件号和文件描述符结构。