进程是一个正在运行的程序的实例,由内核对象和地址空间组成。进程是不活泼的,执行地址空间中代码的是在它的环境中运行线程。每个线程都需要自己的一组CPU寄存器和堆栈。

  为了让所有线程都能运行,操作系统就要为所有线程安排一定的CPU时间,通过循环的方式提供时间片到线程。如果有多个CPU,操作系统就要用更复杂的算法,才能支持CPU线程的负载均衡了。

  进程被创建后,创建的第一个线程,也称为主线程。

  Windows应用程序有两种类型,GUI基于图形用户界面和CUI基于控制台用户界面。

  1. 拥有图形前端程序,可以创建窗口,拥有菜单,通过对话框和用户打交道,可以使用Windows的标准组件。
    1. 链接开关 /SUBSYSTEM:CONSOLE
    2. 入口WinMainwWinMain
    3. 启动函数WinMainCRTStartupwWinMainCRTStartup
  2. 用于文本操作的应用程序,通常不用于操作界面。虽然有窗口,但是只有文本。
    1. 链接开关 /SUBSYSTEM:WINDOWS
    2. 入口mainwmain
    3. 启动函数mainMainCRTStartupwmainMainCRTStartup

  程序类型和入口函数如果不匹配,链接程序就返回一条“未转换的外部符号”的消息。好在删除这个开关,链接程序也能自己找到正确的入口函数。

  入口和启动函数都有两个版本,是为了兼容Ansi和Unicode两种字符串类型。

  EXE或者DLL在被加载到进程空间后,都有一个独一无二的实例句柄。可执行文件的实例由(w)WinMain函数的第一个参数传递。有些函数需要的是HMODULE类型的参数,可以通过GetModuleHandle获取。

  实际情况说明,hInstancehModule是完全相同的对象。只有在16位系统中两者才表示不同的东西。

  可执行文件的被加载到哪里由链接器决定,而VC++的默认加载基址是0x400000,可以使用/BASE:address开关修改。address在Win2k和win98下修改加载基址最好是比0x400000大。

  使用函数GetModuleHandle可以获取可执行文件的句柄,也是加载到进程地址空间的地址。要注意的是,这个函数在DLL中调用,获得的不是DLL的加载地址,而是EXE的。

  WinMain的第二个参数hInstExePrev,是用在16位系统中的,不应该使用,CC++运行期启动代码总是把NULL传给它。

  第三个参数pszCmdLine,当进程创建的时候会传递一个命令行,就放在这个参数中,第一个一定是可执行文件的(全)路径。CreateProcess函数也有一个参数,用于设置这个命令行。调用GetCommandLine函数就可以拿到完整的命令行,然后调用CommandLineToArgv函数获取参数的数目。

  每个进程都有其相关的环境变量,是一组字符串,每个字符串是一个键值对,每个键值对以key=val\0的方式存在。如果要使用环境变量,可以调用函数GetEnvironmentVariable,再调用ExpandEnvironmentStrings解析环境变量。如果要添加环境变量,就使用SetEnvironmentVariable。

  获得和设置当前驱动器和目录使用的是GetCurrentDirectorySetCurrentDirectory,但获取全路径的函数是GetFullPathName

  获得系统的版本使用函数GetVersion,它的返回值是个DWORD,高位字节代表主版本号,低位字节代表次版本号。但是,编写改代码的程序员犯了个错,颠倒了高低位版本号。而很多程序员又开始使用这个函数了,因此巨硬不得不保持函数的原样,修改了文档以说明这个错误。为了解决这个问题,巨硬增加了一个新的函数GetVersionEx,它的参数是个结构体,成员中已经包含了主版本号和次版本号。


