栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Debug调试原理 -- 用户态调试模型

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Debug调试原理 -- 用户态调试模型

bug总是存在的,不可能做到没有bug。

很多人和我一样,不知道debug的调试原理,只知道点击IDE的debug调试按钮进行调试程序。

我很好奇,java、C、python、js 这些编程语言是如何做到debug的?

于是乎就有了今天这篇文章


感谢 张银奎 老师录了《Windows 驱动开发 之 WinDbg调试》这套视频,在此之前我也不知道debug是怎么实现的,以前也没有想过这些问题。最近因为工作等一些原因,有幸看到了这套视频,很值得学习。

为此,我准备写一些博客记录一下这套视频中一些关键的内容。


在真正开始讲底层原理前,需要讲一些额外的内容作为补充。

一、用户态 和 内核态

众所周知,windows操作系统分为 用户态、内核态。
早期的操作系统并没有这些概念,而是让用户直接操作内存,会导致程序之间内存可能会出现互相覆盖,因此早期的系统不稳定,当程序覆盖到系统比较核心的内容时就会导致系统出现蓝屏等症状。

后来微软为了解决这个问题,将操作系统重新设计,依据用户和系统之间的角色将操作系统划分为2块空间。这种方式就避免了用户程序影响到系统程序。

当然系统之间的程序应该也存在互相影响的可能性,联想到早期windows的频繁更新,推测有一方面的原因在于解决系统程序之间互相影响的bug。
随着这几年使用windows,发现进几年windows的蓝屏等现象越来越少,会不会就是这个原因呢?

平时我们使用windows写的程序,都是在用户态跑的,对于windows一些高级的api,比如创建线程、阻塞线程、唤醒线程,这些操作肯定是在内核态完成的。那么这块内容也经常会在程序员面试的过程当作考题。

在写java程序的过程中,我也在思考,平时写过下面这些代码,通过Thread类创建一个线程

package top.yumbo.demo;

public class ThreadDemo {

    public static void main(String[] args) {
        new Thread(()->{
            System.out.println("hello Thread!");
        }).start();
    }
}

如果你是一个热爱技术的开发人员,相信你可能会点进去看下,内部是怎么做的,点进去后,你会发现代码原来是这样的,其中start0();则是我们要关注的,也是可以大作文章的。

    public synchronized void start() {
        
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        
        group.add(this);

        boolean started = false;
        try {
            start0();// 这个方法是关键点,调用了后面native方法
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                
            }
        }
    }
    private native void start0();

当你发现这个private native void start0();时,很多人就不知道怎么追踪下去了,实际上native就是由C实现的动态链接库,安装jdk的过程中肯定也看到过这些jdk目录中有一些dll文件。如果你对这个native方法好奇,自己也想尝试写一个native的方法试一下,可以看下我之前的文章《JNI 的hello world 案例》

实际上看到这里就得追踪到jdk源码中去,看下 Thread类对于这个start0它是如何实现的。一想到这里,很多人就停下了学习的脚步。但实际上很多时候,你都会遇到这些问题。在面试的过程中经常会问道并发,锁这些内容,尤其是synchronized,经常会问到底层。

谈到synchronized就嘿嘿一笑,因为这是一个关键字,对于一般人来说,这你让我怎么回答,只有看过相关资料的人才可以答上一二。实际上这里面的学问很多,要是继续追踪下去,很快就会到操作系统内核这些概念。


一句话总结上面内容:操作系统分用户态和内核态,我们跑的程序都在用户态执行,程序需要用到内核态的 api 时才会调用内核态完成相关的运算。
想要强调的一点是:用户态 和 内核态 的基础概念很重要


在知道windows系统分为用户态和内核态后,这个时候才可以介绍

二、Windows 用户态调试模型

这个调试模型在《Windows 驱动开发 之 WinDbg调试》第一集中也有介绍,感兴趣的小伙伴可以自行前往。

整个windows调试模型是基于调试事件驱动设计,有下面这些事件

EXCEPTION_DEBUG_EVENT 异常debug事件CREATE_THREAD_DEBUG_EVENT 创建线程debug事件CREATE_PROCESS_DEBUG_EVENT 创建进程debug事件EXIT_THREAD_DEBUG_EVENT 退出线程debug事件EXIT_PROCESS_DEBUG_EVENT 退出进程debug事件LOAD_DLL_DEBUG_EVENT 加载动态链接库debug事件UNLOAD_DLL_DEBUG_EVENT 卸载动态链接库debug事件OUTPUT_DEBUG_STRING_EVENT 输出debug内容事件 断点

可以理解为cpu调试指令,指令为 int3

在听 张银奎 老师介绍的过程中,为了理解这个模型,我自己也画一下这个用户态调试模型,查漏补缺。

用户态调试模型:

在写程序过程中经常会写

try{
}
catch(Exception e){
}

以及throws都由图中的3RaiseException 进行实现

在操作系统中异常来源于下面2种

CPU产生的异常

执行指令时检测到的错误,除0,GP,无效指令Machine Check Exceptions,总线错误,ECC错误,Cache错误预先埋伏的,int3,调试异常

程序产生的异常

RaiseException,Win32 APIC++,throw E,编译器翻译为RaiseException的调用C#,throw,最终任然由RaiseException

由此可见RaiseException相当重要!
我相信java也是调用系统api 与C++一样,毕竟跨进程调试,需要在内核态中完成

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/750635.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号