1.什么是内核对象
像进程、线程、文件、互斥体、事件等在内核都有一个对应的结构体,这些结构体由内核负责管理。我们管这样的对象叫做内核对象。
红色部分就是内核层对应的结构体。
Q:内核层中的结构体和内核对象是一个东西吗,还是有什么关系呢?
2.如何管理内核对象
一个进程可以创建进程、线程、事件、文件,在内核中也会对应一个内核对象(结构体)。
- 每个内核结构体在内存:80000000之上,所以这些内核结构体是共用的,而这个地址是不可以随意的传给相应用户层函数的,为什么呢?
- 因为如果用户层函数不小心更改了该地址值,那么就会导致后续访问到错误的地址,因为在内核层,这样就会导致系统蓝屏。所以内核结构体是不能直接传该地址给函数的。
- 那要怎么让函数操纵内核结构体呢,请看下文。
3.每个进程都有一个句柄表
- 要回答上面的问题,就产生了句柄表。句柄表是一张表,如上图左下角,A对应的是A结构体对应的地址,1表示地址对应的编号,编号是自己起的。
- 然后把编号传回给相关函数,这样函数拿到的就是具体的句柄值,当要使用函数对应的内核层结构体时,通过句柄值取到对应句柄表的地址,然后根据地址访问到对应的内核空间。
4.多进程共享一个内核对象
由上图所示,A进程要访问A的内核层空间,而B进程要使用A的内核空间,那么就拥有了计数这个概念,A进程使用了A内核空间,那么A内核空间的计数为1,B进程再使用了A内核空间,那么计数值就增加了1,变成了2。这样就达到了A、B进程共用A地址空间的局面。
要想移除A空间,要满足两个条件。其一:A内核空间的计数值减为0。其二:在内核计数为0的基础上,将对应线程关闭,内核对象才会被移除。对应的函数为:CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
Q:移除内核对象,会将内核层的内核结构体删除掉吗?
5.句柄是否可以被继承
继承了能有什么影响?
创建进程、线程、事件、文件时,创建函数时会有一个安全描述符成员,内部有一个变量就是表示是否继承父进程的句柄表·。
可以看到左下角的表中,第一列就是表示是否允许继承,如果该函数允许继承,那么该表中数字为1,否则为0。
Q:安全描述符中表示的是本函数可以被继承,还是说是否接受父进程的继承?
6.句柄是否“允许”被继承
和5有啥区别??????????
7.代码演示
#include "windows.h"
#include <stdio.h>
BOOL CreateProcess(PTCHAR route, PTCHAR szCommandLine) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (!CreateProcess(
route,
szCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi
)) {
printf("EROOR:%d\n", GetLastError());
return FALSE;
}
SuspendThread(pi.hThread); //暂停执行
ResumeThread(pi.hThread); //继续执行
//释放句柄
CloseHandle(pi.hProcess);//相关计数器值减一
CloseHandle(pi.hThread);
return TRUE;
}
int main(int argc, char* argv[]) {
TCHAR route[] = TEXT("C://Program Files (x86)//Google//Chrome//Application//chrome.exe");
TCHAR szCmdLine[] = TEXT("https://baidu.com");
CreateProcess(route, szCmdLine);
return 0;
}
Q:进程的计数器减一和线程计数器是同一个计数器吗?
8.总结
句柄实际是一个指针,他指向一块包含具体信息数据的内存,可以当做索引 ,所以进程句柄是当你要访问该进程时取得的,使用完毕必须释放。