内核对象的基本概念

Windows系统是非开源的,它提供给我们的接口是用户模式的,即User-Mode API。当我们调用某个API时,需要从用户模式切换到内核模式的I/O System Services API。例如我们调用Kernel32.dll中的CreateFile创建文件,最终将执行ntdll.dll中的系统服务NtCreateFile。

内核为我们创建的文件对象以内核级数据结构FILE_OBJECT存储管理,内核级文件信息数据结构包括FILE_BASIC_INFORMATION、FILE_STANDARD_INFORMATION等,但是系统提供给我们在用户模式下与这个文件对象交互的接口是一个文件句柄(HANDLE)。

内核对象和普通的数据结构间的最大区别在于其内部数据结构是隐藏的,我们无法(系统没有操作接口)直接读或改变对象内部的数据结构。这里的文件句柄实际是文件内核对象在内核分配的内存中的一个索引,我们执行后期的用户模式下的文件操作调用都需传入文件句柄参数,内核根据该句柄来查找(定位)我们要操作的对象。类似的Windows提供的创建线程的API—CreateThread创建的内核对象也是以句柄(HANDLE)的形式返回给用户,后期对该线程内核对象的操作都需提供该句柄参数。

因为内核对象的所有者是内核,而不是进程,所以何时撤销内核对象由内核决定,而内核做这个决定的依据就是该内核对象是否仍然被使用。那么如何判断内核对象是否被使用呢?何时释放回收该内核对象资源呢?这就引出了内核对象的使用计数(Usage Count)问题。

内核对象是进程内的资源,使用计数属性指明进程对特定内核对象的引用次数,当系统发现引用次数是 0 时,它就会自动关闭资源。事实上这种机制是很简单的,一个进程在第一次创建内核对象的时候,系统为进程分配内核对象资源,并将该内核对象的使用计数属性初始化为1;以后每次打开这个内核对象,系统就会将使用计数加 1,如果关闭它,系统将使用计数减1,减到 0 就说明进程对这个内核对象的所有引用都已关闭,系统应该释放此内核对象资源。

我们编写程序都是在用户模式下进行的,要做内核级调试或者观察内部数据结构,需要下载系统符号(Symbols)。因为我们无法真正去操作其内部数据结构,又无内核源码,所以使用Windbg等工具一窥Windows系统内幕。结合具体内核对象的属性,通过观察其内部数据结构,感知其内核机制及操作流程。

 

进程和线程的基本概念

进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行过程,是系统进行资源分配和调度的独立单位。进程是由进程控制块(PCB)、程序段、数据段三部分组成。其中进程控制块是存放进程管理和控制信息的数据结构,是进程存在的唯一标志。

“进程是一个正在运行的程序,它拥有自己的虚拟地址空间,拥有自己的代码、数据和其他系统资源,如进程创建的文件、管道、同步对象等。一个进程也包含了一个或者多个运行在此进程内的线程。”

进程是执行程序的实例。例如,当你运行记事本程序notepad.exe时,你就创建了一个用来容纳组成notepad.exe的代码及其所需调用动态链接库的进程。每个进程均运行在其专用且受保护的地址空间内。因此,如果你同时运行记事本的两个拷贝,该程序正在使用的数据在各自实例中是彼此独立的。在记事本的一个拷贝中将无法看到该程序的第二个实例打开的数据。

在以上语境中,存储在磁盘上的notepad.exe程序是一连串静态的指令,而进程则是一个容器,它包含了一系列运行在这个程序实例上下文中的线程使用的资源。进程是不活泼的。一个进程要完成任何事情,必须有一个运行在它的地址空间上的线程。此线程负责执行该进程地址空间的代码。每个进程至少拥有一个在它的地址空间中运行的线程。对一个不包含任何线程的进程来说,它是没有理由继续存在下去的,系统会自动地销毁此进程和它的地址空间。

线程是进程内执行代码的独立实体。线程(Thread)是进程内的一个独立执行单元,是CPU调度和分派的基本单位。一个线程就是运行在一个进程上下文中的一个逻辑流,它描述了进程内代码的执行路径。每个线程都有自己的线程上下文(Context),包括唯一的整数线程id,栈(Stack),栈指针(Stack Pointer),程序计数器(Program Counter),通用目的寄存器。所有运行在一个进程中的线程共享该进程的整个虚拟地址空间。