CreateProcess函数

    

  当一个线程调用CreateProcess的时候,系统就会做这些事:

  • 创建一个内核对象,其初识引用计数是1。
  • 为新进程创建虚拟地址空间,并将EXE(和DLL,如果有)的代码和数据加载进去。
  • 为新进程的主线程创建一个内核对象,引用计数是1。
  • 主线程开始运行,开始调用入口函数。

  如果成功创建,CreateProcess便返回TRUE。但是这个时候并不意味着操作系统已经找出了所有的DLL,如果有一个DLL未找到,或者不能正常初始化,进程就停止运行。这时候父进程是不知道发生了什么的。

  前两个参数pszApplicationNamepszCommandLine分别用于指定设定新进程要使用的可执行文件名字和命令行字符串。

  • pszCommandLine的原型指明它不能是个常量字符串,否则会出现“违规访问”的问题。除非使用Win2K上的Ansi版本。
  • pszCommandLine可以是一个完整的命令行,便于CreateProcess创建新进程。当CreateProcess分析命令行字符串时,将查看第一个标记,并假定它是个可执行文件,如果没有扩展名,就假定扩展名是exe。如果不是全路径,CreateProcess也按下面的顺序搜索可执行文件:
    • 包含调用进程的当前目录
    • 调用进程的当前目录
    • Windows的目录
    • PATH环境变量列出的目录

  前面说的都是在pszApplicationName的值是NULL的情况(99%)下发生的。它也可以是可执行文件的路径,不过必须有扩展名,因为系统不会假设它是exe。而且,只要在当前目录中找不到这个文件,就不找了的,直接运行失败。还有,即便设置了文件名,CreateProcess也会将命令行参数的内存作为命令行传递给新进程。

  然后第三到第五三个参数psaProcesspsaThreadbInheritedHandles就涉及必要的安全性了。

  既然是进程,那就是可以继承的,需要安全性结构体和句柄(s),前面说过的,两个结构体分别给进程和进程的主线程。现在我们假定ProcessA创建了 ProcessB,那么情况会是这样:

  • 调用CreateProcess,为psaProcess传递一个SERCURITY_ATTRIBUTES的结构体地址,在这个结构体中bInheritedHandles被设置为TRUE。
  • 同样参数psaThread参数也是一个SERCURITY_ATTRIBUTES结构体的地址,不过里面bInheritedHandles被设置成FALSE。
  • 当系统创建ProcessB的时候,同时指定一个进程内核对象和线程内核对象,并且将句柄返回给ppiProcInfo参数中指向结构体的ProcesssA

  现在ProcessA就可以对ProcessB的进程句柄和线程句柄进行操作了。

  参数fdwCreate是一些选项,指明如何创建新进程,多个标志位可以用OR操作符组合。

  • DEBUG_PROCESS
  • DEBUG_ONLY_THIS_PROCESS
  • CREATE_SUSPEND
  • DETACHED_PROCESS
  • CREATE_NEW_CONSOLE
  • CREATE_NO_WINDOW
  • CREATE_NEW_PROCESS_GROUP
  • CREATE_DEFAULT_ERROR_MODE
  • CREATE_SEPARATE_WOW_VDM
  • CREATE_SHARED_WOW_VDM
  • CREATE_UNICODE_ENVIRONMENT
  • CREATE_FORCEDOS
  • CREATE_BREAKAWAY_FROM_JOB

  pvEnvironment参数指定新进程的环境变量字符串,大多数情况下是NULL,这样子进程就能使用父进程使用的环境变量字符串。

  pszCurDir不是很重要。

  psiStartupInfo指针指向一个STARTUPINFO结构:

  而创建新进程的时候,这个结构体除了置零,大多数情况下都只用默认值。

  

   

  dwFlags成员包含一组标志,用于修改如何创建子进程。

  

  如果想在进程被创建的期间鼠标变成一个沙漏,使用STARTF_FORCEONFEEDBACKSTARTF_FORCEOFFEEDBACK属性,时常5秒。

  ppiProcInfo参数指向PROCESS_INFORMATION结构,

  


  终止进程的运行,有四种方法:

  • 主线程的进入点函数返回,好处是:

    • 这个线程创建的C++对象能够使用它们的析构函数正确地撤销。
    • 操作系统能正确释放该线程堆栈使用的内存。
    • 系统将进程的退出代码设置为入口点函数的返回值。
    • 系统吧内核对象的引用计数递减1。
  • 进程中的一个线程调用ExitProcess函数(避免使用)
    • 有些C++对象未能调用析构函数正确撤销。
  • 另一个进程中的线程调用TerminateProcess函数(避免使用)
    • 没有办法的办法
  • 进程中的线程集体自杀(几乎没发生过)

  


  枚举系统中运行的进程:见源码。

