之前有听到别人的面试题是问系统创建进程的具体过程是什么,首先想到的是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. Flask 入门(第二篇)

    1. 数据库 Flask 没有限定使用哪种数据库,不管是 SQL 还是 NoSQL.如果你不想自己编写负责的 SQL语句的话,你也可以使用 ORM,通过 SQLALchemy 即可实现. 1.1 SQ ...

  2. [Python] 利用Python做定时任务, 及时了解互联网动态

    前言 本人因为比较喜欢看漫画和动漫, 所以总会遇到一些问题, 因为订阅的漫画或者动漫太多, 总会忘记自己看到那一章节或者不知道什么时候更新. 故会有这么一个需求, 想记录自己想看的漫画或动画并在其更新 ...

  3. PAT 1071【STL string应用】

    1.单case很多清空没必要的 2.string+ char 最好用pushback 3.string +string就直接+ #include <bits/stdc++.h> using ...

  4. loj #2325. 「清华集训 2017」小Y和恐怖的奴隶主

    #2325. 「清华集训 2017」小Y和恐怖的奴隶主 内存限制:256 MiB时间限制:2000 ms标准输入输出 题目类型:传统评测方式:文本比较   题目描述 "A fight? Co ...

  5. ligerUI弹出框

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  6. IDEA开发Spark的漫漫摸索(二)

    1 新建Maven项目 特别提醒,Maven项目中有GropId和ArtifactId.GroupId是项目组织唯一的标识符,实际对应JAVA的包的结构,是main目录里java的目录结构.一般Gru ...

  7. Android Gradle 学习笔记(六):Gradle 插件

    Gradle 本身提供了一些基本的概念和整体核心的框架,其他用于描述真实使用场景的都可以通过插件扩展的方式来实现.这样就可以通过抽象的方式提供一个核心的框架,其他具体的功能和业务都通过插件扩展的方式来 ...

  8. Start and Stop Bitbucket Server

    Starting and stopping Bitbucket Server This page describes the various ways you can start or stop Bi ...

  9. Flutter SDK的下载与安装步骤 (mac版)

    本月初(应该是2018年12月4日),Google在其Flutter Live 2018大会上正式发布 Flutter 1.0 版本. 当然我们不会怀疑Google团队的技术实力,但它和React N ...

  10. POJ2488 A Knight's Journey

    题目:http://poj.org/problem?id=2488 题目大意:可以从任意点开始,只要能走完棋盘所有点,并要求字典序最小,不可能的话就impossible: 思路:dfs+回溯,因为字典 ...