引言

本来在写一篇Windows内存管理的文章,写着写着就发现好多基础的概念都要先讲。更可怕的是,这些基础的概念我却不能完全讲清楚。只好再把这本《深入解析Windows操作系统》翻到第一章…… 很多书的第一章往往都会说:第一次读本章往往一知半解,建议学习后面的内容时隔段时间再回来看本章,会有更多的收获。这本书就是这样。

友情提示:结尾更精彩~

进程

尽管表面上看起来程序和进程非常类似,但本质上它们却是截然不同的。程序是一个静态的指令序列,而进程则是一个容器,其中包含了当执行一个程序的特定实例时所用到的各种资源。比如:Notepad是一个程序,运行这个程序便产生了进程。

从高层次的抽象来看,一个Windows进程是由以下元素构成的。

  • 一个私有的虚拟地址空间。(虚拟地址空间指的是一组虚拟内存地址的范围)
  • 一个可以执行的程序。它定义了初始的代码和数据,并且被映射到该进程的虚拟地址空间。即可执行映像。
  • 一个已打开句柄的列表,这些句柄指向各种系统资源。该进程内的所有线程都可以访问。
  • 一个被称为访问令牌的安全环境,它标识了与该进程关联的用户、安全组和特权。
  • 一个被称为进程ID的唯一标识符。
  • 至少一个执行线程。(第一个执行线程称为主线程)

进程、线程的定义很多,但像这本书这么直接了当的毫不抽象(貌似与上面某句话矛盾哈^_^)的定义倒很新颖。当然,说的这么细,人家就不一定能看懂了。

线程

线程是一个进程内部的实体,也是Windows执行此进程时的调度实体(抛开与进程的关系,线程是系统进行调度的单位)。如果没有线程,进程的程序不可能运行。

线程包括以下一些最基本的部件:

  • 一组代表处理器状态的CPU寄存器中的内容的备份;
  • 两个栈,一个用于当线程在内核模式下执行的时候,另一个用于在用户模式下执行时。可想而知,一个叫内核模式栈,一个叫用户模式栈。
  • 一个被称为线程局部存储区(TLS)的私有存储区域。
  • 一个被称为线程ID的唯一标识符(进程ID和线程ID在内部都叫客户ID,它们是在同一个名字空间中生成的,不可能重叠)。
  • 有时也有线程安全环境。牵扯到Windows的模仿机制。

易失的寄存器、栈、和局部存储区合起来被称为线程的上下文(context)。

虚拟内存

虚拟内存提供了一个内存的逻辑视图,它并不对应于物理内存的布局。在运行的时候,内存管理器借助于硬件的支持,将虚拟地址映射成物理地址。

每个进程都有自己的虚拟地址空间,而且它会感觉到自己独占了这个很大的地址空间。在32位x86系统中,总的虚拟地址空间的大小为4GB。因为32位指针可以表示0X00000000到0XFFFFFFFF之间的值。但是默认情况下,Windows会将2GB的地址空间给进程,作为其私有地址空间,称为用户模式空间;而另一半(地址空间中较高的一半,从0X80000000到0XFFFFFFFF)则用做它自己的受保护的内核使用,称为内核模式空间。

虚拟内存有三个好处:

  • 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的内存块。
  • 程序可以访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。页面会根据需要在物理内存与磁盘之间换入换出。
  • 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法访问正在由另一进程使用的物理内存。

内核模式和用户模式

CPU有不止一种特权级别,可以用来保护系统代码和数据不被低级别的代码修改。Windows就使用了CPU的两种模式,而将其称为用户模式和内核模式。用户程序代码运行在用户模式下,而操作系统代码运行在内核模式下。内核模式的权限更高:它允许访问所有的系统内存和执行所有的CPU指令。而只有操作系统的代码才能在内核模式执行,从而能防止恶意的应用程序破坏系统。

