Socket与内核调用深度分析

------------恢复内容开始------------

1 概念

Linux的设计哲学之一就是:对不同的操作赋予不同的执行等级,就是所谓特权的概念,即与系统相关的一些特别关键的操作必须由最高特权的程序来完成。
Intel的X86架构的CPU提供了0到3四个特权级,数字越小,特权越高,Linux操作系统中主要采用了0和3两个特权级,分别对应的就是内核态(Kernel Mode)用户态(User Mode)

  • 内核态:CPU可以访问内存所有数据,包括外围设备(硬盘、网卡),CPU也可以将自己从一个程序切换到另一个程序;
  • 用户态:只能受限的访问内存,且不允许访问外围设备,占用CPU的能力被剥夺,CPU资源可以被其他程序获取;

Linux中任何一个用户进程被创建时都包含2个栈:内核栈,用户栈,并且是进程私有的,从用户态开始运行。内核态和用户态分别对应内核空间与用户空间,内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。

2 内核空间相关

  • 内核空间:存放的是内核代码和数据,处于虚拟空间;
  • 内核态:当进程执行系统调用而进入内核代码中执行时,称进程处于内核态,此时CPU处于特权级最高的0级内核代码中执行,当进程处于内核态时,执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈;
  • CPU堆栈指针寄存器指向:内核栈地址;
  • 内核栈:进程处于内核态时使用的栈,存在于内核空间;
  • 处于内核态进程的权利:处于内核态的进程,当它占有CPU的时候,可以访问内存所有数据和所有外设,比如硬盘,网卡等等;

3 用户空间相关

  • 用户空间:存放的是用户程序的代码和数据,处于虚拟空间;
  • 用户态:当进程在执行用户自己的代码(非系统调用之类的函数)时,则称其处于用户态,CPU在特权级最低的3级用户代码中运行,当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核态,因为中断处理程序将使用当前进程的内核栈;
  • CPU堆栈指针寄存器指向:用户堆栈地址;
  • 用户堆栈:进程处于用户态时使用的堆栈,存在于用户空间;
  • 处于用户态进程的权利:处于用户态的进程,当它占有CPU的时候,只可以访问有限的内存,而且不允许访问外设,这里说的有限的内存其实就是用户空间,使用的是用户堆栈;

4 内核态和用户态的切换

(1)系统调用

所有用户程序都是运行在用户态的,但是有时候程序确实需要做一些内核态的事情,例如从硬盘读取数据等。而唯一可以做这些事情的就是操作系统,所以此时程序就需要先操作系统请求以程序的名义来执行这些操作。这时需要一个这样的机制:用户态程序切换到内核态,但是不能控制在内核态中执行的指令。这种机制叫系统调用,在CPU中的实现称之为陷阱指令(Trap
Instruction)。

(2)异常事件

当CPU正在执行运行在用户态的程序时,突然发生某些预先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的异常事件,典型的如缺页异常。

(3)外围设备的中断

当外围设备完成用户的请求操作后,会像CPU发出中断信号,此时,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。

注意:系统调用的本质其实也是中断,相对于外围设备的硬中断,这种中断称为软中断,这是操作系统为用户特别开放的一种中断,如Linux int
80h中断。所以从触发方式和效果上来看,这三种切换方式是完全一样的,都相当于是执行了一个中断响应的过程。但是从触发的对象来看,系统调用是进程主动请求切换的,而异常和硬中断则是被动的。

总而言之 我们可以用下面这个图来概括他们之间的关系:

5 跟踪分析Linux/Socket系统调用(以listen函数为例)

我们首先以最常用的listen()函数为例进行分析:

进入上次的MenuOS目录,更改makefile,将-S参数去除。(否则将会挂起CPU)

之后在终端编译:

make rootfs

如图所示:

同上次实验一样,给__sys_linsten打上断点:

gdb
file ./vmlinux
target remote:1234
break __sys_listen

如图所示,我们到socket.c中查看该函数的相关定义:

int __sys_listen(int fd, int backlog)
{
    struct socket *sock;
    int err, fput_needed;
    int somaxconn;

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
        somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;

        if ((unsigned int)backlog > somaxconn)
            backlog = somaxconn;
        err = security_socket_listen(sock, backlog);
        if (!err)
            err = sock->ops->listen(sock, backlog);

        fput_light(sock->file, fput_needed);
    }
    return err;
}

