Windows核心编程(二)

4 进程

进程通常被定义为一个正在运行的实例,由两部分组成:系统用来管理这个进程的内核对象、地址空间(包括所有可执行模块或DLL模块的数据和代码)。


应用程序进入点



32位系统中,HINSTANCE和HMODULE是完全相同的对象,它表示可执行文件或DLL文件加载到进程空间时所用的基地址。可以用下面的函数得到一个模块的句柄

/基地址:

HMODULE GetModuleHandle( PCTSTR pszModule );

如果传入NULL,返回调用的可执行文件的基地址(即使是通过包含在DLL中的代码调用,返回的不是DLL文件的基地址)。另外,它只查看调用进程的地址空间。


进程的命令行

新进程创建时,要传递一个命令行,它几乎永远不会是空的,因为可执行文件的名字是命令行上的第一个标记。如果要修改命令行字符串,最好先将命令行拷贝到本地缓存中(即拷贝出新的使用)。

还可以使用GetCommandLine获取一个指向进程的完整命令行的指针。用CommandLineToArgvW将Unicode字符串分割成它的各种标记(其实就是得到argc,argv)。


进程的环境变量

环境块是进程的空间中分配的一个内存块,每个环境块都包含一组字符串:

VarName1=VarValue1\0

VarName2=VarValue1\0

VarName3=VarValue1\0

.

.

\0

环境块中的字符串必须按环境变量名的字母顺序排序。

进程启动时,要查看环境块,就可以找出某些变量的值,供程序使用,改变程序属性或行为。

有关环境变量的几个函数:GetEnvironmentVariable, ExpandEnviromentString, SetEnviromentVariable


如果创建一个子进程之后,不关心它的执行情况或希望放弃与子进程之间的联系,可以通过调用CloseHandle来关闭子进程的句柄和它的主线程的句柄。


五、作业

如果需要把一组进程当作单个实体来处理,可以使用作业。作业内核对象,能够将进程组合在一起,并且创建一个“沙框”,以便限制进程能够进行的操作。因此可以将作业视为进程的一个容器。

首先,可以创建一个作业,CreateJobObject( PSECURITY_ATTRIBUTES psa, PCTSTR pszName );

通过作业名字可以在另一个进程中访问该作业,OpenJobObject( DWORD dwDesiredAccess, BOOL bInheritHandle, PCTSTR pszName );

在进程中关闭作业的句柄,也只是使进程无法访问该作业,作业仍然存在,只有作业中的所有进程全部终止运行之后,作业内核对象才会被自动撤销。

对作业中的进程可以作的限制:

1)基本限制和扩展基本限制,用于防止作业中的进程垄断系统的资源;

2)基本的UI限制,用于防止作业中的进程改变用户界面;

3)安全性限制,用于防止作业中的进程访问保密资源(文件、注册表关键字等);

BOOL WINAPI SetInformationJobObject(
  __in          HANDLE hJob,
  __in          JOBOBJECTINFOCLASS JobObjectInfoClass,
  __in          LPVOID lpJobObjectInfo,
  __in          DWORD cbJobObjectInfoLength
);

第一个参数指定要限制的作业。第二个参数是一个枚举类型,指定了要施加的限制的类型。第三个参数是一个数据类型的地址,该数据类型包含着具体的限制设置。第四个参数指出此数据结构的大小(用于版本控制)。

可以使用下面的函数来查询这些限制:

BOOL QueryInformationJobObject(
    __in_opt  HANDLE hJob,
    __in      JOBOBJECTINFOCLASS JobObjectInformationClass,
    __out_bcount_part(cbJobObjectInformationLength, *lpReturnLength) LPVOID lpJobObjectInformation,
    __in      DWORD cbJobObjectInformationLength,
    __out_opt LPDWORD lpReturnLength
    );

这个函数还可以用来获得作业的基本的统计信息,此时向第2个参数传递JobObjectBasicAccountingInformation和一个JOBOBJECT_BASIC_ACCOUNTING_INFORMATION结构的地址。

还可以传入JobObjectBasicAndIoAccountingInformation来查询基本统计信息和I/O统计信息。


如果要将一个进程显式的放入作业中,使用下面的函数:

BOOL AssignProcessToJobObject(
    __in HANDLE hJob,
    __in HANDLE hProcess
    );

注意:一个进程一旦属于一个作业的一部分,就不能再移动到另一个作业中,也不能成为让它从原有的作业中脱离成为“无业的”。

作业中的一个进程生成了另一个进程的时候,新进程将自动成为父进程所属于的作业的一部分。如果不希望这样,可以通过以下方式改变这种行为:

1)打开基本限制中的JOB_OBJECT_LIMIT_BREAKAWAY_OK标志,告诉系统新生成的进程可以在作业外部执行。同时,在CreateProcess时指定CREATE_BREAKAWAY_FROM_JOB标志。

2)打开基本限制中的JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_ON标志,此时不需要在CreateProcess时指定任何标志,新进程都会脱离当前作业。


要“杀死”作业内的所有进程,只需调用以下代码:

BOOL TerminateJobObject( HANDLE hJob, UINT uExitCode );


如果想获得作业的一些通知(比如进程创建或终止运行),应该创建一个I/O完成端口内核对象,并将作业对象与完成端口关联。然后,必须有一个或多个线程等待作业通知到达完成端口,以便对它们进行处理。

1)创建完成端口,句柄为hIOCP;

2)  将完成端口和作业关联,关联以后,系统监视作业,只要有事件发生,就会将它们投递(Post)到I/O完成端口。

JOBOBJECT_ASSOCIATE_COMPLETE_PORT joacp;                                                  

joacp.CompletionKey = 1;

joacp.CompletionPort = hIOCP;

SetInformationJobObject( hJob, JobObjectAssociateCompletionPortInformation, &joacp, sizeof( jaocp ) );

3)线程通过调用GetQueuedCompletionStatus来监视完成端口。

BOOL
WINAPI
GetQueuedCompletionStatus(
    __in  HANDLE CompletionPort,
    __out LPDWORD lpNumberOfBytesTransferred,
    __out PULONG_PTR lpCompletionKey,
    __out LPOVERLAPPED *lpOverlapped,
    __in  DWORD dwMilliseconds
    );

lpNumberOfBytesTransferred的值指出了具体发生了什么事件(查询系统定义的枚举表)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值