《Windows核心编程》笔记2 --- 内核对象二

续上......

内核对象如何在进程边界共享?
1.对象句柄的继承
    当进程具有父子关系,就能使用对象句柄的继承性。父进程有一个或多个对象句柄,并且父进程可以决定生成一个子进程,为子进程赋于父进程的内核对象的访问权。
    具体实现:
    在安全描述符中指定,如:
   
SECURITY_ATTRIGBUTES sa
    sa.nLength
=sizeof(sa);
    sa.lpSecuntyDescriptor
=null;
    sa.bInherithandle
=TRUE; //指定子进程可以继承该父进程的内核对象。
    HANDLE hMutex    =    CreateMutex(@sa,FALSE,NULL);
   
    然后,在进程中使用CreateProcess函数:
BOOL CreateProcess(
   PCTSTR pszApplicationName,
   PTSTR pszCommandLine,
   PSECURITY_ATTRIBUTES psaProcess,
   PSECURITY_ATTRIBUTES psaThread,
   BOOL bInheritHandles,
   DWORD fdwCreale,
   PVOIO pvEnvironment,
   PCTSTR pszCurDir,
   PSTARTUPINFO psiStartInfo,
   PPROCESS_INFORMATION ppiProcInfo);
为bInheritHandles指定TRUE,则告诉系统,子进程继承父进程的句柄表中的可继承句柄。

2.改变句柄标志:
    因为每个进程都会有个内核对象句柄表,指明该进程所拥有的句柄信息。那么,我们可以直接设置进程的句柄表,以获取某个内核对象的访问权。
    句柄表的样式:
索引 内核对象内存块的指针 访问屏蔽(标志位的D W O R D ) 标志(标志位的D W O R D )
1 0 x 0 0 0 0 0 0 0 0 (无) (无)
2 0 x 0 0 0 0 0 0 0 0 (无) (无)
3 0 x F 0 0 0 0 0 1 0 0 x ? ? ? ? ? ? ? ? 0 x 0 0 0 0 0 0 0 1
    设置句柄标志的函数:
BOOL SetHandleInformation(
   HANDLE hObject,
   DWORD dwMask,
   DWORD dwFlags);
可以看到,该函数拥有3 个参数。第一个参数h O b j e c t 用于标识一个有效的句柄。第二个参数d w M a s k 告诉该函数想要改变哪个或那几个标志。目前有两个标志与每个句柄相关联:
#define HANDLE FLAG_INHERIT 0x00000001
#define HANDLE FLAG PROTECT FROM CLOSE 0x00000002
比如,打开一个内核对象的继承标志,则可以这样:
SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
关闭该标志,则可以:
SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, 0);

3.给内核对象命名
    创建许多内核对象可以进行命名,如:
   
HANDLE CreateMutex(
   PSLCURITY_ATTRIBUTES psa,
   BOOL bInitialOwner,
   PCTSTR pszName);

HANDLE CreateEvent(
   PSECURITY_ATTRIBUTES psa,
   BOOL bManualReset,
   BOOL bInitialState,
   PCTSTR pszName);

HANDLE CreateSemaphore(
   PSECURITY_ATTRIBUTES psa,
   LONG lInitialCount,
   LONG lMaximumCount,
   PCTSTR pszNarne);

HANDLE CreateWaitableTimer(
   PSLCURITY_ATTRIBUTES psa,
   BOOL bManualReset,
   PCTSTR pszName);

