MDL(内存描述符表) 详解
C语言中内存四区的详解

C语言中内存四区的详解C语言编程2022-05-10 14:00来自:今日头条,作者:抖点料er链接:https:///article/7046019680989037069/1、内存四区1.1数据类型本质分析1.1.1数据类型的概念•“类型”是对数据的抽象•类型相同的数据有相同的表示形式、存储格式以及相关的操作•程序中使用的所有数据都必定属于某一种数据类型1.1.2数据类型的本质•数据类型可理解为创建变量的模具:是固定内存大小的别名。
•数据类型的作用:编译器预算对象(变量)分配的内存空间大小。
•注意:数据类型只是模具,编译器并没有分酤空间,只有根据类型(模具)创建变量(实物),编译器才会分配空间。
1.2变量的本质分析1.2.1变量的概念概念:既能读又能写的内存对象,称为变量;若一旦初始化后不能修改的对象则称为常量。
变量定义形式:类型标识符,标识符,…,标识符;1.2.2变量的本质1、程序通过变量来申请和命名内存空间int a = 0。
2、通过变量名访问内存空间。
1.3程序的内存四区模型流程说明1、操作系统把物理硬盘代码load到内存2、操作系统把c代码分成四个区栈区( stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等堆区(heap):一般由程序员分配释放(动态内存申请与释放),若程序员不释放程序结束时可能由操作系统回收全局区(静态区)(statIc):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,该区域在程序结束后由操作系统释放常量区:字符串常量和其他常量的存储位置,程序结束后由操作系统释放。
程序代码区:存放函数体的二进制代码。
3、操作系统找到main函数入口执行1.4函数调用模型1.5函数调用变量传递分析(1)(2)(3)(4)(5)1.5栈的生长方向和内存存放方向相关代码:02_数据类型本质.c#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int main(){int a;//告诉编译器,分配4个字节int b[10];//告诉编译器,分配4*10个字节/*类型本质:固定内存块大小别名可以通过sizeof()测试*/printf("sizeof(a)=%d,sizeof(b)=%d\n", sizeof(a), sizeof(b));//打印地址//数组名称,数组首元素地址,数组首地址printf("b:%d,&b:%d\n",b,&b);//地址相同//b,&b数组类型不同//b,数组首地址元素一个元素4字节,+1 地址+4//&b,整个数组首地址一个数组4*10=40字节, +1 地址+40 printf("b+1:%d,&b+1:%d\n", b + 1, &b + 1);//不同//指针类型长度,32位机器32位系统下长度是 4字节// 64 64 8char********* p = NULL;int* q = NULL;printf("%d,%d\n", sizeof(p), sizeof(q));//4 , 4return0;}03_给类型起别名.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>typedef unsigned int u32;//typedef 和结构体结合使用struct Mystruct{int a;int b;};typedef struct Mystruct2{int a;int b;}TMP;/*void 无类型1.函数参数为空,定义函数时用void修饰 int fun(void)2.函数没有返回值:使用void void fun (void)3.不能定义void类型的普通变量:void a;//err 无法确定是什么类型4.可以定义 void* 变量 void* p;//ok 32位系统下永远是4字节5.数据类型本质:固定内存块大小别名6.void *p万能指针,函数返回值,函数参数*/int main(){u32 t;//unsigned int//定义结构体变量,一定要加上struct 关键字struct Mystruct m1;//Mystruct m2;//errTMP m3;//typedef配合结构体使用struct Mystruct2m4;printf("\n");return0;}04_变量的赋值.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int main(){//变量本质:一段连续内存空间别名int a;int* p;//直接赋值a = 10;printf("a=%d\n", a);//间接赋值printf("&a:%d\n", &a);p = &a;printf("p=%d\n", p);*p = 22;printf("*p=%d,a=%d\n", *p, a);return0;}05_全局区分析.c#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int main(){//变量本质:一段连续内存空间别名int a;int* p;//直接赋值a = 10;printf("a=%d\n", a);//间接赋值printf("&a:%d\n", &a);p = &a;printf("p=%d\n", p);*p = 22;printf("*p=%d,a=%d\n", *p, a);return0;}06_堆栈区分析.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>char* get_str(){char str[] = "abcdef";//内容分配在栈区,函数运行完毕后内存释放printf("%s\n", str);return str;}char* get_str2(){char* temp = (char*)malloc(100);if (temp == NULL){return NULL;}strcpy(temp, "abcdefg");return temp;}int main(){char buf[128] = { 0 };//strcpy(buf,get_str());//printf("buf = %s\n", buf);//乱码,不确定内容char* p = NULL;p = get_str2();if (p != NULL){printf("p=%s\n", p);free(p);p = NULL;}return0;}07_静态局部变量.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int* getA(){static int a = 10;//在静态区,静态区在全局区return &a;}int main(){int* p = getA();*p = 5;printf("%d\n",);return0;}08_栈的生长方向.c#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>int* getA(){static int a = 10;//在静态区,静态区在全局区return &a;}int main(){int* p = getA();*p = 5;printf("%d\n",);return0;}版权申明:内容来源网络,版权归原创者所有。
MDL(内存描述符表)详解

MDL(内存描述符表)详解MDL(内存描述符表)详解分类:初学驱动2013-01-25 18:07 308人阅读评论(0) 收藏举报mdl以下的虚拟内存可以理解成逻辑内存,因为我觉得只有这样才能讲通下面所有的东西。
以下的“未分页”指没有为页进行编码。
以下为MDL结构体(我很郁闷,我在MSDN上没有找到这个结构体)typedef struct _MDL {struct _MDL *Next; //下一个MDLCSHORT Size; //大小CSHORT MdlFlags; //标志,保护属性等struct _EPROCESS *Process;//PVOID MappedSystemVa;PVOID StartVa;ULONG ByteCount;ULONG ByteOffset;} MDL, *PMDL;如何使用MDL:一个连续的虚拟内存地址范围可能是由多个分布(spread over)在不相邻的物理页所组成的。
系统使用MDL(内存描述符表)结构体来表明虚拟内存缓冲区的物理页面布局。
我们应该避免直接访问MDL。
我们可以使用MS-Windows提供的宏,他们提供了对这个结构体基本的访问。
·MmGetMdlVirtualAddress 获取缓冲区的虚拟内存地址·MmGetMdlByteCount 获取缓冲区的大小(字节数)·MmGetMdlByteOffset 获取缓冲区开端的物理页的大小(字节数)·MmGetMdlPfnArray 获取记录物理页码的一个数组指针。
我们可以用IoAllocateMdl函数来分配一个MDL。
如果要取消分配,可是使用IoFreeMdl 函数。
或者,可以使用MmInitializeMdl来把一个之前定义的缓冲区定制成一个MDL。
但是以上两种方式都不能初始化物理页码数组。
对于在未分页池中分配的缓冲区,可以用MmBuidlMdlForNonpagedPool函数来初始化页码数组。
3. MicroStation_MDL

④ 可以利用VS提供的直观而强大的调试功能对程序进行调试。
………….
6 |
© 2009 Bentley Systems, Incorporated
7 |
启动MicroStation Developer Shell,切换到项目所在目录,键
入bmake 进行编译链接生成.ma和.dll MicroStation Developer Shell使用的是 …\MicroStation\mdl\bin\mstndevvars.bat 启动MicroStation,键入MDL LOAD project1装载应用, 然后键入project1 RUN MFCTEST能出现右图所示对话框。
© 2009 Bentley Systems, Incorporated
•
• •
mdlModelRef_xxx / mdlDgnFileObj_xxx 模型及文件操作类函数
mdlRaster_xxx / mdlRefFile_xxx 等等…… 光栅参考和矢量参考类函数
18 |
•
• •
mdlTextStyle_xxx/mdlDimStyle_xxx/mdlLineStyle_xxx/mdlMlineStyle_xx x 各种样式的操作函数
mdlVec_xxx / mdlRMatrix_xxx / mdlTMatrix_xxx 几何运算函数 mdlKISolid_xxx / mdlBspline_xxx / mdlMesh_xxx / mdlPop_xxx 三维函数
© 2009 Bentley Systems, Incorporated
(转)MDL及MDL宏MDL函数

(转)MDL及MDL宏MDL函数⼀. MDL是什么 在MSDN中有这样的定义 内存描述符列表 (MDL) 是⼀个系统定义的结构,通过⼀系列物理地址描述缓冲区。
执⾏直接 I/O 的驱动程序从 I/O 管理器接收⼀个 MDL 的指针,并通过 MDL 读写数据。
⼀些驱动程序在执⾏直接 I/O 来满⾜设备 I/O 控制请求时也使⽤ MDL。
因此通俗的解释⼀下,MDL仅仅运⽤于内核中,在应⽤层并不会涉及这个结构,由于内核中的驱动有跟应⽤层程序通信的需要,因此可能会接收到来⾃进程空间的虚拟地址,⽽在windows的分页机制下,进程空间中的任何⼀个虚拟地址所属的页⾯都有可能被内存管理器从RAW置换到页⽂件中,或者,进程被释放或是取消地址的映射。
这些都会导致严重的错误发⽣。
因此内核创建⼀个MDL,并将其与来⾃进程空间的虚拟地址相关联,当需要对这些虚拟地址进⾏读写的时候调⽤相关的内核函数,锁定这些虚拟地址对应的物理页⾯和逻辑页⾯,防⽌物理页⾯被置换,逻辑页⾯被修改或者释放。
另外⼀种情况下⼀个驱动程序在执⾏纯内核任务中也可以使⽤MDL,特别的仅仅调⽤⾮分页内存的话,这些页⾯是不会置换到页⽂件中的,因此不需要考虑锁定页⾯的问题。
⼆. MDL的内容 先看看wdm.h中MDL的定义:typedef __struct_bcount(Size) struct _MDL{struct _MDL *Next;CSHORT Size;CSHORT MdlFlags;struct _EPROCESS *Process;PVOID MappedSystemVa;PVOID StartVa;ULONG ByteCount;ULONG ByteOffset;} MDL, *PMDL; 先⼤概说明⼀下爱各个字段: Next:MDL可以连接成⼀个单链表,因此可以将分散的虚拟机地址串接起来。
Size:⼀个MDL并不单单包含结构⾥这些东西,在内存中紧接着⼀个MDL结构,存着这个MDL对应的各个物理页⾯编号,由于⼀个物理页⾯⼀定是4KB对齐的,所以这个编号相当于⼀个物理页⾯起始地址的⾼20位。
INFORMIX-体系

Informix动态服务器采用的是一种多线索体系结构,这就意味着一个进程利用自己的多个线索可以同时完成多个任务,因而整个系统只需要较少的进程就足以完成DBMS的任务。
动态服务器系统由三个主要组件构成:共享内存,进程和磁盘。
共享内存包括三个段:驻留段。
虚拟段和消息段。
驻留段主要用作磁盘数据的缓存。
虚拟段主要用作内存池以支持进程及相关的会话(session)和线索。
当客户与服务器利用共享内存进行通信时,消息段将用作两者之间的消息缓冲区。
系统中还包括若干构成数据库服务器的UNIX进程,称为oninit.这些进程被称为虚拟处理器(VP)(virtual processors)。
每一VP隶属于某一虚拟处理类。
而每一虚拟处理器类都负责完成一类特定的任务。
系统的磁盘组件由多个原始磁盘空间组成,称为chunk,chunk以页(page)为基本单位。
多个chunk集合逻辑上构成了数据空间(dbspace)。
数据空间用于存储数据库、表、系统信息以及物理日志和逻辑日志。
一个数据空间至少包括一个chunk.共享内存段动态服务器的共享内存由三个段构成:驻留段主要用作缓冲池,内含逻辑日志和物理日志的缓冲区以及其他系统数据结构。
驻留段的大小主要有系统参数BUFFERS决定。
虚拟段主要用作内存池以支持会话和线索。
根据用途的不同,内存池可以进一步分为:会话池、多线索池、字典池、存储过程池、排序池、大缓冲池以及全局池。
内存池的分配和释放是动态进行的。
内存池分配单位大小为8k。
如果已有虚拟段耗尽,动态服务器可以根据onconfig参数再次动态申请。
虚拟内存段的初始大小由参数SHMVIRTSIZE决定,其增量由参数SHMADD决定,但整个共享内存段的大小不能超过SHMTOTAL。
如果客户与服务器利用共享内存进行通讯,消息段将用作通讯缓冲区。
每一用户连接(利用共享内存进行通讯)大致占用12K空间。
用户连接的数量在参数NETTYPE中定义。
内存表功能说明

内存表功能说明一、功能实现内存表实现了数据库的简单功能,他没有单独的程序,而是作为了scf的一个模块,对内存表的命令控制不像INFORMIX有DBACCESS,ORACLE有SQLPLUS界面,而是用原来控制SCF的INACCESS程序;另外,内存表对SQL语句的支持也不像数据库那样强大,内存表可以理解成实现了简单的sql语句的简单数据库。
二、与数据库的区别内存表与数据库的区别就是,他表里的内容,完全是放在内存里的,故查询起来变得比数据库要快;另外,内存表是把第一个字段固定的作为索引的,且不能添加索引。
三、支持的命令1、memtab tables显示所有表的名称2、memtab desc <tablename>这个命令是仿照的Oracle,是显示指定表的所有字段的名称,类型,是否唯一,desc 也可以写成describe。
3、memtab version显示内存表的版本,以后如果维护,可能会改变存储文件的数据结构,如果数据结构变化以后,如果读取不是同样结构的表文件,会有问题,故表文件里都保存了一个版本。
4、memtab export <tablename>将一个表的内容导出放到一个与表的名字同名的文本文件,该文件的第一行使创建表的SQL语句,第二行及以后的内容才是真正的表的内容。
字段之间用’|’隔开,字段之间用’\n’隔开。
5、memtab import <filename>讲一个文件的内容导入表,是ememtab export的逆过程,注意第一行必须是创建表的SQL语句,生成的表名与文件名并不一定一致,与第一行的CREATE sql 有关6、memtabe wcount [number]当执行Insert,Delete,Update语句时,并不是没执行一次都要写一次文件,这样太慢了,而是执行一定数量的Insert,Delete或Update时才写一遍文件,这个命令可以设置这个数量。
3. MicroStation_MDL

15 |
© 2009 Bentley Systems, Incorporated
在VS中调试MDL
启动MS V8i; 切换到VS中,设置断点; 在VS中选菜单Debug > Attach to Process 在Attach to Process对话框中设置Attach to 为NativeCode,然后选择 ustation.exe进程并点击Attach按钮 再切换到MS V8i,装载你的MDL程序,此时,自动进入VS调试环境 其它步骤详见VS的使用说明
9 |
© 2009 Bentley Systems, Incorporated
10 |
Questions ?
© 2009 Bentley Systems, Incorporated
3. NativeCode程序的生成、运行、分析和调试
© 2009 Bentley Systems, Incorporated
MDL开发
——用C/C++开发MicroStation应用程序
Bentley开发商网络(BDN)工程师 符永安 yongan.fu@
1. 两种MDL开发模式介绍 2. MDL程序开发流程
3. NativeCode程序的生成、运行、分析和调试
Table CT_MAIN = { } Table CT_ACTION = { { 1, CT_ACTYPE, } INHERIT, NONE, "ACTION" },
© 2009 Bentley Systems, Incorporated
{ 1, CT_ACTION, PLACEMENT, REQ,
如果希望达到想开发Addins那样在VS环境中直接编译,请参考 《在VS中手工建立MDL项目.pdf》中描述的详细步骤。
驱动设备读写即通信方式

//设置 IRP 操作了多少字节 pIrp->rmation = ulReadLength; // bytes xfered
南邮王下邀月熊_驱动学习之驱动设备读写
读写方式 DO_BUFFERED_IO
缓冲区地址 Irp->AssociatedIrp.SystemBuffer
DO_DIRECT_IO
缓冲区长度:
length
=
stack->Parameters.Read.Length;
或者:
length
=
stack->Parameters.Write.Length;
int main() {
HANDLE hDevice = CreateFile("\\\\.\\HelloDDK",
南邮王下邀月熊_驱动学习之驱动设备读写
GENERIC_READ | GENERIC_WRITE,
0,
// share mode none
NULL, // no security
OPEN_EXISTING,
Irp->MdlAddress
缓冲区设备读写:
以缓冲区方式读写设备时候,操作系统会将 WriteFile 提供的用户模式的缓冲区复制到 内核模式地址下,有 WriteFile 创建的 IRP 的 AssociatedIrp.SystemBuffer 子域记录。
Ring3 层程序: #include <windows.h> #include <stdio.h>
pIrp->IoStatus.Status = status;
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
MDL(内存描述符表)详解
分类:初学驱动2013-01-25 18:07 308人阅读评论(0) 收藏举报mdl
以下的虚拟内存可以理解成逻辑内存,因为我觉得只有这样才能讲通下面所有的东西。
以下的“未分页”指没有为页进行编码。
以下为MDL结构体(我很郁闷,我在MSDN上没有找到这个结构体)
typedef struct _MDL {
struct _MDL *Next; //下一个MDL
CSHORT Size; //大小
CSHORT MdlFlags; //标志,保护属性等
struct _EPROCESS *Process;//
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
如何使用MDL:
一个连续的虚拟内存地址范围可能是由多个分布(spread over)在不相邻的物理页所组成的。
系统使用MDL(内存描述符表)结构体来表明虚拟内存缓冲区的物理页面布局。
我们应该避免直接访问MDL。
我们可以使用MS-Windows提供的宏,他们提供了对这个结构体基本的访问。
·MmGetMdlVirtualAddress 获取缓冲区的虚拟内存地址
·MmGetMdlByteCount 获取缓冲区的大小(字节数)
·MmGetMdlByteOffset 获取缓冲区开端的物理页的大小(字节数)·MmGetMdlPfnArray 获取记录物理页码的一个数组指针。
我们可以用IoAllocateMdl函数来分配一个MDL。
如果要取消分配,可是使用IoFreeMdl 函数。
或者,可以使用MmInitializeMdl来把一个之前定义的缓冲区定制成一个MDL。
但是以上两种方式都不能初始化物理页码数组。
对于在未分页池中分配的缓冲区,可以用MmBuidlMdlForNonpagedPool函数来初始化页码数组。
对于可分页的内存,虚拟内存和物理内存之间的联系是暂时的,所以MDL的页码数组只在特定的环境和时间段有效,因为很可能其他的程序对它们进行重新分配,为了使其
他的程序无法对他们进行修改和重新分配(在我们释放之前),我们就需要把这段内存锁定,防止其他程序修改,我们可以用MmProbeAndLockPages来实现,这个函数同时还为当前的布局初始化了页码数组。
当我们用MmUnlockPages来释放被锁定的内存时,页码数组也会随之无效。
假如MDL指定的是映射一块内核级别的虚拟地址空间,那么我们要用MmGetSystemAddressForMdlSafe ,这样我们就能防止映射目标是来自用户模式的空间,而来自用户模式空间的物理页只能在用户模式上下文环境中使用,并且随时可能被清空。
用函数进行申明后,就可以防止以上情况发生了。
以下这个函数是用于新创建一个MDL的:参数详解可翻阅MSDN
PMDL IoAllocateMdl(
IN PVOID VirtualAddress,
IN ULONG Length,
IN BOOLEAN SecondaryBuffer,
IN BOOLEAN ChargeQuota,
IN OUT PIRP Irp OPTIONAL
);
最后一个参数是指IRP(输入输出请求包),也就是将新建的这个MDL缓冲区和指定的IRP 关联,为什么要关联?举个例子,比如网络驱动中就应该为每一个发送到本机的IP数据报建立一个临时缓冲区,而最终用户——应用程序要读取这个缓冲区的数据,必须使用IRP
请求驱动程序来完成,所以要提取某个IP数据报,只要发送相关IRP给驱动程序,驱动程序遍历其创建的MDL链找到相应的MDL,然后进行数据提取和发送,请求处理完之后,驱动程序会自动清除这个MDL。
一个IRP可以关联多个MDL,就像上面举的例子一样,一个IRP 关联到多个IP数据报(也就是多个MDL)。
当应用成语的某个IRP要求一个新的请求(可能是一个新的IP地址的IP数据报)时,驱动程序发现没有MDL与之联,这个时候该IP发送来一个IP数据报,这时驱动程序便建立一个MDL与之关联,如果这是第一个新建的与这个IRP关联的MDL,那么驱动程把该MDL的地址赋值给MdlAddress。
如果不是第一个,那么将把新建的MDL放到上一个MDL的下一个单位,形成MDL 链。
这就是MDL结构体中的第一个成员的含义所在。
总结:MDL就是描述一块虚拟内存的结构体,里面有个成员记录了多个页码,这些页码即处于各个不同物理地址的物理块的页号。
所以要对一块受系统保护的区域进行写操作的话,可以这样来修改它的保护属性:
1.创建一个MDL,显然里面的物理页号数组没有初始化 IoAllocateMdl
2.初始化页码数组,使之成为实际有效的MDL MmBuildMdlForNonPagedPool
3.进行锁定,并且重新赋值新的保护属性为可读 MmProbeAndLockPages
4.获得我们所映射后的实际内存区域的虚拟地址 MmMapLockedPagesSpecifyCache
网上的很多代码是用于2000和其之前的OS的,很多函数都改了,方式也不一样了,以下代码用于在SSDT表所在的内核区映射一个MDL,并且修改其只读属性为可写的,然后固定这块内存,防止它被其他应用程序修改。
g_pmdlSystemCall =IoAllocateMdl
(KeServiceDescriptorTable.ServiceTableBase,KeServiceDescriptorTable.NumberOfSer vices*4,FALSE,FALSE,NULL);
if(!g_pmdlSystemCall)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(g_pmdlSystemCall);//初始化MDL页码数组
//g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags |
MDL_MAPPED_TO_SYSTEM_VA;// 改变MDL的flags为可读状态
MmProbeAndLockPages(g_pmdlSystemCall,KernelMode,IoWriteAccess);//在内存中锁定,并且指
明对它的改写权力
MappedSSDT=MmMapLockedPagesSpecifyCache( //映射被锁定内存,所谓映射,就是创建原来内存区的一个联合体,你
//修改了这个MDL,就是修改了它所描述的内存区
g_pmdlSystemCall,
KernelMode,
MeNonCached,//是否允许用作CPU缓冲区
FALSE,//当第二个参数为UserMode的时候才有效
NULL,//如果发生错误,直接返回NULL
); //MappedSSDT即映射后的SSDT地址
【注意】最后一个函数其实是大材小用了,只不过2000之后能得到映射缓冲区地址的函数中这个函数是最先进的被支持者,2000用的是MmMapLockedPages;。