【Socket系统调用】Socket与系统调用深度分析

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

时间: 2024-09-28 09:12:11

【Socket系统调用】Socket与系统调用深度分析的相关文章

Socket与系统调用深度分析

Socket与系统调用深度分析 可以想象的是,当应用程序调用socket()接口,请求操作系统提供服务时,必然会系统调用,内核根据发起系统调用时传递的系统调用号,判断要执行的程序,若为socket对应的编号,则执行socket对应的中断服务程序.服务程序内部,又根据你要请求的不同服务,来执行不同服务对应的处理程序.当处理结束,执行返回,从中断服务程序到发起中断的int 0x80,再到用户态我们执行的用户程序,层层返回,socket()也就执行完毕了. 本次,我们关心三个问题: 1.应用程序如何如

Socket与系统调用深度分析 ——X86 64环境下Linux5.0以上的内核中

1.Socket与系统调用——概述 Socket API编程接口之上可以编写基于不同网络协议的应用程序: Socket接口在用户态通过系统调用机制进入内核: 内核中将系统调用作为一个特殊的中断来处理,以socket相关系统调用为例进行分析: socket相关系统调用的内核处理函数内部通过“多态机制”对不同的网络协议进行的封装方法: 下面会将Socket API编程接口.系统调用机制及内核中系统调用相关源代码. socket相关系统调用的内核处理函数结合起来分析,并在X86 64环境下Linux5

Socket 与系统调用深度分析

一.实验环境准备 uname -a 在本机编译linux 5.0.1 X86-64内核,重新按照64位方式编译,步骤同上一篇博客. make x86_64_defconfig make menuconfig make #编译内核 二.Socket与系统调用 1.socket Socket API编程接口之上可以编写基于不同网络协议的应用程序: Socket接口在用户态通过系统调用机制进入内核: 内核中将系统调用作为一个特殊的中断来处理,以socket相关系统调用为例进行分析: socket相关系

socket系统调用深度分析

1.系统调用过程 1.1用户态和内核态以及系统调用机制 1.进程的地址空间 linux进程有4GB地址空间,如图所示: 3G-4G大部分是共享的,是内核态的地址空间.这里存放整个内核的代码和所有的内核模块以及内核所维护的数据. 2.特权级别 对于任何操作系统来说,创建一个进程是核心功能.创建进程要做很多工作,会消耗很多物理资源.比如分配物理内存,父子进程拷贝信息,拷贝设置页目录页表等等,这些工作得由特定的进程去做,所以就有了特权级别的概念.最关键的工作必须交给特权级最高的进程去执行,这样可以做到

基于x86-64 Linux-5.0.1的Socket与系统调用深度分析

一.Socket API编程接口 Libc库中定义的一些应用编程接口(Application Program Interface, API)引用了封装例程(Wrapper Routine),一般一个封装例程对应一个系统调用,大部分封装例程返回一个整数,其值含义依赖于相应的系统调用,-1在多数情况下表示内核不能满足进程的请求,Libc中定义的errno变量包含特定的出错码.C语言中的Socket API就是一种涉及系统调用的API,常用的函数如下: int socket(int domain, i

UDP socket也可以使用connect系统调用

UDP socket也可以使用connect系统调用 UDP是一个无连接的协议,因此socket函数connect()似乎对UDP是没有意义的,然而事实不是这样.它可以用来指定本地端口和本地地址,来建立虚拟连接. 一个socket接口有几个属性,其中包括协议,本地地址/端口,目的地址/端口. 对于UDP来说: socket()函数建立一个插口: bind()函数指明了本地地址/端口(包括ADDR_ANY, 通配所有本地网络接口): connect()可以用来指明目的地址/端口: 一般来说,UDP

linux内核中socket的创建过程源码分析(详细分析)

http://www.cnblogs.com/hyd-desert-camel/p/3536341.html 1三个相关数据结构. 关于socket的创建,首先需要分析socket这个结构体,这是整个的核心. 104 struct socket { 105         socket_state            state; 106 107         kmemcheck_bitfield_begin(type); 108         short                 

linux内核中socket的创建过程源码分析(总结性质)

http://www.jianshu.com/p/5d82a685b5b6 在漫长地分析完socket的创建源码后,发现一片浆糊,所以特此总结,我的博客中同时有另外一篇详细的源码分析,内核版本为3.9,建议在阅读本文后若还有兴趣再去看另外一篇博文.绝对不要单独看另外一篇. 一:调用链: 二:数据结构 一一看一下每个数据结构的意义: 1) socket, sock, inet_sock, tcp_sock的关系创建完sk变量后,回到inet_create函数中: 这里是根据sk变量得到inet_s

基于Linux C的socket抓包程序和Package分析 (一)

 测试运行平台:CentOS 6.5发行版,内核版本3.11 1. Linux抓包源程序 在OSI七层模型中,网卡工作在物理层和数据链路层的MAC子层. 进行网络通信时,源主机通过socket(或其它)应用程序产生IP报文,经过各个OSI层层封装,数据包以Ethernet帧的形式进入物理层.Ethernet帧包含源主机地址.IP报文.目标地址(IP地址.端口号或映射的6字节MAC地址)和需要传送到目标主机的其它信息. 目标的MAC地址是哪里来的呢?这牵扯到一个ARP协议(介乎于网络层和数据链