从零开始—Socket系统调用和多态封装
1 重新搭建实验环境
前面都是用实验楼环境做的实验,偷的懒总是要还的,这一次重装环境前后花了十几个小时,踩了无数的坑。
1.1 Ubuntu和LINUX内核的区别
Ubuntu是基于LINUX内核编写的一个操作系统。LINUX内核定义了一些基本的系统功能,Ubuntu在内核之上加入了图形界面,包管理等功能,优化了人机交互。本次实验,要求使用LINUX内核5.0以上,所以,在下载安装完Ubuntu系统后,需要对内核进行更新。
$ uname -a
上面这个指令会显示Ubuntu当前的内核版本,我们可以通过它来观察内核的升级是否成功。
1.2 从零开始
下载安装Ubuntu
首先到Ubuntu官网上下载一个Ubuntu镜像,但是太慢了,我们可以在国内的镜像网站上去下载。指路
反汇编init
对init
进行反汇编
$ objdump -d init > init_ASM.txt
查看init_ASM.txt
文件,在第104553行找到socket对应的系统调用。
证明对于socket api的调用是通过socketcall这个特殊中断来实现的。
syscall的具体实现
利用同样的办法,我们按照上一节的方法启动qemu进行远程调试,设置如下断点:
$ (gdb) break sys_socketcall
跟踪到一个关键函数:SYSCALL_DEFINE2()
,它位于linux-5.0.1/net/socket.c
之中。
关键代码如下:
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; // ... 省略其余部分 }
可见,每次socket都会调用同一个函数,通过传入的call值不同,在分支语句中执行对应的系统服务例程。
2.2 Socket封装网络协议的多态机制
以__sys_socket()
为例,其源码位于同一文件下,也是C语言实现的:
int __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))