之前有听到别人的面试题是问系统创建进程的具体过程是什么,首先想到的是CreateProcess,但是对于具体过程却不是很清楚,今天整理一下。

从操作系统的角度来说

创建进程步骤:
        1.申请进程块      
        2.为进程分配内存资源
        3.初始化进程块
        4.将进程块链入就绪队列

课本上的知识。。。

从CreateProcess的具体流程来说:

CreateProcess它首先创建一个执行体进程对象,即EPROCESS 对象,然后创建一个初始线程,为初始线程建立一个栈,并设置好它的初始执行环境。完成这些工作以后,该线程就可以参与系统的线程调度了。然而,通过Windows API 函数创建的进程也要接受Windows 子系统的管理,在这种情况下,仅仅内核部分的工作还不够,系统在创建进程过程中,还需要跟子系统打交道。另外,还需建立起独立的内存地址空间。

CreateProcess通过内核创建进程的步骤,大致分为六个阶段:

NtCreateProcess,它只是简单地对参数稍作处理,然后把创建进程的任务交给NtCreateProcessEx 函数,所以我们来看NtCreateProcessEx 的原型及其流程。

NTSTATUS
NtCreateProcessEx(
__out PHANDLE ProcessHandle,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in HANDLE ParentProcess,
__in ULONG Flags,
__in_opt HANDLE SectionHandle,
__in_opt HANDLE DebugPort,
__in_opt HANDLE ExceptionPort,
__in ULONG JobMemberLevel
);

NtCreateProcessEx 函数的代码只是简单地检查ProcessHandle 参数代表的句柄是否可写,然后把真正的创建工作交给PspCreateProcess 函数,所以,PspCreateProcess 才是真正创建进程的函数。

PsCreateSystemProcess 可用于创建系统进程对象,它创建的进程都是PsInitialSystemProcess 的子进程。所以,PspCreateProcess函数负责创建系统中的所有进程,包括System 进程。下面介绍此函数的基本流程。

第一阶段:打开目标映像文件

第二阶段:创建内核中的进程对象

第三阶段:创建初始线程

第四阶段:通知windows子系统进程csrss.exe进程来对新进程进行管理

第五阶段:启动初始线程

第六阶段:用户空间的初始化和Dll连接

具体内容:

在Windows中,CreateProcess要先通过系统调用NtCreateProcess创建进程,成功以后就立即通过系统调用NtCreateThread创建其第一个线程。
 
第一阶段:打开目标映像文件
 
首先用CreateProcess(实际上是CreateProcessW)打开指定的可执行映像文件,并创建一个内存区对象。注意,内存区对象并没有被映射到内存中(由于目标进程尚未建立起来,不可能完成内存映射),但它确实是打开了。
 
 
第二阶段:创建内核中的进程对象
 
实际上就是创建以EPROCESS为核心的相关数据结构,主要包括:
 
调用内核中的NtCreateProcessEx 系统服务,实际的调用过程是这样的:kernel32.dll 中的CreateProcessW调用ntdll.dll 中的存根函数NtCreateProcessEx,而ntdll.dll的NtCreateProcessEx 利用处理器的陷阱机制切换到内核模式下;在内核模式下,系统服务分发函数KiSystemService 获得控制,它利用当前线程指定的系统服务表,调用到执行体层的NtCreateProcessEx 函数。然后,执行体层的NtCreateProcessEx 函数执行前面介绍的进程创建逻辑,包括创建EPROCESS 对象、初始化其中的域、创建初始的进程地址空间、创建和初始化句柄表,并设置好EPROCESS 和KPROCESS 中的各种属性,如进程优先级、安全属性、创建时间等。到这里,执行体层的进程对象已经建立起来,进程的地址空间已经初始化,并且EPROCESS 中的PEB 也已初始化。
 
第三阶段:创建初始线程
 
这个阶段是通过调用NtCreateThread()完成的,主要包括:
 现在,虽然进程对象已经建立起来,但是它没有线程,所以,它自己还不能做任何事情。接下来需要创建一个初始线程,在此之前,首先要构造一个栈以及一个可供运行的环境。初始线程的栈的大小可以通过映像文件获得,而创建线程则可以通过调用ntdll.dll 中的NtCreateThread 函数来完成。
