Windows_内核对象

一、概念:

  1.1、句柄是属于当前进程的。而句柄对应了一个内核对象。

  1.2、任意一个进程都可以来使用任意的内核对象,进程只有使用的权限。

  1.3、使用计数:为了使得多个进程可以使用同一个内核对象,节约系统资源。并且可以使得内核对象能够顺利的释放。

  1.4、每个都有一个内核对象安全性的设置(一般设置成null,让他继承当前进程中的安全性):

    能够决定哪些用户可以访问当前内核对象的权限(有不同的用户级)、当一个进程设置了一个内核对象的安全性设置之后,其他进程想要访问这个内核对象的话

    必须要符合前一个进程所设置的安全性设置(比如说一个进程对file这个内核对象进行了写的设置,其他进程将无法进行读的操作)。

  1.5、进程内核对象:在R3层中的进程仅仅是R0层中进程内核对象的一个镜像。

    1.5.1、在进程内核对象中存在一个句柄表(索引,指向内核对象的指针,标识)。

    1.5.2、R3层是全开放的。

    1.5.3、R0层是系统及软件运行的地方

  1.6、句柄是不能够夸进程而存在的。句柄只是在当前进程中有效。

  1.7、API会发送一条指令,比如说CreateFile,会启动一个file的Obj(内核的Obj),这个Obj有自己的地址,而且会生成一个索引(以4字节的方式进行递增),

    同一内核对象在不同进程中的索引值是不同的,句柄就是一个索引。

  1.8、整个进程:当系统启动之后、进程会去指定自己的空间、同时会在自己的内核对象当中开辟一段空间(这段空间主要用于存放句柄表)。

  1.9、当内核对象完成了初始化、当整个进程完成了初始化:此时操作系统会帮我们启动一条叫MainThread的主线程,最开始会来CreateThread。

  1.10、会创建一个主线程内核对象,这时候会去句柄表中找空白的区域,当找到空白的区域之后,就会将主线程句柄的地址放入到当前句柄表里面、索引被填充。

  1.11、CloseHandle可以将使用的对象进行关闭,对于R3层来说有意义的就只是索引。

     句柄表存在的意义在于:当我们忘记关闭某一个在当前进程中使用到的内核对象的时候,操作系统可以根据句柄表来进行内核对象中使用计数进行减,如果此时

     没有任何进程使用当前的内核对象的话,操作系统会对此内核对象进行回收。因为在当前进程中存在着这个句柄表,而句柄表记录着当前进程所使用到的所有的内核对象使用情况。

  1.12、内核对象的Signal状态:初始化的时候一般FALSE。

  1.13、内核对象有:进程、线程、标准输入输出流、事件内核对象、互斥体、信号、可等待计时器、作业。

  1.14、所有的这些内核对象中都存在一个Signal(受信状态),有信号状态时,是可调度情况(在执行的线程是不可调度状态)。

  1.15、Wait的三种结果:

case WAIT_TIMEOUT:
    printf("!等待超时\r\n");
    break;
case WAIT_OBJECT_0:
    printf("成功等待\r\n");
    break;
case WAIT_FAILED:
    printf("失败\r\n");
    break;

  1.16、WaitForMultipleObjects,是永远都获取不到二号线程的完成标识的。

     WaitForSingleObject();函数会使得当前线程变成不可调度的状态。

  1.17、事件内核对象:自动重置、手动重置。

  1.18、

HANDLE WINAPI CreateEvent(
  _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,    //安全扫描
  _In_ BOOL bManualReset,    //手动或自动重置
  _In_ BOOL bInitialState,    //有信号还是无信号
  _In_opt_ LPCTSTR lpName    //创建的事件内核对象的名字,可以为null
);

  1.19、CreateEvent配合WaitForSingleObject使用:

    1.19.1、手动:当bManualReset参数设置为TRUE的话,WaitForSingleObject函数不能改变事件内核对象的信号状态。

        此时我们只能手动的进行信号状态的改变,ResetEvent函数使得无信号,SetEvent函数使得有信号。

    1.19.2、自动:当bManualReset参数设置为FALSE的话,WaitForSingleObject函数自动改变事件内核对象的信号状态。

  1.20、SetEvent(HANDLE hEvent) //函数将该事件设为通知状态

     ResetEvent(HANDLE hEvent)//函数将该事件设为未通知状态

     已通知可执行,未通知等待

  1.21、在我们开发的时候可能需要,在某一些时间点上启动某个线程。或者按照某些频率来启动某些线程。

