当我们调用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- .globl __socket
- ENTRY (__socket)
- #if defined NEED_CANCELLATION && defined CENABLE
- SINGLE_THREAD_P
- jne 1f
- #endif
- /* 保存ebx寄存器的值 */
- movl %ebx, %edx
- cfi_register (3, 2)
- /* 把系统调用的调用号放到eax寄存器里 */
- movl $SYS_ify(socketcall), %eax
- /* 传给syscall的第一个参数是子code,用来识别是哪一个socket系统调用 */
- movl $P(SOCKOP_,socket), %ebx
- /* 第二个参数是用户态传递的参数数组的地址 */
- lea 4(%esp), %ecx
- /* 执行系统调用 */
- ENTER_KERNEL
- /* 恢复ebx寄存器 */
- movl %edx, %ebx
- cfi_restore (3)
- /* 如果发生了错误,%eax小于0 */
- cmpl $-125, %eax
- jae SYSCALL_ERROR_LABEL
- /* 成功的话返回系统调用的结果 */
- L(pseudo_end):
- 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- asmlinkage long sys_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函数,从用户空间的内存地址拷贝参数到内核空间 */
- if (copy_from_user(a, args, nargs[call]))
- return -EFAULT;
- err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
- if (err)
- return err;
- 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_socketcall会通过call这个参数来判断执行哪个socket动作。
因为我们调用的是socket()系统调用,所以会执行sys_socket()这个函数。
4.内核调用sys_socket()来处理创建socket的系统调用。
文件net/socket.c- asmlinkage long sys_socket(int family, int type, int protocol)
- {
- int retval;
- struct socket *sock;
- int flags;
- /* Check the SOCK_* constants for consistency. */
- BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
- BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
- BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
- BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);
- flags = type & ~SOCK_TYPE_MASK;
- if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
- return -EINVAL;
- type &= SOCK_TYPE_MASK;
- if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
- flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
- /* 实际创建socket */
- retval = sock_create(family, type, protocol, &sock);
- if (retval < 0)
- goto out;
- /* 把创建的socket映射到文件描述符 */
- retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
- if (retval < 0)
- goto out_release;
- out:
- /* It may be already another descriptor 8) Not kernel problem. */
- return retval;
- out_release:
- sock_release(sock);
- return retval;
- }
可以看到,实际创建socket的函数是sock_create(),接下来就让我们好好研究一下这个函数。
阅读(883) | 评论(0) | 转发(0) |