创建和设置目标线程的ETHREAD数据结构,并处理好与EPROCESS的关系(例如进程块中的线程计数等等)。
在目标进程的用户空间创建并设置目标线程的TEB。
将目标线程在用户空间的起始地址设置成指向Kernel32.dll中的BaseProcessStart()或BaseThreadStart(),前者用于进程中的第一个线程,后者用于随后的线程。
     用户程序在调用NtCreateThread()时也要提供一个用户级的起始函数(地址), BaseProcessStart()和BaseThreadStart()在完成初始化时会调用这个起始函数。
     ETHREAD数据结构中有两个成份,分别用来存放这两个地址。
调用KeInitThread设置目标线程的KTHREAD数据结构并为其分配堆栈和建立执行环境。
   特别地,将其上下文中的断点(返回点)设置成指向内核中的一段程序KiThreadStartup,使得该线程一旦被调度运行时就从这里开始执行。
系统中可能登记了一些每当创建线程时就应加以调用的“通知”函数,调用这些函数。
 
 
第四阶段:通知windows子系统
 
每个进程在创建/退出的时候都要向windows子系统进程csrss.exe进程发出通知,因为它担负着对windows所有进程的管理的责任,
注意,这里发出通知的是CreateProcess的调用者,不是新建出来的进程,因为它还没有开始运行。
 
至此,CreateProcess的操作已经完成,但子进程中的线程却尚未开始运行,它的运行还要经历下面的第五和第六阶段。
 
 
第五阶段:启动初始线程

在内核中,新线程的启动例程是KiThreadStartup函数,这是当PspCreateThread 调用KeInitThread 函数时,KeInitThread 函数调用KiInitializeContextThread(参见base\ntos\ke\i386\thredini.c 文件)来设置的。

KiThreadStartup 函数首先将IRQL 降低到APC_LEVEL,然后调用系统初始的线程函数PspUserThreadStartup。这里的PspUserThreadStartup 函数是PspCreateThread 函数在调用KeInitThread 时指定的,。注意,PspCreateThread函数在创建系统线程时指定的初始线程函数为PspSystemThreadStartup  。线程启动函数被作为一个参数传递给PspUserThreadStartup,在这里,它应该是kernel32.dll 中的BaseProcessStart。

PspUserThreadStartup 函数被调用。逻辑并不复杂,但是涉及异步函数调用(APC)机制。

新创建的线程未必是可以被立即调度运行的,因为用户可能在创建时把标志位CREATE_ SUSPENDED设成了1;
如果那样的话,就需要等待别的进程通过系统调用恢复其运行资格以后才可以被调度运行。否则现在已经可以被调度运行了。至于什么时候才会被调度运行,则就要看优先级等等条件了。
 
 
第六阶段:用户空间的初始化和Dll连接

PspUserThreadStartup 函数返回以后,KiThreadStartup 函数返回到用户模式,此时,PspUserThreadStartup 插入的APC 被交付,于是LdrInitializeThunk 函数被调用,这是映像加载器(image loader)的初始化函数。LdrInitializeThunk 函数完成加载器、堆管理器等初始化工作,然后加载任何必要的DLL,并且调用这些DLL 的入口函数。最后,当LdrInitializeThunk 返回到用户模式APC 分发器时,该线程开始在用户模式下执行,调用应用程序指定的线程启动函数,此启动函数的地址已经在APC 交付时被压到用户栈中。

DLL连接由ntdll.dll中的LdrInitializeThunk()在用户空间完成。在此之前ntdll.dll与应用软件尚未连接,但是已经被映射到了用户空间
函数LdrInitializeThunk()在映像中的位置是系统初始化时就预先确定并记录在案的,所以在进入这个函数之前也不需要连接。

涉及到了Windows内核的知识,细节处还有待理解。。。

参考资料:

http://www.cnblogs.com/csyisong/archive/2010/10/22/1858115.html

http://www.cnblogs.com/Gotogoo/p/5262536.html

http://book.51cto.com/art/201011/235767.htm

《Windows内核原理与实现》潘爱民

