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

1.Socket与系统调用——概述

Socket API编程接口之上可以编写基于不同网络协议的应用程序;

Socket接口在用户态通过系统调用机制进入内核;

内核中将系统调用作为一个特殊的中断来处理,以socket相关系统调用为例进行分析;

socket相关系统调用的内核处理函数内部通过“多态机制”对不同的网络协议进行的封装方法;

下面会将Socket API编程接口系统调用机制内核中系统调用相关源代码、 socket相关系统调用的内核处理函数结合起来分析,并在X86 64环境下Linux5.0以上的内核中进一步跟踪验证。

至于QEMU搭建X86 64位实验环境,可以参考上一篇博文https://www.cnblogs.com/qfdzztt/p/12018425.html

2.系统调用机制以及内核中相关源代码

socket系统调用发生流程(如下图所示)

当用户进程使用socket API 的时候,会产生向量为0x80的编程异常,系统执行系统调用。

进程传递系统调用号到寄存器eax,指明需要哪个系统调用,同时会将系统调用需要的参数存入相关寄存器。

系统调用处理函数system_call是Linux中所有系统调用的入口点,通过进程存在eax寄存器中的系统调用号决定调用哪个系统调用。

其中,socket api有两种系统调用方式:(1)所有的socket系统调用的总入口是sys_socketcall(系统调用号102)  (2)每一个独立的socket api都对应一个单独的系统调用。

系统调用初始化

系统调用的初始化过程为:start_kernel --> trap_init --> cpu_init --> syscall_init。主要分为两步,第一步是中断初始化,第二步是系统调用初始化

当产生向量为0x80的编程异常时,系统怎么就知道要执行中断处理函数system_call呢?那是因为在初始化内核的时候,会执行中断初始化函数trap_init,此函数拷贝中断异常向量表到指定位置,系统就能根据中断向量号跳转到对应的中断处理。而系统调用对应的中断是软件中断,其向量号为0x80。在第一步中,通过将软件中断的处理程序system_call(entry_SYSCALL_64)与0x80绑定,所以执行int 0x80时系统就会跳转到中断处理函数system_call。

其次,中断处理函数还需要根据系统调用号来执行相应的系统调用,这就需要初始化系统调用,将系统调用中断向量与服务例程绑定。内核维护一张系统调用表system call table,系统调用表是Linux内核源码文件 arch/x86/entry/syscall_64.c中定义的数组sys_call_table的对应。在第二步中,cpu_init函数调用syscall_init完成per-cpu状态初始化。该函数执行系统调用入口的初始化,该函数没有参数且首先填充两个特殊模块寄存器:

第一个特殊模块集寄存器- MSR_STAR 的 63:48 为用户代码的代码段。这些数据将加载至 CS 和 SS 段选择符,由提供将系统调用返回至相应特权级的用户代码功能的 sysret 指令使用。 同时从内核代码来看, 当用户空间应用程序执行系统调用时,MSR_STAR 的 47:32 将作为 CS and SS段选择寄存器的基地址。

第二行代码中我们将使用系统调用入口entry_SYSCALL_64 填充 MSR_LSTAR 寄存器。 entry_SYSCALL_64 在arch/x86/entry/syscall_64.S汇编文件中定义,包含系统调用执行前的准备。

