您的位置:首页 > 其它

socket调用流程分析

2012-03-07 10:02 267 查看
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是通过那个功能号调用的;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: