Chinaunix首页 | 论坛 | 博客
  • 博客访问: 233145
  • 博文数量: 35
  • 博客积分: 659
  • 博客等级: 上士
  • 技术积分: 357
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-01 21:16
文章分类
文章存档

2012年(12)

2011年(23)

分类:

2012-04-19 08:10:00

当我们调用socket()这个系统调用的时候,Linux内核究竟做了哪些事情呢?
Ok,就让我们来看看这socket背后的故事吧。

1.用户程序首先调用socket()系统调用,其原型为:
int socket(int domain, int type, int protocol)

2.我们调用的socket()系统调用其实是glic这个库提供的,glic内部会执行实际的系统调用。
我们可以看一下glic的内部实现。
文件sysdeps/unix/sysv/linux/i386/socket.S

点击(此处)折叠或打开

  1. .globl __socket
  2. ENTRY (__socket)
  3. #if defined NEED_CANCELLATION && defined CENABLE
  4.     SINGLE_THREAD_P
  5.     jne 1f
  6. #endif

  7.     /* 保存ebx寄存器的值 */
  8.     movl %ebx, %edx
  9.     cfi_register (3, 2)

  10.     /* 把系统调用的调用号放到eax寄存器里 */
  11.     movl $SYS_ify(socketcall), %eax

  12.     /* 传给syscall的第一个参数是子code,用来识别是哪一个socket系统调用 */
  13.     movl $P(SOCKOP_,socket), %ebx

  14.     /* 第二个参数是用户态传递的参数数组的地址 */
  15.     lea 4(%esp), %ecx

  16.     /* 执行系统调用 */
  17.     ENTER_KERNEL

  18.     /* 恢复ebx寄存器 */
  19.     movl %edx, %ebx
  20.     cfi_restore (3)

  21.     /* 如果发生了错误,%eax小于0 */
  22.     cmpl $-125, %eax
  23.     jae SYSCALL_ERROR_LABEL

  24.     /* 成功的话返回系统调用的结果 */
  25. L(pseudo_end):
  26.     ret
为了节约系统调用号,在2.4以后的内核中,已经把所有的socket相关的系统调用,比如connect(),send()等等,都统一封装在一个内核函数sys_socketcall()里,执行系统调用的时候,需要传递一个SOCKOP_XXX(sysdeps/unix/sysv/linux/socketcall.h)来识别是哪个socket函数

3.当内核收到系统调用的命令以后,会从eax寄存器中取出系统调用号,然后在sys_call_table
这个表中查找对应的处理系统调用的函数
,具体过程不再赘述。
内核会调用sys_socketcall()来处理

文件net/socket.c

点击(此处)折叠或打开

  1. asmlinkage long sys_socketcall(int call, unsigned long __user *args)
  2. {
  3.     unsigned long a[6];
  4.     unsigned long a0, a1;
  5.     int err;

  6.     if (call < 1 || call > SYS_ACCEPT4)
  7.         return -EINVAL;

  8.     /* 调用copy_from_user函数,从用户空间的内存地址拷贝参数到内核空间 */
  9.     if (copy_from_user(a, args, nargs[call]))
  10.         return -EFAULT;

  11.     err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
  12.     if (err)
  13.         return err;

  14.     a0 = a[0];
  15.     a1 = a[1];

  16.     switch (call) {
  17.     case SYS_SOCKET:
  18.         err = sys_socket(a0, a1, a[2]);
  19.         break;
  20.     case SYS_BIND:
  21.         err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
  22.         break;
  23.     case SYS_CONNECT:
  24.         err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
  25.         break;
  26.     case SYS_LISTEN:
  27.         err = sys_listen(a0, a1);
  28.         break;
  29.     case SYS_ACCEPT:
  30.         err = sys_accept4(a0, (struct sockaddr __user *)a1,
  31.                  (int __user *)a[2], 0);
  32.         break;
  33.     case SYS_GETSOCKNAME:
  34.         err =
  35.          sys_getsockname(a0, (struct sockaddr __user *)a1,
  36.                  (int __user *)a[2]);
  37.         break;
  38.     case SYS_GETPEERNAME:
  39.         err =
  40.          sys_getpeername(a0, (struct sockaddr __user *)a1,
  41.                  (int __user *)a[2]);
  42.         break;
  43.     case SYS_SOCKETPAIR:
  44.         err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
  45.         break;
  46.     case SYS_SEND:
  47.         err = sys_send(a0, (void __user *)a1, a[2], a[3]);
  48.         break;
  49.     case SYS_SENDTO:
  50.         err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
  51.                  (struct sockaddr __user *)a[4], a[5]);
  52.         break;
  53.     case SYS_RECV:
  54.         err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
  55.         break;
  56.     case SYS_RECVFROM:
  57.         err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
  58.                  (struct sockaddr __user *)a[4],
  59.                  (int __user *)a[5]);
  60.         break;
  61.     case SYS_SHUTDOWN:
  62.         err = sys_shutdown(a0, a1);
  63.         break;
  64.     case SYS_SETSOCKOPT:
  65.         err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
  66.         break;
  67.     case SYS_GETSOCKOPT:
  68.         err =
  69.          sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
  70.                  (int __user *)a[4]);
  71.         break;
  72.     case SYS_SENDMSG:
  73.         err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
  74.         break;
  75.     case SYS_RECVMSG:
  76.         err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);
  77.         break;
  78.     case SYS_ACCEPT4:
  79.         err = sys_accept4(a0, (struct sockaddr __user *)a1,
  80.                  (int __user *)a[2], a[3]);
  81.         break;
  82.     default:
  83.         err = -EINVAL;
  84.         break;
  85.     }
  86.     return err;
  87. }
可以看到,sys_socketcall会通过call这个参数来判断执行哪个socket动作。
因为我们调用的是socket()系统调用,所以会执行
sys_socket()这个函数。

4.内核调用sys_socket()来处理创建socket的系统调用。
文件net/socket.c

点击(此处)折叠或打开

  1. asmlinkage long sys_socket(int family, int type, int protocol)
  2. {
  3.     int retval;
  4.     struct socket *sock;
  5.     int flags;

  6.     /* Check the SOCK_* constants for consistency. */
  7.     BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
  8.     BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
  9.     BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
  10.     BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

  11.     flags = type & ~SOCK_TYPE_MASK;
  12.     if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
  13.         return -EINVAL;
  14.     type &= SOCK_TYPE_MASK;

  15.     if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
  16.         flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

  17.     /* 实际创建socket */
  18.     retval = sock_create(family, type, protocol, &sock);
  19.     if (retval < 0)
  20.         goto out;

  21.     /* 把创建的socket映射到文件描述符 */
  22.     retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
  23.     if (retval < 0)
  24.         goto out_release;

  25. out:
  26.     /* It may be already another descriptor 8) Not kernel problem. */
  27.     return retval;

  28. out_release:
  29.     sock_release(sock);
  30.     return retval;
  31. }
可以看到,实际创建socket的函数是sock_create(),接下来就让我们好好研究一下这个函数。




阅读(4597) | 评论(0) | 转发(0) |
0

上一篇:Linux环境下手动配置IP

下一篇:学习计划

给主人留下些什么吧!~~