线程内核对象(Thread Kernel Object)和线程上下文(Thread Context)等概念,参考《线程的数据结构》。在WinDbg中,可通过lkd> dt nt!_kthread查看线程内核对象数据结构;通过lkd> dt nt!_teb查看线程TEB数据结构;通过lkd> dt nt!_context查看线程上下文数据结构。

抛开线程实体,进程中的程序代码是不可能执行的。操作系统创建进程后,会创建一个线程执行进程中的代码。通常我们把这个线程称为该进程的主线程,主线程在运行过程中可能会创建其他线程。一般将主线程创建的线程称为该进程的辅助线程

从从属关系的角度来讲,线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间。一个进程可以包含若干线程,线程可以帮助应用程序同时做几件事(比如一个线程向磁盘写入文件,另一个则接收用户的按键操作并及时做出反应,互相不干扰),在程序被运行后中,系统首先要做的就是为该程序进程建立一个默认线程,然后程序可以根据需要自行添加或删除相关的线程。

线程不像进程按照严格的父子关系来组织。和一个进程相关的线程组成一个对等线程池,一个线程可以杀死其任意对等线程。每个线程都能读写相同的共享数据。当进程退出时该进程所产生的线程都会被强制退出并清除。

Win32多线程架构

主线程在运行过程中可以创建新的辅助线程,即所谓的多线程。多线程较之多进程的优点在于,线程的上下文要比进程的上下文小的多,所以线程的上下文切换要比进程的上下文切换快得多。

进程的主线程的进入点为main函数,辅助线程的进入点为线程函数(Thread Procedure)。

进程中同时可以有多个线程在执行,为了使它们能够“同时”运行,操作系统为每个线程轮流分配 CPU时间片。为了充分地利用 CPU,提高软件产品的性能,一般情况下,Win32基于窗口的GUI应用程序使用主线程接受用户的输入,显示运行结果,而创建新的线程(称为辅助线程)来处理长时间的操作,比如读写文件、访问网络等。这样,即便是在程序忙于繁重的工作时也可以由专门的线程响应用户命令。

换句话说,程序的主线程是一个老板,而其它线程是老板的职员。老板将繁重的工作丢给职员处理,而他自己保持和外界的联系。因为那些线程仅仅是职员,所以它们不会举行记者招待会,它们会认真地完成分内职务,将结果报告给老板,并等待他们的下一个任务。而对外的一切事务谈判都交由老板等管理层交涉。

一个程序中的线程是同一程序的不同部分,因此他们共享程序的资源,如内存和打开的文件。因为线程共享程序的内存,所以他们还共享静态变量。然而,每个线程都有他们自己的堆栈,因此动态变量(自动变量)对每个线程是唯一的。每个线程还有各自的处理器状态(和数学协处理器状态),这个状态在进行线程切换期间被储存和恢复,也即所谓的上下文切换。

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。理论上,安装了N个CPU的PC,在某一时刻,系统底层所能并发执行的线程个数为N。对于单核PC,多线程微观串行,如果开辟的线程过多,则频繁的线程上下文切换将会耗费较多的CPU时钟周期。因此,多线程并不是多多益善,这便涉及到多线程的池化管理问题。

Win32线程消息队列

与基于MS - DOS的应用程序不同,Windows的应用程序是事件(消息)驱动的。它们不会显式地调用函数(如C运行时库调用)来获取输入,而是等待windows向它们传递输入。 windows系统把应用程序的输入事件传递给各个窗口,每个窗口有一个函数,称为窗口消息处理函数。窗口消息处理函数处理各种用户输入,处理完成后再将控制权交还给系统。窗口消息处理函数一般是在注册一个窗口的时候指定的。

在Windows NT和Windows 98中,没有消息队列线程和无消息队列线程的区别,每个线程在建立时都会有它自己的消息队列,可通过lkd> dt nt!_kthread查看其中的_KTHREAD::_KQUEUE*对象Queue。应用程序调用GetMessage/PeekMessage函数从调用线程消息队列中取出指定窗口(HWND)的消息,调用SendMessage/PostMessage函数向调用线程消息队列中压入指定窗口(HWND)的消息。

书籍参考:

《Windows 2000系统编程》

《Windows核心编程》

《Win32多线程程序设计》

《C++面向对象多线程编程》

专题参考:

《Windows进程/线程浅谈》

《架构设计:进程还是线程?》

《C++多线程》

《VC多线程编程》

《从单线程到多线程》

《Windows多线程编程总结》

《深入浅出Win32多线程程序设计》

