• 读《软件调试》第九章


    今日读了张银奎老师的《软件调试》,前面的CPU和硬件相关的部分离得比較远,所以从第九章操作系统读起,今天的读书笔记:

    9.2採集调试消息
    调试事件分为8种
    typedef enum _DBGKM_APINUMBER
    {
    DbgkmExceptionApi = 0, // 异常
    DbgkmCreateThreadApi = 1, // 创建线程
    DbgkmCreateProcessApi = 2, // 创建进程
    DbgkmExitThreadApi = 3, // 退出线程
    DbgkmExitProcessApi = 4, // 进程退出
    DbgkmLoadDllApi = 5, // 映射DLL
    DbgkmUnloadDllApi = 6, // 反映射DLL
    DbgkmErrorReportApi = 7, // 内部错误
    DbgkmMaxApiNumber = 8, // 这组常量的最大值
    } DBGKM_APINUMBER;


    9.2.2 进程和线程创建消息
    操作系统就支持向调试系统发送消息,这个我是没有想到的,详细步骤例如以下:
    创建用户态windows线程时,首先为线程建立必要的内核对象和数据结构,并分配栈(stack)空间,这些工作完毕后,
    该线程处于挂起状态(CREATE_SUSPEND), 而后进程管理器会通知环境子系统,环境子系统会作必要的设置和登记,最后
    进程管理器会调用PspUserThreadStartup例程,准备启动该线程。
    为了支持调试,PspUserThreadStartup总是会调用调试子系统的内核函数DbgkCreateThread,以便让调试子系统得到处理机会。


    DbgkCreateThread会检查新创建线程所在的进程是否正在被调试(依据DebugPort是否为空),假设不是,便马上返回,
    假设是,则会继续检查该进程的用户态执行时间(UserTime)是否为0,目的是推断该线程是否是进程中的第一个线程,假设是,
    则通过DbgkSendApiMessage()函数向DebugPort发送DbgkmCreateProcessApi消息,假设不是,
    则发送DbgkmCreateThreadApi消息。
    调试器收到的进程创建(CREATE_PROCESS_DEBUG_EVENT,值为3)和线程创建(CREATE_THREAD_DEBUG_EVENT,值为2)事件就是源于这两个消息。


    9.2.3 进程和线程退出消息 --- 与上面类似


    9.2.4 模块映射和反映射消息 
    DLL(Dynamic-link Library)是Windows中使用最多的技术之中的一个。如:
    Windows内核文件NTOSKRNL.EXE尽管是EXE后缀,事实上质是一个DLL;
    NTDLL.DLL是连接用户态和操作系统内核的桥梁,用户态代码通过它訪问内核服务;
    Windows子系统DLL(KERNEL32.DLL,ADVAPI32.DLL,USER32.DLL,GDI32.DLL)是Windows API的载体;


    观察进程中的DLL:
    1.执行notepad.exe
    2.启动VC6,通过Build>Start Debug>Attatch to Process...菜单弹出Attach Process对话框,然后选择notepad.
    3. 通过Debug>Modules...菜单弹出模块列表,便能够看到notepad进程中的DLL了。
    第二列是该模块在进程空间中的地址(虚拟地址,均小于0x80000000),可见这些模块都是位于用户空间中的。


    存在于多个进程空间中的DLL,是否会反复占用内存?
    否!当LoadLibrary()和LoadLibraryEx() API载入一个DLL时,会首先推断该DLL是否已经载入过,假设是,则不会反复载入,
    仅仅是将该DLL相应的内存页面映射(map)到目标进程的内存空间,并把该DLL的引用次数加1.
    当进程退出或调用FreeLibrary() API要卸载一个DLL时,Windows会从进程的虚拟内存空间中把该DLL的映射删除(unmap),
    并递减该DLL的引用次数,假设引用次数变为0,那么该DLL会被彻底移出内存。


    9.2.5 异常消息
    为了支持调试,系统会把被调试程序中发生的全部异常发送给调试器。
    内核中KiDispatchException函数是分发异常的枢纽,它会给每一个异常安排最多两轮被处理的机会,
    对于每一轮处理机会,它都会调用调试子系统的DbgkForwardException函数来通知调试子系统。


    总结:
    系统的进程管理器、内存管理器和异常分发函数会调用调试子系统的Dbgk採集例程,来向调试子系统通报调试消息,
    这些例程被调用后会依据当前进程的DebugPort字段来推断当前进程是否处于被调试状态。
    假设不是,便忽略这次调用,直接返回;
    假设是,便产生一个DBGKM_APIMSG结构,然后调用下一节将介绍的DbgkSendApiMessage函数来发送调试消息。

  • 相关阅读:
    HashMap底层实现原理及面试常见问题
    Java面试题:==运算符与equals方法的区别
    SpringBoot基础,Java配置(全注解配置)取代xml配置
    实战SpringBoot Admin
    Java Object类中toString方法的重写
    java题
    1.5 安全性测试(功能)
    1.4 容量测试
    压力测试/极限测试(可靠性)
    1.2 性能测试(效率)
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/3777902.html
Copyright © 2020-2023  润新知