每个Windows进程都有它自己的私有地址空间,但内核模式的操作系统和设备驱动程序共享同一个虚拟地址空间。虚拟内存中的每个页面都标记了处理器在什么访问模式下才可以访问该页面。注意,设备驱动程序可不一定是微软写的,因此系统对恶意的驱动程序就缺少保护。这就是为什么Windows会更慎重的对待驱动程序,并引入了驱动程序签名。

应用程序在发出一个系统服务调用的时候,会从用户模式切换到内核模式,这时会发生什么?线程会切换吗?用户地址空间和内核地址空间不同,而页目录、页表又是进程相关的,怎么办?

从用户模式转换到内核模式,可以通过专门的处理器指令来完成。这条指令会将CPU切换到内核模式。操作系统会捕捉到这条指令,注意到有一个系统服务的请求到来,然后执行相应的内部函数。在将控制返回给用户线程以前,处理器的模式被切换回用户模式。

从用户模式到内核模式的装换本身并不会影响线程的调度——模式转换并不是环境切换。这就要说到系统机制了。

浅谈一点系统机制

当应用程序调用系统服务时,处理器将从用户模式切换到内核模式。这个过程是通过中断实现的。

中断

中断原本是硬件的概念,CPU提供中断的功能,它也外接中断控制器用来控制外围设备的中断请求。Windows系统在内核中定义了一套中断优先级方案,称为中断请求级别(IRQL)。下图就是x86系统的中断请求级别的定义。

优先级高的中断屏蔽优先级低的中断,即假如当前CPU正在处理一个优先级5的中断请求,它会将IRQL设为5,低于或等于5的后来中断都会被屏蔽。

普通线程运行在优先级0上(它实际上并不是一个中断优先级),因而会被任何中断打断。

准确地说,在x86早期的CPU上,采用int 0x2e这条中断指令来触发系统服务分发。发生中断后,CPU会去查中断分发表(IDT),根据中断号(0x2e)索引到中断分发例程,在这里就是系统服务分发器。

后来的处理器则提供了更高级的指令,而不是中断指令了。

重点来了!

回到上面的疑问,首先,由于系统调用只是处理器模式的切换,并没有发生环境(上下文)切换,所以线程和进程没有切换。

第二个问题,用户地址空间和内核地址空间不同,而页目录、页表又是进程相关的,进程又没变,这不是矛盾了吗?

答案是,进入到内核模式之后,内核模式的代码只能访问内核地址空间,不能访问用户地址空间。而由于内核地址空间对所有进程都一样,即所有进程都有相同的内核地址空间(只是在用户模式下访问不到而已),因此到了内核模式之后(运行在内核模式的)代码不会出错。

再详细的解释一下,页目录、页表与进程相关,指的是在用户模式下。即,在x86体系中,每个进程的虚拟地址空间的0x00000000~0x7FFFFFFF都是不相同的(映射到不同的物理内存地址),而0x80000000-0xFFFFFFFF都是相同的。所谓内核模式共享同一个虚拟内存空间就是这个意思。

但还有一个问题,既然运行在用户空间的代码访问不了内核空间,内核空间的代码也访问不了用户空间,那我进行一个系统调用的时候,参数和返回值怎么传递?

答案是,由内核(系统服务分发器)拷贝过去。

参考文献

《深入解析Windows操作系统》第四版

《Windows核心编程》

MSDN

