socket调用流程分析
2014-08-17 23:30
155 查看
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是通过那个功能号调用的;
相关文章推荐
- socket调用流程分析
- eMule源码主要调用流程分析
- USB驱动Suspend&Resume 调用流程分析
- linux-kernel 3.5.3Tcp系统调用,源码分析2-sys_socket & sock_create
- FUSE调用流程分析
- linux网络协议栈分析——ioctl的调用流程
- 利用Anthem.net 实现前台javascript调用服务器端c#函数 及流程分析
- linux网络协议栈分析——ioctl的调用流程
- HEVC/H.265整个程序调用流程分析以及函数复杂度分析
- zencart v150 文件调用流程(index.php分析)
- open系统调用在内核中的流程分析
- 从USB设备插上到驱动probe调用流程分析
- Mysql源代码分析系列(3): 主要调用流程--转载
- MySQL源码分析(4):InnoDB主要数据结构及调用流程
- LDD3之scull--从open系统调用到模块的scull_open执行流程分析
- linux网络协议栈分析——ioctl的调用流程
- 利用Anthem.net 实现前台javascript调用服务器端c#函数 及流程分析
- 从USB设备插上到驱动probe调用流程分析
- MySQL5.5-audit plugin的函数调用流程分析
- 2012-11-26 13:06 mahout调用流程分析