回炉重造之重读Windows核心编程-004-进程的更多相关文章

  1. 回炉重造之重读Windows核心编程-003-内核对象

    内核对象是个比较难理解的概念,问题的根源就在于即使是<核心编程>书中也没有说清楚它的定义,只是不停地举例和描述它的性质,还有如何使用. 盲人摸象,难见全貌.只能尽可能列举它的性质,注意使用 ...

  2. 回炉重造之重读Windows核心编程-006-线程

    线程也是有两部分组成的: 线程的内核对象,操作系统用来管理线程和统计线程信息的地方. 线程堆栈,用于维护现场在执行代码的时候用到的所有函数参数和局部变量. 进程是线程的容器,如果进程中有一个以上的线程 ...

  3. 回炉重造之重读Windows核心编程-002-字符集

    使用Unicode的优势: 便于在不同语言之间进行数据交换. 让你的exe或者dll文件支持所有的语言. 提高应用程序的执行效率. Windows2000是使用Unicode重新开发的,核心部分都需要 ...

  4. 回炉重造之重读Windows核心编程-001-错误处理

    Windows处理错误靠的是API的返回值,类型不止一种种: VOID,函数不可能失败,Windows API的返回值很少是这个情况. BOOL,如果函数失败,则返回值是0,否则返回是非零值.不要测试 ...

  5. windows核心编程之进程间共享数据

    有时候我们会遇到window进程间共享数据的需求,例如说我想知道系统当前有多少某个进程的实例. 我们能够在程序中定义一个全局变量.初始化为0.每当程序启动后就加1.当然我们我们能够借助第三方介质来储存 ...

  6. 《windows核心编程系列》二十一谈谈基址重定位和模块绑定

    每个DLL和可执行文件都有一个首选基地址.它表示该模块被映射到进程地址空间时最佳的内存地址.在构建可执行文件时,默认情况下链接器会将它的首选基地址设为0x400000.对于DLL来说,链接器会将它的首 ...

  7. Asp.Net SignalR 使用记录 技术回炉重造-总纲 动态类型dynamic转换为特定类型T的方案 通过对象方法获取委托_C#反射获取委托_ .net core入门-跨域访问配置

    Asp.Net SignalR 使用记录   工作上遇到一个推送消息的功能的实现.本着面向百度编程的思想.网上百度了一大堆.主要的实现方式是原生的WebSocket,和SignalR,再次写一个关于A ...

  8. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  9. windows核心编程---第二章 字符和字符串处理

        使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是指UTF-16.也 ...

随机推荐

  1. 1.Vue初始及相关Vue核心组件

    1.Vue官方文档 官网文档:https://cn.vuejs.org/v2/guide/ 2.Vue-cli官方文档(脚手架) 官网文档:https://cli.vuejs.org/zh/guide ...

  2. C语言作业12—学期总结

    一.我学到的内容 二.我的作业及收获 我的作业: 第一次作业 第二次作业 第三次作业 第四次作业 第五次作业 第六次作业 第七次作业 第八次作业 第九次作业 第十次作业 第十一次作业 我的收获: 转眼 ...

  3. 使用IDEA构建Spring-boot多模块项目配置流程

    使用IDEA构建Spring-boot多模块项目配置流程 1.创建项目 点击Create New Project 在左侧选中Spring Initializer,保持默认配置,点击下一步. 在Grou ...

  4. Vue中的计算属性

    一.什么是计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护. 二.计算属性的用法 在一个计算属性里可以完成各种复杂的逻辑,包括运算.函 ...

  5. SVM(2)-模式识别课堂笔记

    三.非线性支持向量机 问题起源:1.对于一些非线性可分的问题,我们希望能通过一个映射问题将特征映射到新的空间中去(可能是更高维的空间),寄希望于在新的空间中样本能够线性可分:2.我们注意到在线性支持向 ...

  6. js的new操作符深度解析

    引言 我们都知道new操作符在js中一般是用来创建一个构造函数的实例,它在创建实例具体做了什么,MDN文档是这么说的: 我一开始看到,完全没有任何的头绪和理解,到底什么意思,后面通过上网查阅了大量的资 ...

  7. 《快乐编程大本营》java语言训练班 3课:java的运算符

    第1节. 算术运算符 第2节. 递增和递减运算符 第3节. 比较运算符 第4节. 逻辑运算符 第5节. 运算符优先级 第6节. 字符串运算 http://code6g.com/pxphp/px/ban ...

  8. android studio sqlite实际应用中存在的问题

    原项目已上传到github long f = dbdatabase.update("user", values, "id=?", new String[]{St ...

  9. 请转发!简单2分钟制作无接触式小区进出微信登记表!全免费!数据安全!所有数据均存在创建人登录的QQ腾讯文档里!

    全免费!数据安全!所有数据均存在创建人登录的QQ腾讯文档里! 阻击疫情到了最吃劲的关键期,大家能不出门就不出门,但免不了出去买个菜.取个快递啥的,每次出入的时候,社区同志都在认真拿着笔记录每个进出信息 ...

  10. Codeforces 922 C - Robot Vacuum Cleaner (贪心、数据结构、sort中的cmp)

    题目链接:点击打开链接 Pushok the dog has been chasing Imp for a few hours already. Fortunately, Imp knows that ...