总版主
|
阅读:2620回复:6
Windows驱动编程基础教程(连载4)
本文作者为楚狂人。有问题请联系QQ1619135,msn walled_river@hotmail.com
Windows驱动编程基础教程(2.3-2.4) 2.3 使用长长整型数据 这里解释前面碰到的LARGE_INTEGER结构。与可能的误解不同,64位数据并非要在64位操作系统下才能使用。在VC中,64位数据的类型为__int64。定义写法如下: __int64 file_offset; 上面之所以定义的变量名为file_offset,是因为文件中的偏移量是一种常见的要使用64位数据的情况。同时,文件的大小也是如此(回忆上一小节中定义的文件大小)。32位数据无符号整型只能表示到4GB。而众所周知,现在超过4GB的文件绝对不罕见了。但是实际上__int64这个类型在驱动开发中很少被使用。基本上被使用到的是一个共用体:LARGE_INTEGER。这个共用体定义如下: typedef __int64 LONGLONG; typedef union _LARGE_INTEGER { struct { ULONG LowPart; LONG HighPart; }; struct { ULONG LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER; 这个共用体的方便之处在于,既可以很方便的得到高32位,低32位,也可以方便的得到整个64位。进行运算和比较的时候,使用QuadPart即可。 LARGE_INTEGER a,b; a.QuadPart = 100; a.QuadPart *= 100; b.QuadPart = a.QuadPart; if(b.QuadPart > 1000) { KdPrint(“b.QuadPart < 1000, LowPart = %x HighPart = %x”, b.LowPart,b.HighPart); } 上面这段代码演示了这种结构的一般用法。在实际编程中,会碰到大量的参数是LARGE_INTEGER类型的。 2.4使用自旋锁 链表之类的结构总是涉及到恼人的多线程同步问题,这时候就必须使用锁。这里只介绍最简单的自选锁。 有些读者可能疑惑锁存在的意义。这和多线程操作有关。在驱动开发的代码中,大多是存在于多线程执行环境的。就是说可能有几个线程在同时调用当前函数。 这样一来,前文1.2.2中提及的追加链表节点函数就根本无法使用了。因为MyFileInforAppendNode这个函数只是简单的操作链表。如果两个线程同时调用这个函数来操作链表的话:注意这个函数操作的是一个全局变量链表。换句话说,无论有多少个线程同时执行,他们操作的都是同一个链表。这就可能发生,一个线程插入一个节点的同时,另一个线程也同时插入。他们都插入同一个链表节点的后边。这时链表就会发生问题。到底最后插入的是哪一个呢?要么一个丢失了。要么链表被损坏了。 如下的代码初始化获取一个自选锁: KSPIN_LOCK my_spin_lock; KeInitializeSpinLock(&my_spin_lock); KeInitializeSpinLock这个函数没有返回值。下面的代码展示了如何使用这个SpinLock。在KeAcquireSpinLock和KeReleaseSpinLock之间的代码是只有单线程执行的。其他的线程会停留在KeAcquireSpinLock等候。直到KeReleaseSpinLock被调用。KIRQL是一个中断级。KeAcquireSpinLock会提高当前的中断级。但是目前忽略这个问题。中断级在后面讲述。 KIRQL irql; KeAcquireSpinLock(&my_spin_lock,&irql); // To do something … KeReleaseSpinLock(&my_spin_lock,irql); 初学者要注意的是,像下面写的这样的“加锁”代码是没有意义的,等于没加锁: void MySafeFunction() { KSPIN_LOCK my_spin_lock; KIRQL irql; KeInitializeSpinLock(&my_spin_lock); KeAcquireSpinLock(&my_spin_lock,&irql); // To do something … KeReleaseSpinLock(&my_spin_lock,irql); } 原因是my_spin_lock在堆栈中。每个线程来执行的时候都会重新初始化一个锁。只有所有的线程共用一个锁,锁才有意义。所以,锁一般不会定义成局部变量。可以使用静态变量、全局变量,或者分配在堆中(见前面的1.2.1内存的分配与释放一节)。请读者自己写出正确的方法。 LIST_ENTRY有一系列的操作。这些操作并不需要使用者自己调用获取与释放锁。只需要为每个链表定义并初始化一个锁即可: LIST_ENTRY my_list_head; // 链表头 KSPIN_LOCK my_list_lock; // 链表的锁 // 链表初始化函数 void MyFileInforInilt() { InitializeListHead(&my_list_head); KeInitializeSpinLock(&my_list_lock); } 链表一旦完成了初始化,之后的可以采用一系列加锁的操作来代替普通的操作。比如插入一个节点,普通的操作代码如下: InsertHeadList(&my_list_head, (PLIST_ENTRY)& my_file_infor); 换成加锁的操作方式如下: ExInterlockedInsertHeadList( &my_list_head, (PLIST_ENTRY)& my_file_infor, &my_list_lock); 注意不同之处在于,增加了一个KSPIN_LOCK的指针作为参数。在ExInterlockedInsertHeadList中,会自动的使用这个KSPIN_LOCK进行加锁。类似的还有一个加锁的Remove函数,用来移除一个节点,调用如下: my_file_infor = ExInterlockedRemoveHeadList ( &my_list_head, &my_list_lock); 这个函数从链表中移除第一个节点。并返回到my_file_infor中。 |
最新喜欢:greenp... |
沙发#
发布于:2008-05-23 14:34
强烈支持!!!
|
|
板凳#
发布于:2008-05-23 14:41
必顶!!
|
|
地板#
发布于:2008-05-23 22:00
强烈支持
|
|
地下室#
发布于:2008-05-23 22:32
太高深了!!!! 顶!!!
|
|
|
5楼#
发布于:2008-05-26 11:17
强烈建议出一本书
|
|
6楼#
发布于:2008-05-30 16:10
找到了 顶起来 全力支持楚大大
|
|