HANDLE CreateFileMapping(
   HANDLE hFile,
   PSECURITY_ATTRIBUTES psa,
   DWORD flProtect,
   DWORD dwMaximumSizeHigh,
   DWORD dwMaximumSizeLow,
   PCTSTR pszName);

    如果创建时最后的参数设置NULL,则创建一个末名命的内核对象当创建一个未命名的对象时,可以通过使用继承性(如上一节介绍的那样)或D u p l i c a t e H a n d l e (下一节将要介绍)共享跨越进程的对象。若要按名字共享对象,必须为对象赋予一个名字
    实现:我们在ProcessA中创建一个内核对象:
    HANDLE hMutexPronessA = CreateMutex(NULL, FALSE, "JeffMutex");
    然后,在ProcessB中创建一个与ProcessA中同名的内核对象:
    HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, "JeffMutex");
    下面,会发生什么情况?以下是书中所揭示的:
    当Process B 调用C r e a t e M u t e x 时,系统首先要查看是否已经存在一个名字为“J e ff M u t e x ”的内核对象。由于确实存在一个带有该名字的对象,因此内核要检查对象的类型。由于试图创建一个互斥对象,而名字为“J e ff M u t e x ”的对象也是个互斥对象,因此系统会执行一次安全检查,以确定调用者是否拥有对该对象的完整的访问权。如果拥有这种访问权,系统就在Process B 的句柄表中找出一个空项目,并对该项目进行初始化,使该项目指向现有的内核对象。如果该对象类型不匹配,或者调用者被拒绝访问,那么C r e a t e M u t e x 将运行失败(返回N U L L )。当Process B 对C r e a t e M u t e x 的调用取得成功时,它并不实际创建一个互斥对象。相反,Process B 只是被赋予一个与进程相关的句柄值,用于标识内核中现有的互斥对象。当然,由于Process B 的句柄表中的一个新项目要引用该对象,互斥对象的使用计数就会递增。在Process A和Process B 同时关闭它们的对象句柄之前,该对象是不会被撤消的。请注意,这两个进程中的句柄值很可能是不同的值。这是可以的。Process A 将使用它的句柄值,而Process B 则使用它自己的句柄值来操作一个互斥内核对象。

按名字共享对象的另一种方法是,进程不调用C r e a t e *函数,而是调用下面显示的O p e n *函数中的某一个,如:
HANDLE OpenMutex(
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   PCTSTR pszName);

HANDLE OpenEvent(
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   PCTSTR pszName);

HANDLE OpenSemaphore(
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   PCTSTR pszName);

HANDLE OpenWaitableTimer(
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   PCTSTR pszName);

HANDLE OpenFileMapping(
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   PCTSTR pszName);

调用Create*与调用Open*来创建打开内核对象有什么区别呢?
调用C r e a t e *函数与调用O p e n *函数之间的主要差别是,如果对象并不存在,那么C r e a t e *函数将创建该对象,而O p e n *函数则运行失败。

4.复制内核对象句柄:
    共享跨越进程边界的内核对象的最后一个方法是使用D u p l i c a t e H a n d l e 函数:
BOOL DuplicateHandle(
   HANDLE hSourceProcessHandle,
   HANDLE hSourceHandle,
   HANDLE hTargetProcessHandle,
   PHANDLE phTargetHandle,
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   DWORD dwOptions);

简单说来,该函数取出一个进程的句柄表中的项目,并将该项目拷贝到另一个进程的句柄表中。

二,进程

1.何为进程,进程有何特性?
    所谓进程,就是一个正在运行程序的实例。它由两部分组成:
    • 一个是操作系统用来管理进程的内核对象。
    • 另一个是地址空间,它包含所有可执行模块或D L L 模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。

   注意,进程是不活泼的,即进程完成的操作,是需要靠进程中的线程来完成。每个进程都必须有一个线程,也可以有多个线程。线程负责执行包含在进程地址空间内的代码。所谓多线程,就是多个线程同时执行包含在地址空间内的代码。
    若要使线程执行进程内的代码,操作系统必须为线程分配CPU时间片。它是以一种循环的方式来提供时间片的,即给每个线程多少时间,时间完成之后,收回CPU,转交给另外的线程。
    创建一个进程,系统会自动为其进程创建一个主线程。然后,可利用该线程可以创建其它的线程,其正是多线程。
posted @ 2005-08-24 11:04  shipfi  阅读(896)  评论(1编辑  收藏  举报