《Multithreaded Programming with ThreadMentor》

《Chrome源码剖析[1] - Chrome的多线程模型》

《Chrome源码剖析[2] - Chrome的进程间通信》

《Chrome源码剖析[3] - Chrome的进程模型》

Win32多线程编程(1) — 基础概念篇相关推荐

  1. 【软件测试】基础-概念篇

    软件测试基础-概念篇 记录 - 慕课网 imooc 软件测试基础-概念篇 简介:系统介绍什么是软件测试,从软件测试的定义.原则以及测试阶段.测试模式.测试手段和测试类型分别详细说明软件测试中的各种测试 ...

  2. Java基础了解-12-网络编程/发送邮件/多线程编程/Applet 基础/文档注释

    @ 网络编程/发送邮件/多线程编程/Applet 基础/文档注释 一.Java 网络编程 网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.net 包中 J2SE ...

  3. java多线程编程_Java多线程编程实战指南+设计模式篇.pdf

    Java多线程编程实战指南+设计模式篇.pdf 对Java架构技术感兴趣的工程师朋友们可以关注我,转发此文后私信我"Java"获取更多Java编程PDF资料(附送视频精讲) 关注我 ...

  4. C++面试题(一)——基础概念篇

    https://blog.csdn.net/worldwindjp/article/details/18909079 C++面试题--基础概念篇 唐璐 http://blog.csdn.net/wor ...

  5. 模型推理部署——基础概念篇

    模型推理部署--基础概念篇 训练(training)vs推理(inference) 训练是通过从已有的数据中学习到某种能力,而推理是简化并使用该能力,使其能快速.高效地对未知的数据进行操作,以获得预期 ...

  6. Win32多线程编程(3) — 线程同步与通信

    一.线程间数据通信 系统从进程的地址空间中分配内存给线程栈使用.新线程与创建它的线程在相同的进程上下文中运行.因此,新线程可以访问进程内核对象的所有句柄.进程中的所有内存以及同一个进程中其他所有线程的 ...

  7. Win32多线程编程(2) — 线程控制

    Win32线程控制只有是围绕线程这一内核对象的创建.挂起.恢复.终结以及通信等操作,这些操作都依赖于Win32操作系统提供的一组API和具体编译器的C运行时库函数.本篇围绕这些操作接口介绍在Windo ...

  8. java多线程编程01---------基本概念

    一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...

  9. Java多线程编程实战指南+设计模式篇pdf

    下载地址:网盘下载 随着CPU 多核时代的到来,多线程编程在充分利用计算资源.提高软件服务质量方面扮演了越来越重要的角色.而 解决多线程编程中频繁出现的普遍问题可以借鉴设计模式所提供的现成解决方案.然 ...

最新文章

  1. 机器学习、数据科学、人工智能、深度学习和统计学之间的区别!
  2. Springboot项目中的favicon
  3. IO之流程与buffer概览
  4. IBM在欧洲启动“智能云”研发计划
  5. Linux基础练习题(二)
  6. CSS块元素水平垂直居中的实现技巧
  7. vector, list, map在遍历时删除符合条件的元素
  8. Node.js npm 详解
  9. java 数组 length 减少_java中数组有没有length()方法?string没有lenght()方法?
  10. python扩展,用python扩展列
  11. mysql集群如何保障数据分布均匀_如何保证数据库集群时候,主从库一致性的问题?...
  12. 龙芯2F笔记本安装gentoo系统
  13. Python常用模块-20个常用模块总结
  14. 省赛 和 南京邀请赛总结
  15. ubuntu 完整学习资料
  16. 超级计算机有什么特点,计算机的特点有哪些
  17. OpenCV: Mat属性step,size,step1,elemSize,elemSize1一次搞清
  18. 文件误删秒恢复!微软又发布了一款命令行神器!
  19. 碳交易计价结算货币:理论、现实与选择
  20. Linux安装和使用

热门文章

  1. 方法级别权限控制-基本介绍与JSR250注解使用
  2. 部门微服务:基本dao和service代码编写
  3. 入门案例中使用的组件介绍
  4. 文本编码-Python2.x处理中文字符串
  5. SpringBoot_日志-SpringBoot日志关系
  6. Spring Session实战4
  7. 什么决定了电商双11大促的成败
  8. FastDFS在项目中的应用
  9. JVM--字节码执行引擎
  10. mysql linux-syn25000是什么_当MySQL数据库遇到Syn Flooding