第2章

1.用户态和内核态
2.单个进程内的编程特点:
 a.自由使用通用寄存器
 b.如果一个进程对一个变量取地址,然后把这个地址传递给其他进程,那么另一个进程看见的地址中的内容是一样的吗?答案是否定的,每个进程的用户空间内存是隔离的。
 c.大多数操作系统要求进程在使用TCP协议时必须要打开一个端口,以避免和其他进程冲突。
3.进程的空间实际上被分成了两个部分。一部分供进程独立使用,成为用户空间;另一个部分容纳操作系统的内核,成为内核空间,或称为系统空间。具体到可以容纳4GB内存空间的32位windows系统上,低2GB是用户空间,高2GB是内核空间。
4.用户空间是各个进程隔离的,但是内核空间是共享的。也就是说,每个进程看见的高2GB空间范围内的数据,应该是一样的!!!
5.我们所编写的内核模块,并非是和普通程序一样作为一个进程执行,而是运行在内核空间,成为操作系统的一个模块,最终被所有需要该模块提供功能的应用程序调用。
6.初学者在编写一个内核模块时,常常有一个疑问:这些代码运行在哪个进程的空间中呢?
 内核模块位于内核空间,被所有的进程共享着。因此,内核模块实际上是位于任何一个进程空间中。但是任意一段代码的任意一次执行,一定是位于某个进程空间中的。这个进程是哪一个呢?这就取决于请求的来源、处理的过程等。
7.有人可能会误以为所有内核代码都运行在系统进程内。
 windows的所谓系统进程是一个名为“System”的进程,在xp下PID始终是4.DriverEntry函数被调用时,一般都位于系统进程中。这是因为windows一般都用系统进程来加载内核模块,并不说明内核代码始终运行在System进程里。
8.WDK对数据类型进行了重新定义:
 unsigned long 重定义为ULONG.
 unsigned char 重定位为UCHAR.
 void 重定义为VOID.
 unsigned long* 重定义为PULONG.
 unsigned char* 重定义为PUCHAR
 unsigned int* 重定义为PUINT
 void* 重定义为PVOID。
9.绝大部分内核API的返回值都是一个返回状态,也就是一个错误码。这个类型为NSTATUS.使用NT_SUCCESS()可以判断一个返回值是否成功。
10.如果碰到某个函数返回了奇特的NTSTATUS的值,请注意在帮助里往往是搜索不到的,正确的方法是在WDK的头文件(如:ntstatus.h)中去寻找答案。
11.驱动力字符串一般用一个结构来容纳。是一个宽字符串。这个结构的指针可以直接在DbgPrint中打印。或者这样:
UNICODE_STRING str = RTL_CONSTANT_STRING(L"first:Hello,my salary!");
DbgPrint("%wZ",&str);
12.一个驱动对象(DRIVER_OBJECT)代表了一个驱动程序,或者说一个内核模块。内核模块并不生成一个进程,只是填写一组回调函数让windows来调用,而这组回调函数必须符合windows内核规定。这组回调函数就包括“普通分发函数”和“快速IO分发函数”。
13. 设备对象(DEVICE_OBJECT,简称DO)相当于应用程序中的窗口,窗口是唯一可以接收消息的东西。而在内核世界里,大部分“消息”都以请求(IRP)的方式传递。而设备对象是唯一可以接收请求的实体,任何一个“请求”都是发送给某个设备对象的。
14.一个设备对象总是属于一个驱动对象!!!在一个驱动对象中有N个设备对象,这些设备对象用指针连接起来作为一个单向的链表。
15.IRP也是一个内核数据结构,这个结构非常复杂,这是因为这个结构要表示无数种实际请求的缘故。
16.一个请求并非简单的一个输入,并等待一个输出,而是经过许多中转才得以完成。一个IRP往往要传递n个设备才能得以完成。而在中转的过程中,输入都可能会改变,所以可变部分的输入信息保存在一个栈的结构中,也即IRP栈空间。
17.如果函数B调用了函数A,则称函数B为A的调用者。一段代码的调用源是指,调用的这段代码,编程者所能见到的最初始的源头的那个函数。
18.在编程过程中,一定要注意函数的多线程的安全性!多线程安全性是指,一个函数在被调用过程中,还没有返回时,又被其他线程调用的情况下,函数执行结果的可靠性。
19.如果要在多线程环境下调用一个不可重入的函数,必须要在调用路径上采取多线程序列化成单线程的强制措施。
20.在用户态编程中,没有中断级的概念,这是因为用户态程序都运行在同一中断级。但是在内核编程中,就常出现运行的代码处在不同的中断级的情况。
21.中断级主要有Passive级和Dispatch级两种,Dispatch级比Passive级高。大部分较复杂功能的内核API都要求必须在Passive级执行,只有较简单的函数能在Dispatch级执行。
22.
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, NdisProtUnload)
#pragma alloc_text这个宏仅仅用来指定某个函数的可执行代码在编译出来后在sys文件中的位置。
内核模块编译出来之后是一个PE格式的sys文件,这个文件的 代码段(text段)中有不同的节(Section),INIT节的特点就是在初始化完毕之后就被释放。PAGE节的特点是位于可以进行分页交换的内存空间,这些空间在内存紧张时可以被交换到硬盘上以节省内存。如果没有用预编译指令处理,则代码默认位于PAGELK节,加载后位于不可分页交换的内存空间中。
23.放在PAGE节中的函数不可以在Dispatch级调用,因为这种函数的调用可能诱发缺页中断。一般都用一个宏PAGED_CODE()进行测试。如果发现当前中断级为Dispatch级,则程序直接报异常。
posted @ 2009-08-03 21:19  小试锋芒  阅读(206)  评论(1编辑  收藏  举报