glibc中socket实现分析

| 2009年10月11日

glibc-2.9, linux-2.6.32-rc1 在查看系统调用的时候发现: 关于网络的系统调用只有一个socketcall:

/usr/include/asm/unistd_32.h 
#define __NR_socketcall         102   

而这个系统调用的调用实现是在glibc中: 在glibc中socket的实现实在这里: sysdeps/unix/sysv/linux/i386/socket.S 对这个文件稍后分析,先看其它的函数: 而其它的网络相关函数都在这里定义:

  2 F   d    socket            sysdeps/unix/sysv/linux/accept.S
               #define socket accept
  3 F   d    socket            sysdeps/unix/sysv/linux/bind.S
               #define socket bind
。。。。其它还有:
               #define socket connect
               #define socket getpeername
               #define socket getsockname
               #define socket getsockopt
               #define socket listen
               #define socket recv
               #define socket recvfrom
               #define socket recvmsg
               #define socket sendmsg
               #define socket sendto
               #define socket setsockopt
。。。。

而其中每个.S文件中的内容几乎一致,下面只取几个说明一下:

#define socket  bind
#define NARGS   3	//这个是说它的参数的个数
#define NO_WEAK_ALIAS   1 //这个是说这个没有其它的别名
#include <socket.S>  
---------------------------------------------------------
#define socket  listen                                                                          
#define NARGS   2
#define NO_WEAK_ALIAS   1
#include <socket.S>
----------------------------------------------------------
#define socket  accept                                                                          
#define __socket __libc_accept  //这里还定义了其它的别名
#define NARGS   3
#include <socket.S>

也就是所有的网络系统调用都是基于socket这个系统调用的,只是在调用的时候传递的参数不同而已。

那现在再来具体分析sysdeps/unix/sysv/linux/i386/socket.S: 主要内容如下:

/*这个要在上面定义其它函数中使用,如accept,bind等
*看在上面的具体函数中有些定义了__socket,有些没有,有些定义了NO_WEAK_ALIAS。
*这些都是为选择不同的函数做设置的
*/
#ifndef __socket     
# ifndef NO_WEAK_ALIAS
#  define __socket P(__,socket)
# else
#  define __socket socket
# endif
#endif

.globl __socket

ENTRY (__socket)                                                                                
#if defined NEED_CANCELLATION && defined CENABLE
        SINGLE_THREAD_P
        jne 1f
#endif

        /* Save registers.  */
        movl %ebx, %edx
        cfi_register (3, 2)

        movl $SYS_ify(socketcall), %eax /* System call number in %eax.  */

        /* Use ## so `socket' is a separate token that might be #define'd.  */
	/* 这个号是来区别调用那个具体函数的,是socket还是bind
	*这里的socket的具体值是会发生变化的,就是在上面的#define socket bind
        *这样类似的语句中变换这个socket的值,关于其具体的值在后面给出
        */
        movl $P(SOCKOP_,socket), %ebx   
        lea 4(%esp), %ecx               /* Address of args is 2nd arg. 这里以堆栈方式传递其它的参数 */

        /* 这里进入系统调用,从PII之后的cpu都有两种方式进入系统调用,一种是传统的int 0x80 
       另一种就是cpu直接提供的指令enter_syscall,所以这里提供了这样一个语句,
       而其实这也是通过条件编译的方式将其值为int 0x80或其它方式
         */
        ENTER_KERNEL

        /* Restore registers. 恢复寄存器 */
        movl %edx, %ebx
        cfi_restore (3)

        /* %eax is < 0 if there was an error. 比较返回值 */
        cmpl $-125, %eax
        jae SYSCALL_ERROR_LABEL

        /* Successful; return the syscall's value. 正常返回 */
L(pseudo_end):
        ret

$P(SOCKOP_,socket)的值: sysdeps/unix/sysv/linux/socketcall.h :

#define SOCKOP_socket           1                                                               
#define SOCKOP_bind             2
#define SOCKOP_connect          3
#define SOCKOP_listen           4
#define SOCKOP_accept           5
#define SOCKOP_getsockname      6
。。。。
#define SOCKOP_recvmsg          17
#define SOCKOP_paccept          18

还有其内核的实现我们下一篇文章在进行分析。。。

看完本文有收获?请分享给更多人

关注「黑光技术」,关注大数据+微服务