Windows 中一个应用程序的启动过程

 

# 1. Explorer.exe 

    Windows 能够流行起来,很大一个原因是它有友好的用户图形界面,操作方便简单,容易上手。在Windows环境下打开一个程序,只要双击软件的图标就行了,那么它是如何启动起来的? 
    当我们启动电脑进入桌面时,系统会创建 Explorer.exe 进程。Explorer.exe是Windows程序管理器 或者叫 文件资源管理器,用于管理Windows图形壳,删除该程序会导致 Windows 图形界面无法使用。所以,如果有时候我们电脑的桌面空白了,或者蓝屏,可以通过 Alt+Ctrl+delete(或者在dos中输入 taskmgr 命令) 打开任务管理器, 点击“文件”-> “新建任务”,输入 "explorer.exe",就可以找回我们的桌面了。
    当双击某个图标时,Explorer.exe进程的一个线程会侦测到这个操作,它根据注册表中的信息取得文件名,然后Explorer.exe 以这个文件名调用 CreateProcess 函数。注册表中有相关的项保存着双击操作的信息,如 exe 文件关联、启动 exe 的 Shell 是哪个。PC中的大多其它的进程都是 Explorer.exe 的子进程,因为它们都是由Explorer.exe 进程创建的。
 

# 2. CreateProcess 函数的执行过程

(1)CreateProcess 实际上是通过 NtCreateProcess 函数实现的, 此时,系统会创建一个进程内核对象。进程内核对象可以看作是操作系统用来管理进程的小的数据结构,它是在内核堆区分配的一个结构体,是系统用来存放关于进程统计信息的地方。进程内核对象维护了一个句柄表的结构,当进程被初始化之后,其句柄表是空的。当进程内的一个线程通过指定的函数创建了一个内核对象时,内核会为对象分配一块内存区域并初始化这块区域,然后内核会在进程的句柄表中查找一个空的入口,找到之后会初始化句柄表的以索引定位的区域。初始化的主要过程就是填充句柄表的一个单元,包括指定内核对象地址,指定访问码,指定标记等。

(2) 进程内核对象创建后,它的引用计数被置为1。然后系统为刚刚创建的进程分配的进程虚拟地址空间。要注意了,之所以称为虚拟地址空间,就是因为这块地址空间并不在内存之中,它只是在硬盘上划分的被称为“页”的文件。每个进程都有自己的虚拟地址空间,在进程初始化的时候,其所有的程序和数据会被加载到这个地址空间中。等到真正运行的时候,系统为每个进程配置的页表会把虚拟地址映射为真正的物理地址(这个过程,我会在后面的博客中详细介绍如何映射)。

 

(3)初始化虚拟地址空间。进程地址空间创建后,Windows的装载器(loader,也称为PE装载器)开始工作,Loader会读取EXE文件的信息(PE文件)。此时 loader 会检查PE文件的有效性,如果PE文件有错误,进程也就无法启动了。如果PE文件没有错误,装载器就把PE文件的内容(二进制代码)映射到进程的地址空间中,然后读取 PE文件的导入地址表(Import Table),这里存放有exe文件需要导入的模块文件(DLL),系统会一一加载这些DLL到进程的地址空间中,具体做法是调用 LoadLibrary 函数加载程序代码到某个地址,然后系统会映射这些代码到进程的地址空间中,要知道DLL只需加载一次就可映射到所有进程的地址空间(映射过程我会在后面详细阐述)中,并为每个DLL维护一个引用计数,当引用计数为 0 时,DLL就从内存中卸载,释放占用的内存。DLL里面可能又引用了其它的DLL,因此加载DLL时是递归形式的,直到加载完Import Table 里描述的所有DLL模块,此时进程初始化部分完成。

(4)创建进程的主线程。当进程的初始化完成后,开始创建进程的主线程,一个进程至少要有一个主线程才能运行,可以说进程只是充当一个容器的作用,而线程才是执行代码的载体。线程是用 CreateThread 这个函数创建的。创建线程时,也和进程相似,系统会创建线程内核对象,初始化线程堆栈。线程堆栈有两个,一个是核心堆栈,由核心态维护;另一个是用户堆栈,运行在用户态下。同样的,线程的引用计数也置为1。
(5)C/C++运行期库初始化。当进程的主线程初始化完成后,并且线程得到了CPU时间片,CPU把CS:IP指向程序入口(OEP),这个地址相当重要,因为这是程序运行时第一条指令所在的地址(我们可以使用一些PE辅助工具来查看PE文件的地址信息,注意真实地址==偏移地址 + 基址)。其实,CS:IP指向的地址处是一条JMP指令,它跳转到程序真正的入口函数,入口函数有以下4种形式:
···
mainCRTStartup  (用于 ANSI 版本的控制台应用程序 )
wmainCRTStartup ( 用于 Unicode 版本的控制台应用程序 )
WinMainCRTStartup ( 用于 ANSI 版本的窗口应用程序 )
wWinMainCRTStartup ( 用于 Unicode 版本的窗口应用程序)
···