Windows系统的四个重要概念——进程、线程、虚拟内存、内核模式和用户模式的更多相关文章

  1. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  2. 理解Windows内核模式与用户模式

     1.基础 执行 Windows 的计算机中的处理器有两个不同模式:"用户模式"和"内核模式". 依据处理器上执行的代码的类型,处理器在两个模式之间切换.应 ...

  3. 【windows】windows系统下,在任务管理器的进程选项卡中查看PID/任务管理器怎么查看PID

    PID,就是windows上的进程ID,是一个进程的唯一标识值. 那今天启动JDK跑起来一个项目之后,想要在任务管理器中查看这个JDK所在进程的PID但是看不到. 怎么解决? 1.我在任务管理器的服务 ...

  4. Windows系统如何找到占用端口的进程并杀掉

    1.先建立用户环境变量:C\WINDOWS/system32 2.输入:cmd,打开命令控制台,然后输入ipconfig 3.再输入:netstat -ano(可以找到所有的进程连接端口及对应PID) ...

  5. windows系统查询指定端口运行的进程并关闭

    假如占用的端口是80: 先打开cmd命令窗口 再查找80端口占用的进程:netstat  -aon|findstr  "80"    ,找到对应的程序的PID号: 根据PID号找到 ...

  6. Android的四个基本概念(线程通信和GLSurfaceView)

    GLSurfaceView提供了下列特性: 1> 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上. 2> 管理一个EGL disp ...

  7. Windows系统基本概念

    windows API:被文档化的可以调用的子例程,如CreateProcess 原生的系统服务(执行体系统服务):未被文档化的.可以再用户模式下调用的底层服务,如NtCreateProcess 内核 ...

  8. windows进程/线程创建过程 --- windows操作系统学习

    有了之前的对进程和线程对象的学习的铺垫后,我们现在可以开始学习windows下的进程创建过程了,我将尝试着从源代码的层次来分析在windows下创建一个进程都要涉及到哪些步骤,都要涉及到哪些数据结构. ...

  9. Linux下的进程类别(内核线程、轻量级进程和用户进程)--Linux进程的管理与调度(四)

    本文中出现的,内核线程,轻量级进程,用户进程,用户线程等概念,如果不太熟悉, 可以参见 内核线程.轻量级进程.用户线程三种线程概念解惑(线程≠轻量级进程) Linux进程类别 虽然我们在区分Linux ...

随机推荐

  1. Oracle-18-select语句初步&SQL中用算术表达式&别名的使用&连接运算符%distinct&where子句

    一.一般SELECT语句的格式例如以下: 1.查询指定表的全部列 select * from 表名 [where 条件] [group by 分组列名] [having 聚合函数] [order by ...

  2. 任哲<<μC/OS>>

    从第二章开始啦: 程序控制块(TCB)  重要概念  相当于对应程序块的学生证,学号,,, 上面的图是一个简单的程序控制块,还会有复杂的程序控制块,也许是嵌套的两级的: 链表就相当于老师手中的花名册, ...

  3. [Vue] Preload Data using Promises with Vue.js and Nuxt.js

    Nuxt.js allows you to return a Promise from your data function so that you can asynchronously resolv ...

  4. ios开发网络学八:NSURLSession相关代理方法

    #import "ViewController.h" @interface ViewController ()<NSURLSessionDataDelegate> /* ...

  5. sublim课程2 sublim编辑器的使用(敲代码的时候把这个放旁边用)

    sublim课程2   sublim编辑器的使用(敲代码的时候把这个放旁边用) 一.总结 一句话总结:不必一次记住所有,不可能也得不偿失,先记住常用,慢慢来.(敲代码的时候把这个放旁边用,一下子就熟了 ...

  6. 【codeforces 742B】Arpa’s obvious problem and Mehrdad’s terrible solution

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  7. NDK 配置及简单项目

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/50998720 文章出自:薛瑄的博客 你也能够查看我的其它同类文章,也会让你有一定的 ...

  8. 【物理/数学】概念的理解 —— pivot、position

    0. 基本释义 pivot: n. 枢轴:中心点:旋转运动 vt. 以-为中心旋转:把-置于枢轴上 vi. 在枢轴上转动:随-转移 adj. 枢轴的:关键的 position: n. 位置,方位:职位 ...

  9. Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动

    在上一篇文章<Android自定义组件系列[3]--自定义ViewGroup实现侧滑>中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布局示 ...

  10. Android Thread.setDaemon设置说明

    Thread.setDaemon的用法,经过学习以后了解: 1. setDaemon需要在start方法调用之前使用 2. 线程划分为用户线程和后台(daemon)进程,setDaemon将线程设置为 ...