void syscall_init(void)
{
    wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
    wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
  ...

用gdbt调试,给start_kernel ,trap_init , cpu_init ,syscall_init几个函数增加断点验证初始化过程。

系统调用执行

用户态程序发起系统调用,对于x86-64位程序应该是直接跳到entry_SYSCALL_64,,在do_syscall_64中根据系统调用号执行对应的系统调用。

SYM_CODE_START(entry_SYSCALL_64)
...
    /* IRQs are off. */
    movq    %rax, %rdi
    movq    %rsp, %rsi
    call    do_syscall_64        /* returns with IRQs disabled */
* [do_syscall_64](https://github.com/torvalds/linux/blob/ab851d49f6bfc781edd8bd44c72ec1e49211670b/arch/x86/entry/common.c#L282)

#ifdef CONFIG_X86_64

__visible void do_syscall_64(unsigned long nr, struct pt_regs* regs)

{

    struct thread_info* ti;

    enter_from_user_mode();

    local_irq_enable();

    ti = current_thread_info();

    if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY)

        nr = syscall_trace_enter(regs);

    if (likely(nr < NR_syscalls)) {

        nr = array_index_nospec(nr, NR_syscalls);

        regs->ax = sys_call_table[nr](regs);

#ifdef CONFIG_X86_X32_ABI

    }    else if (likely((nr & __X32_SYSCALL_BIT) &&

        (nr & ~__X32_SYSCALL_BIT) < X32_NR_syscalls)) {

        nr = array_index_nospec(nr & ~__X32_SYSCALL_BIT,

            X32_NR_syscalls);

        regs->ax = x32_sys_call_table[nr](regs);

#endif

    }

    syscall_return_slowpath(regs);

}

#endif

3.跟踪socket相关系统调用内核处理函数

打开gdb调试,将与socket相关的函数们都打上断点

在本体系结构中,函数们如下:

输入c,继续,发现程序没到第一个断点就停住了,在Qemu中输入replyhi,不停地按回车,发现捕获到如下断点,一直到sys_accept4函数停止。说明此时服务器处于阻塞状态,一直在等待客户端连接。

在Qemu中输入hello,输入c,继续按回车继续,捕获断点,可以看到客户端发起连接,发送接收数据。

追踪完毕,结果如下图:

原文地址:https://www.cnblogs.com/qfdzztt/p/12057457.html

时间: 2024-10-14 16:53:43

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

Socket与系统调用深度分析

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

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

Socket与系统调用深度分析 系统调用 在一开始,应用程序是可以直接控制硬件的,这就需要程序员有很高的编程能力,否则一旦程序出了问题,会将整个系统Crash. 在现在的操作系统中,用户程序运行在用户态,而要进行诸如Socket.磁盘I/O这样的一些操作,这需要切换到内核态,再进行进行相应的操作,而这一过程则是系统调用system call.有了操作系统分离了内核和用户态,应用程序就无法直接进行硬件资源的访问,需要经过系统调用来进行. 每次的系统调用,都会从用户态转换到内核态,运行完任务后,回到

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相关系

基于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

四步法分析定位生产环境下MySQL上千条SQL中的问题所在

第一步:通过以下两种方式之一来打开慢查询功能 (1)方式一:通过修改mysql的my.cnf文件 如果是5.0或5.1等版本需要增加以下选项: log-slow-queries="mysql_slow_query.log" 如果是5.5版本以上可以增加如下选项: slow-query-log=On slow_query_log_file="mysql_slow_query.log" log-query-not-using-indexes 但是以上修改mysql配置文

[转载]Linux 环境下编译 0.11版本内核 kernel

最近在看<linux内 核0.11完全注释>一书,由于书中涉及汇编语言的地方众多,本人在大学时汇编语言学得一塌糊涂,所以实在看不下去了,头都大了只好匆匆看了个头尾(前面 几章和最后一章).看来即使有<九阴真经>这样的武功秘籍,内功不够也是修炼不出来神马来的.于是索性下了个0.11版本的kernel下来尝试编译一 把. linux-0.11.tar.gz 下载地址: 下面开始工作: 1. tar xvfz linux-0.11.tar.gz 2. cd linux-0.11 3. m

Hadoop 2.2.0 常见问题之:Ubuntu 64环境下“Unable to load native-hadoop library for your platform”问题”

问题 最近在学习Hadoop(2.2.0),打算写一个MapReduce的小程序在Ubuntu 64位的环境下测试一把,一切环境配置完毕后,执行的过程中,控制台输出下面的内容: WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable 原因 在网上搜索了一番,得出如下结论: "The reason

深度分析:Android4.3下MMS发送到附件为音频文件(音频为系统内置音频)的彩信给自己,添加音频-发送彩信-接收彩信-下载音频附件-预览-播放(二,发送彩信&lt;1&gt;)

当准备工作(添加附件,输入文本内容)完成之后,我们这里开始进行该流程分析的第二阶段,也就是发送彩信.这里我们从ComposeMessageActivity类的点击发送按钮(mSendButtonMms)的点击事件开始:<TAG 1-1> @Override public void onClick(View v) { if (mShowTwoButtons && (v == mSendButtonSmsViewSec || v == mSendButtonMmsViewSec)

socket系统调用深度分析

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