下面再看看入口函数的源码:

···

int XXXCRTStartup(void)                   //XXX表示不同Windows版本

{   __security_init_cookie();       // 完成安全方面的初始化
    return__tmainCRTStartup();
}
···

在__tmainCRTStartup中首先调用了GetStartupInfoW函数取得父进程创建本进程时的启动信息, 然后又是一系列的初始化,其中包括C++构造函数的调用,还有静态变量,全局变量的初始化,这些操作是在_initterm这个函数中完成的。接着,我们的 (w)WinMain / (w)main 函数会被调用,到这时,用户程序代码才开始被执行。

 

 

    

 

 

 

 

 

 

 

 

 

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Windows系统,要使用命令行一键切换多个JDK版本,可以按照以下步骤操作: 1. 首先,在当前用户的环境变量设置JDK的安装路径。在命令行输入`setx JAVA_HOME "JDK的安装路径"`。例如,如果要切换到JDK 8,将路径替换为JDK 8的安装路径。 2. 接下来,更新系统的Path变量。在命令行输入`setx Path "%JAVA_HOME%\bin;%Path%"`。这将把JDK的bin文件夹路径添加到系统的Path变量,使得系统能找到正确的JDK版本。 3. 为了实现一键切换,可以编写一个批处理脚本或者PowerShell脚本。在脚本,可以使用`setx`命令来切换JDK的安装路径和更新系统的Path变量。 4. 在脚本,可以定义不同的变量来表示不同的JDK版本,然后在切换时将对应的变量传递给`setx`命令。 5. 最后,在命令行运行脚本即可一键切换多个JDK版本。可以创建不同的脚本来切换到不同的JDK版本,这样就可以根据需要灵活选择。 通过以上步骤,我们可以使用命令行一键切换多个JDK版本。这样可以方便地在不同的项目使用不同的JDK版本,并且避免了手动修改环境变量的繁琐步骤。 ### 回答2: 在Windows系统上,可以通过以下步骤使用命令行一键切换多个JDK版本: 1. 首先,确保你已经安装了多个不同版本的JDK,并记录它们的安装路径。 2. 打开命令提示符(CMD)或者PowerShell。 3. 输入以下命令来设置环境变量,将你想要切换的JDK版本路径添加到PATH环境变量: ``` setx /M JAVA_HOME "C:\Path\to\your\jdk\version" setx /M PATH "%JAVA_HOME%\bin;%PATH%" ``` 注意,将上面的`C:\Path\to\your\jdk\version`替换为你想要切换的JDK版本的安装路径。 4. 使用以下命令来验证JDK版本是否已成功更改: ``` java -version ``` 5. 如果你想要切换到另一个JDK版本,重复步骤3和4。 请注意,这种方法只在当前的命令提示符或者PowerShell会话生效。如果你想要在重新启动后保留更改,你需要将以上步骤执行的命令添加到你的系统环境变量。 总之,通过设置环境变量和添加到PATH,使用CMD或者PowerShell可以轻松地在Windows系统一键切换多个JDK版本。 ### 回答3: 在Windows系统上,我们可以使用命令行来一键切换多个JDK版本。下面是一个简单的步骤: 1. 首先,在计算机上安装多个不同版本的JDK,并设置好对应的环境变量。 2. 打开命令提示符,可以通过按下Win键+R,然后输入“cmd”来打开。 3. 在命令行,使用“where java”命令来查找当前系统已安装的所有JDK的路径。该命令会列出所有JDK的安装位置。 4. 根据需要,将不同版本的JDK路径添加到环境变量。使用“setx JAVA_HOME <JDK路径>”命令来设置对应JDK的JAVA_HOME环境变量。 5. 然后,使用“setx PATH %JAVA_HOME%\bin;%PATH%”来设置PATH环境变量。这将确保命令行可以找到所选的JDK版本。 6. 现在,可以使用“java -version”命令来验证所选的JDK版本。在命令行输入该命令后,将显示当前运行的JDK的版本信息。 7. 如果需要切换到另一个JDK版本,只需重复第4步和第5步,重新设置JAVA_HOME和PATH环境变量。 通过以上步骤,我们可以使用命令行在Windows系统一键切换多个JDK版本。这样可以方便地在不同项目或开发环境之间切换,确保与应用程序的版本要求相匹配。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值