Socket与系统调用深度分析
系统调用
在一开始,应用程序是可以直接控制硬件的,这就需要程序员有很高的编程能力,否则一旦程序出了问题,会将整个系统Crash。
在现在的操作系统中,用户程序运行在用户态,而要进行诸如Socket
、磁盘I/O
这样的一些操作,这需要切换到内核态,再进行进行相应的操作,而这一过程则是系统调用system call
。有了操作系统分离了内核和用户态,应用程序就无法直接进行硬件资源的访问,需要经过系统调用来进行。
每次的系统调用,都会从用户态转换到内核态,运行完任务后,回到用户态。这中间的过程需要上下文切换(保存寄存器信息),也就是切换状态是需要消耗资源和时间的。
Socket
Socket是程序实现端到端通信的地址,互联网中每台设备有自己的IP地址,但每台设备上运行着许多程序。同时不同的程序都可能有通信的需求,这就需要一个套接字(Socket)来区分不同的程序。
一个套接字由IP地址和端口号组成。
IP address: port
实验
开启上次实验编译好的MenuOS系统
上次实验编译了一个带调试功能,且带有TCP服务器和客户端的MenuOS系统
进入LinuxKernel目录,启动虚拟机。
[email protected]:~$ cd LinuxKernel
[email protected]:~/LinuxKernel$ qemu-system-i386 -kernel linux-5.4.2/arch/x86/boot/bzImage -initrd rootfs.img -append "root=/dev/sda init=/init nokaslr" -s -S
进入调试
这时候虚拟机进入停止在一个黑屏界面,等待gdb的接入和下一步指令。
新开一个终端窗口,进入gdb调试。
接着分别
- 导入符号表
- 连接调试服务器
- 设置断点
[email protected]:~/LinuxKernel$ gdb
(gdb) file ~/LinuxKernel/linux-5.4.2/vmlinux
Reading symbols from ~/LinuxKernel/linux-5.4.2/vmlinux...done.
(gdb) target remote:1234
Remote debugging using :1234
0x0000fff0 in ?? ()
(gdb) break start_kernel
Breakpoint 1 at 0xc1db5885: file init/main.c, line 576.
然后输入c
让系统继续执行,执行到断点start_kernel ()
则说明成功。
(gdb) c
Continuing.
Breakpoint 1, start_kernel () at init/main.c:576
576 {
添加新断点sys_bind
, sys_listen
, sys_socketcall
(gdb) break sys_bind
Breakpoint 2 at 0xc179beb0: file net/socket.c, line 1656.
(gdb) break sys_listen
Breakpoint 3 at 0xc179bf60: file net/socket.c, line 1688.
(gdb) break sys_socketcall
Breakpoint 4 at 0xc179ce00: file net/socket.c, line 2818.
c
让系统继续执行
(gdb) c
Continuing.
Breakpoint 4, __se_sys_socketcall (call=1, args=-1075909056) at net/socket.c:2818
2818 SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
系统进入sys_socketcall
断点,查看虚拟机窗口,可以看到虚拟机正在启动本地换回接口
再次继续执行,可以看到总共进了三次sys_socketcall
,另外两次分别是启动以太网口和建立连接,这个在上周就已经发现了。
接着成功进入系统,可以查看之前编译进系统的TCP服务端和客户端
在MenuOS中输入replyhi
启动TCP服务端
可以在gdb中发现再次进入sys_socketcall
断点
Breakpoint 4, __se_sys_socketcall (call=1, args=-1075910240) at net/socket.c:2818
2818 SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
可以看到,断点停在__se_sys_socketcall
中,位置是net/socket.c
的2818行,找到函数如下:
2818 SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
2819 {
2820 unsigned long a[AUDITSC_ARGS];
2821 unsigned long a0, a1;
2822 int err;
2823 unsigned int len;
2824
2825 if (call < 1 || call > SYS_SENDMMSG)
2826 return -EINVAL;
2827 call = array_index_nospec(call, SYS_SENDMMSG + 1);
2828
2829 len = nargs[call];
2830 if (len > sizeof(a))
2831 return -EINVAL;
2832
2833 /* copy_from_user should be SMP safe. */
2834 if (copy_from_user(a, args, len))
2835 return -EFAULT;
2836
2837 err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
2838 if (err)
2839 return err;
2840
2841 a0 = a[0];
2842 a1 = a[1];
2843
2844 switch (call) {
2845 case SYS_SOCKET:
2846 err = __sys_socket(a0, a1, a[2]);
2847 break;
2848 case SYS_BIND:
2849 err = __sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
2850 break;
2851 case SYS_CONNECT:
2852 err = __sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
2853 break;
2854 case SYS_LISTEN:
2855 err = __sys_listen(a0, a1);
2856 break;
2857 case SYS_ACCEPT:
2858 err = __sys_accept4(a0, (struct sockaddr __user *)a1,
2859 (int __user *)a[2], 0);
2860 break;
2861 ...
2930 default:
2931 err = -EINVAL;
2932 break;
2933 }
2934 return err;
2935 }
函数内部主体是一个switch
语句,根据我们的call
参数来进行选择,通过gdb我们可以看到,这时(call=1, args=-1075910240)
,具体要根据1是哪个case
来追踪调用。
而这些case
的定义并不在socket.c
中。我们可以在/LinuxKernel/linux-5.4.2/include/linux
下找到socket.h
文件
#define SYS_SOCKET??? 1??? ??? /* sys_socket(2)??? ??? */
#define SYS_BIND??? 2??? ??? /* sys_bind(2)??? ??? ??? */
#define SYS_CONNECT??? 3??? ??? /* sys_connect(2)??? ??? */
#define SYS_LISTEN??? 4??? ??? /* sys_listen(2)??? ??? */
#define SYS_ACCEPT??? 5??? ??? /* sys_accept(2)??? ??? */
#define SYS_GETSOCKNAME??? 6??? ??? /* sys_getsockname(2)??? ??? */
#define SYS_GETPEERNAME??? 7??? ??? /* sys_getpeername(2)??? ??? */
#define SYS_SOCKETPAIR??? 8??? ??? /* sys_socketpair(2)??? ??? */
#define SYS_SEND??? 9??? ??? /* sys_send(2)??? ??? ??? */
#define SYS_RECV??? 10??? ??? /* sys_recv(2)??? ??? ??? */
#define SYS_SENDTO??? 11??? ??? /* sys_sendto(2)??? ??? */
#define SYS_RECVFROM??? 12??? ??? /* sys_recvfrom(2)??? ??? */
#define SYS_SHUTDOWN??? 13??? ??? /* sys_shutdown(2)??? ??? */
#define SYS_SETSOCKOPT??? 14??? ??? /* sys_setsockopt(2)??? ??? */
#define SYS_GETSOCKOPT??? 15??? ??? /* sys_getsockopt(2)??? ??? */
#define SYS_SENDMSG??? 16??? ??? /* sys_sendmsg(2)??? ??? */
#define SYS_RECVMSG??? 17??? ??? /* sys_recvmsg(2)??? ??? */
所以实质则是进入了__sys_socket(a0, a1, a[2]);
函数内。
原文地址:https://www.cnblogs.com/Axi8/p/12070578.html