socket调用流程分析

来源:互联网 发布:看网球软件 编辑:程序博客网 时间:2024/05/22 14:02

0、概述:

在这里将讲述用户层的socket函数如何调用到内核的sys_socket函数,有什么错误啊,还请各位大虾指出,哈哈~

分析:

1、 函数声明

根据其头文件我们可以找到相应的声明;

extern intsocket (int __domain, int __type, int __protocol) __THROW;

__THROW定义如下:即针对C++,表示该函数不抛出任何异常

# if defined__cplusplus && __GNUC_PREREQ (2,8)

#  define __THROW    throw ()

# else

#  define __THROW

# endif

 

2、 函数定义

有了声明,那应该有定义吧,那就去glibc源码中找呗,找啊找,可惜,最终在glibc中却是无法找到socket函数的定义,难道变神了?那是不可能的,那么现在唯一的一条路就是改名(这是必须的,毕竟是人类发明的),于是仔细一找,在socket.S中发现了它:

#ifndef __socket

#ifndefNO_WEAK_ALIAS

#define __socketP(__,socket)

#else

#define __socketsocket

#endif

#endifsocket,就是改名字

即现在的任务就要寻找__socket,在这个文件中不难发现,__socket的定义如下:

.globl __socket

ENTRY (__socket)

 

       /* Save registers.  */

       movl %ebx, %edx

 

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

 

       /* Use ## so `socket' is a separate tokenthat might be #define'd.  */

       movl $P(SOCKOP_,socket), %ebx      /* Subcode is first arg to syscall.  */

       lea 4(%esp), %ecx         /* Address of args is 2nd arg.  */

 

        /* Do the system call trap.  */

       int $0x80

 

       /* Restore registers.  */

       movl %edx, %ebx

 

       /* %eax is < 0 if there was anerror.  */

       cmpl $-125, %eax

       jae SYSCALL_ERROR_LABEL

 

       /* Successful; return the syscall'svalue.  */

L(pseudo_end):

       ret

说明:

(1)SYS_ify(name)作用是在name前面添加__NR_

(2)movl $SYS_ify(socketcall), %eax作用是将__NR_socketcall的值赋给eax寄存器;

__NR_socketcall定义在内核的unistd.h中

#define__NR_socketcall              102

(3)movl $P(SOCKOP_,socket), %ebx,是把SOCKOP_socket值赋给ebx;

SOCKOP_socket定义如下:

#defineSOCKOP_socket             1

(4)lea 4(%esp), %ecx,作用是把socket函数的参数地址传给ecx;

补充:调用函数时,参数存放的位置

如调用add(a, b)时,a是存放于esp+4, b是存放于esp+8的位置;(具体先后还要看压栈顺序)

 

(5)int $0x80系统调用,这进入另一个神秘的世界——内核

 

3、系统调用

这里针对linux内核来大致讲述一下系统调用流程:

在start_kernel->trap_init中,定义了系统调用的相关信息;

# define SYSCALL_VECTOR                    0x80

set_system_trap_gate(SYSCALL_VECTOR,&system_call);

这样我们就知道了,当系统出现调用中断时,则将调用system_call函数,那下面来看一下system_call的函数,(定义在entry_32.S中)

syscall_call:

      call *sys_call_table(,%eax,4)

      movl %eax,PT_EAX(%esp)         #store the return value ()

从函数可以看出,是根据eax(功能号),来调用sys_call_table表中的相应函数;那么socket函数对应的功能号为__NR_socketcall;于是在sys_call_table表中可以找到相应的函数

SYSCALL_DEFINE2(socketcall,int, call, unsigned long __user *, args)

{

      unsigned long a[6];

      unsigned long a0, a1;

      int err;

 

      if (call < 1 || call > SYS_ACCEPT4)

              return -EINVAL;

 

       /* copy_from_user should be SMP safe. */

       if (copy_from_user(a, args, nargs[call]))

              return -EFAULT;

 

       audit_socketcall(nargs[call] / sizeof(unsigned long), a);

 

       a0 = a[0];

       a1 = a[1];

 

       switch (call) {

       case SYS_SOCKET:

              err = sys_socket(a0, a1, a[2]);

              break;

       case SYS_BIND:

              err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);

              break;

       case SYS_CONNECT:

              err = sys_connect(a0, (struct sockaddr __user *)a1,a[2]);

              break;

       case SYS_LISTEN:

              err = sys_listen(a0, a1);

              break;

       case SYS_ACCEPT:

              err = sys_accept4(a0, (struct sockaddr __user *)a1,

                             (int __user *)a[2], 0);

              break;

       case SYS_GETSOCKNAME:

              err =

                 sys_getsockname(a0, (struct sockaddr __user *)a1,

                               (int __user *)a[2]);

              break;

       case SYS_GETPEERNAME:

              err =

                  sys_getpeername(a0,(struct sockaddr __user *)a1,

                               (int __user *)a[2]);

              break;

       case SYS_SOCKETPAIR:

              err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);

              break;

       case SYS_SEND:

              err = sys_send(a0, (void __user *)a1, a[2], a[3]);

              break;

       case SYS_SENDTO:

              err = sys_sendto(a0, (void __user *)a1, a[2], a[3],

                             (struct sockaddr __user *)a[4], a[5]);

              break;

       case SYS_RECV:

              err = sys_recv(a0, (void __user *)a1, a[2], a[3]);

              break;

       case SYS_RECVFROM:

              err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],

                              (struct sockaddr __user *)a[4],

                              (int __user *)a[5]);

              break;

       case SYS_SHUTDOWN:

              err = sys_shutdown(a0, a1);

              break;

       case SYS_SETSOCKOPT:

              err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3],a[4]);

              break;

       case SYS_GETSOCKOPT:

              err =

                 sys_getsockopt(a0, a1, a[2], (char __user *)a[3],

                              (int __user *)a[4]);

              break;

       case SYS_SENDMSG:

              err = sys_sendmsg(a0, (struct msghdr __user *)a1,a[2]);

              break;

       case SYS_RECVMSG:

              err = sys_recvmsg(a0, (struct msghdr __user *)a1,a[2]);

              break;

       case SYS_ACCEPT4:

              err = sys_accept4(a0, (struct sockaddr __user *)a1,

                             (int __user *)a[2], a[3]);

              break;

       default:

              err = -EINVAL;

              break;

       }

       return err;

}

从函数可以看出,用户层函数,最终调用了sys_socket;至于后续的分析,这里就不讲了;

4、总结

通过分析发现,用户层的socket函数,通过系统调用,最终将调用内核的sys_socket函数;

 

5、疑问

在查看内核代码是,有发现__NR_socketcall似乎已经被废弃掉了,但我在.S中又未找到__NR_socket的内容,所以后续还需在看看,现在的socket是通过那个功能号调用的;

0 0