可见该函数用套接字队列长度来判断能不能继续监听一个端口。

SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
    return __sys_listen(fd, backlog);
}

由上可见这里将sys_listen定义为了一个内核处理函数。

我们可以在replyhi中尝试对DEFINE2函数打断点,看看它运行了多少次:

可见其在replyhi程序中运行了4次。

6 跟踪分析其他的程序系统调用

在分析系统调用时,我们常用gdb starce来进行分析。

strace常用来跟踪进程执行时的系统调用和所接收的信号,调试应用程序的时候经常使用。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。

strace用法:

-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column 

如果你已经知道你要找什么,你可以让strace只跟踪一些类型的系统调用。例如,你需要看看在configure脚本里面执行的程序,你需要监视的系统调 用就是execve。让strace只记录execve的调用用这个命令:

strace -f -o configure-strace.txt -e execve ./configure

------------恢复内容结束------------

原文地址:https://www.cnblogs.com/z501938568/p/12067056.html

时间: 2024-10-06 08:17:49

Socket与内核调用深度分析的相关文章

Linux内核链表深度分析【转】

本文转载自:http://blog.csdn.net/coding__madman/article/details/51325646 链表简介: 链表是一种常用的数据结构,它通过指针将一系列数据节点连接成一条数据链.相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或者删除数据.链表的开销主要是访问的顺序性和组织链的空间损失. 内核链表的好主要体现为两点,1是可扩展性,2是封装.可扩展性肯定是必须的,内核一直都是在发展中的,所

Linux内核链表深度分析

链表简介: 链表是一种常用的数据结构,它通过指针将一系列数据节点连接成一条数据链.相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或者删除数据.链表的开销主要是访问的顺序性和组织链的空间损失. 内核链表的好主要体现为两点,1是可扩展性,2是封装.可扩展性肯定是必须的,内核一直都是在发展中的,所以代码都不能写成死代码,要方便修改和追加.将链表常见的操作都进行封装,使用者只关注接口,不需关注实现.分析内核中的链表我们 可以做些什

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

android内核剖析系列---JNI调用机制分析

为什么需要JNI? android这个庞大的系统从下到上主要由linux内核,C/C++库,java应用程序框架,java应用程序组成.这就涉及到一个问题,C/C++库如何与java应用有交集,或者说能相互调用,要解决这个问题,就需要JNI登场了. JNI调用机制分析 JNI--java native interface,翻译成中文是java本地接口,所谓的"本地"是指C/C++库一层的C/C++语言(以下统称C).

Linux tcp被动打开内核源码分析

[我是从2个角度来看,其实所谓2个角度,是发现我分析源码时,分析重复了,写了2个分析报告,所以现在都贴出来.] [如果你是想看看,了解一下内核tcp被动打开时如何实现的话,我觉得还是看看概念就可以了,因为即使看了源码,过一个个礼拜你就忘了,如果是你正在修改协议栈,为不知道流程而发愁,那么希望你能看看源码以及注释,希望你给你帮助.] 概念: tcp被动打开,前提是你listen,这个被动打开的前提.你listen过后,其实创建了一个监听套接字,专门负责监听,不会负责传输数据. 当第一个syn包到达

深度分析LINUX环境下如何配置multi-path

首先介绍一下什么是多路径(multi-path)?先说说多路径功能产生的背景,在多路径功能出现之前,主机上的硬盘是直接挂接到一个总线(PCI)上,路径是一对一的关系,也就是一条路径指向一个硬盘或是存储设备,这样的一对一关系对于操作系统而言,处理相对简单,但是缺少了可靠性.当出现了光纤通道网络(Fibre Channle)也就是通常所说的SAN网络时,或者由iSCSI组成的IPSAN环境时,由于主机和存储之间通过光纤通道交换机或者多块网卡及IP来连接时,构成了多对多关系的IO通道,也就是说一台主机

深度分析:Android中Mms设置页面更改短信中心号码流程

相关控件初始化方法:showSmscPref private void showSmscPref() { int count = MSimTelephonyManager.getDefault().getPhoneCount(); boolean airplaneModeOn = Settings.System.getInt(getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) != 0; for (int i = 0; i < c