1.22、可等待计时器内核对象:

HANDLE WINAPI CreateWaitableTimer(
    _In_opt_ LPSECURITY_ATTRIBUTES lpTimerAttributes,
    _In_ BOOL bManualReset,    //一般设置为FALSE,自动
    _In_opt_ LPCTSTR lpTimerName
);

  自动的时候,当每碰到一个Wait函数的时候就会改变当前信号状态。

    1.22.1、创建完成,是没有信号的状态。

    1.22.2、设置有信号:

BOOL WINAPI SetWaitableTimer(
  _In_ HANDLE hTimer,
  _In_ const LARGE_INTEGER *pDueTime,        //UAC时间,指定被触发有信号的时间(纳秒)。
  _In_ LONG lPeriod,                  //以毫秒为单位,每过多少时间触发有信号。
  _In_opt_ PTIMERAPCROUTINE pfnCompletionRoutine,    
  _In_opt_ LPVOID lpArgToCompletionRoutine,   //传入pfnCompletionRoutine中的第一个参数
  _In_ BOOL fResume
);

    pfnCompletionRoutine参数:

VOID CALLBACK TimerAPCProc(
  _In_opt_ LPVOID lpArgToCompletionRoutine,
  _In_ DWORD dwTimerLowValue,
  _In_ DWORD dwTimerHighValue
);

    这个函数会被压入SetWaitableTimer线程的APC中。当线程空闲下来的时候,会去清理APC中的一些东西

    SleepEx(INFINITE, TRUE);//回调函数 TimerAPCProc会被调用,只有调用SleepEx函数才会被调用

    1.22.3、while(WaitForSingleObject(hTimer, INFINITE) != WAIT_TIMEOUT){}:操作系统中的算法尽可能公平。

  1.23、信号量:决定了线程可以启动多少次。

    1.23.1、

HANDLE WINAPI CreateSemaphore(
  _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  _In_ LONG lInitialCount,
  _In_ LONG lMaximumCount,
  _In_opt_ LPCTSTR lpName
);

    1.23.2、这个内核对象中存在一个count,这个count决定了能够Wait多少次。

    1.24.3、

BOOL WINAPI ReleaseSemaphore(       //这个函数可以在初始化的基础上设置count的大小
  _In_ HANDLE hSemaphore,    
  _In_ LONG lReleaseCount,        //在初始化的基础上加上多少count。
  _Out_opt_ LPLONG lpPreviousCount
);

  1.24、互斥体(常用且特殊):

    1.24.1、在每个互斥体中会保存一个线程ID的对象,而线程ID将决定这个内核对象时有信号状态还是无信号状态。

      线程ID为0时是处于有信号状态,线程可以被激活,当这个线程被激活之后会将激活它的这个线程ID转递到内核对象当中。

      此时使得这个内核对象变成无信号状态。

    1.24.2、和关键段达到一样的效果,可以来做线程的同步。

    1.24.3、

HANDLE WINAPI CreateMutex(
  _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_ BOOL bInitialOwner,                  //初始化这条线程的时候是否拥有它,换句话说:在初始化的时候是否将当前这条线程的线程ID专递进去。
                                //如果专递了这个线程ID,那创建处理的这个互斥体就是一个无信号状态。
  _In_opt_ LPCTSTR lpName
);

    1.24.4、互斥体是和线程绑定的,对于当前创建这个Mutex这个线程来说,他已经拥有了这个Mutex的权限。所以在当前这个线程中不管来Wait多少次,都是有信号的状态。

    1.24.5、线程ID -> 非0(无信号)、0(有信号):当某一个非当前线程的线程Wait之后就会对线程ID进行填充,使得这个Mutex变成无信号状态。

      此时进行ReleaseMutex(hMutex);释放,这个Mutex又会变成有信号状态。

    1.24.6、互斥体中还有一个等待计数:Wait多少次就要释放多少次。

    1.24.7、哪个线程进行了Wait就需要在哪个线程进行释放,因为释放的时候会检查线程ID是否符合

    1.24.8、当线程消亡了,Mutex会变成有信号状态。

  1.25、内核对象时属于内核所有的,而内核属于操作系统的。

    1.25.1、所有的内核对象都是能够跨进程的。如果在一个进程中使用了指定名称的Mutex,在另一个进程中就无法使用同一名称的Mutex,或延续使用

posted @ 2017-08-24 11:01  _xiaohaige  阅读(566)  评论(0编辑  收藏  举报