关于Windows创建进程的过程的更多相关文章

  1. 【转载】详解CreateProcess调用内核创建进程的过程

    原文:详解CreateProcess调用内核创建进程的过程 昨天同学接到了腾讯的电面,有一题问到了CreateProcess创建进程的具体实现过程,他答得不怎么好吧应该是, 为了以防万一,也为了深入学 ...

  2. Windows操作系统下创建进程的过程

    进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.而进程则 ...

  3. 详解CreateProcess调用内核创建进程的过程

    昨天同学接到了腾讯的电面,有一题问到了CreateProcess创建进程的具体实现过程,他答得不怎么好吧应该是, 为了以防万一,也为了深入学习一下,今天我翻阅了好多资料,整理了一下,写篇博客,也算是加 ...

  4. 64位CreateProcess逆向:(二)0环下参数的整合即创建进程的整体流程

    转载:https://bbs.pediy.com/thread-207683.htm 点击下面进入总目录: 64位Windows创建64位进程逆向分析(总目录) 在上一篇文章中,我们介绍了Create ...

  5. Windows创建的基本含义和进程的进程的内核

    过程 1 这意味着过程: 1.1   一个是在操作系统的内核对象管理处理. 的统计信息的地方. 1.2   还有一个是地址空间.它包括全部可运行模块或DL L 模块的代码和数据.它还包括动态内存分配的 ...

  6. windows中根据进程PID查找进程对象过程深入分析

    这里windows和Linxu系列的PID 管理方式有所不同,windows中进程的PID和句柄没有本质区别,根据句柄索引对象和根据PID或者TID查找进程或者线程的步骤也是一样的.   句柄是针对进 ...

  7. linux内核分析作业6:分析Linux内核创建一个新进程的过程

    task_struct结构: struct task_struct {   volatile long state;进程状态  void *stack; 堆栈  pid_t pid; 进程标识符  u ...

  8. 分析Linux内核创建一个新进程的过程

    一.原理分析 1.进程的描述 进程控制块PCB——task_struct,为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct ...

  9. 分析Linux内核创建一个新进程的过程【转】

    转自:http://www.cnblogs.com/MarkWoo/p/4420588.html 前言说明 本篇为网易云课堂Linux内核分析课程的第六周作业,本次作业我们将具体来分析fork系统调用 ...

随机推荐

  1. Sharding-JDBC 使用入门和基本配置

    一.什么是Sharding-JDBC Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务.它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理 ...

  2. 【leetcode 968. 监控二叉树】解题报告

    解题思路: 由于叶子节点一定不要安装监视器,这样才能使总监视器数量比较少,因此需要从下往上进行判断当前节点的状态(共:3种状态): 0: 当前节点安装了监视器 1: 当前节点可观,但没有安装监视器 2 ...

  3. LightOJ 1336 - Sigma Function

    原题链接 基础数论中很经典的一道题 题意 给出了σ(n)的计算公式,让你找出整数1-n中有多少对应σ(n)的值是偶数. 思路 观察σ(n)的公式发现,每一个乘项都是 (piei+1 - 1) / (p ...

  4. idea中使用Git对项目进行版本控制

  5. SMTP服务器配置

    Windows Server 2012/2012 R2:安装和配置 SMTP 服务器 安装 SMTP 服务器 以下是安装 SMTP 服务器功能的步骤: 1. 打开“服务器管理器”:单击键盘上的 Win ...

  6. [题解](优先队列广搜)POJ_3635_Full Tank

    用二元组$(city,fuel)$即可记录所有状态,以当前花费为关键字优先队列,开数组记录直接做即可 有一个点在于每次不用枚举所有的加油数量,只需要加一即可,因为如果在加一升更优的话又会扩展出加更多油 ...

  7. 运算符优先级 (JavaScript)

    运算符优先级描述了在计算表达式时执行运算的顺序.先执行具有较高优先级的运算,然后执行较低优先级的运算.例如,先执行相乘,再执行相加. JavaScript 运算符     下表列出了 JavaScri ...

  8. tomcat memecached session 共享同步问题的解决

    事件缘由:一个主项目“图说美物”,另外一个子功能是品牌商的入驻功能,是跟主项目分开的项目,为了共享登录的用户信息,而实现session共享,俩个tomcat,一个tomcat6,一个tomcat7 w ...

  9. Vue 5 -- axios、vuex

    一.内容回顾 1.webpack(前端中工作,项目上线之前对整个前端项目优化) - entry:整个项目的程序入口(main.js或index.js): - output:输出的出口: - loade ...

  10. spark shell start

    spark-shell \--master yarn \--deploy-mode client \--queue default \--driver-memory 1G \--executor-me ...