频道栏目
首页 > 资讯 > 安全编程 > 正文

驱动笔记2:在驱动中使用链表

09-01-09        来源:[db:作者]  
收藏   我要投稿
 

文章作者:grayfox
作者主页:http://nokyo.blogbus.com
原始出处:http://nokyo.blogbus.com/logs/33271026.html 

    在驱动程序的开发中经常需要用到链表,常见的链表有单向链表和双向链表,我们只介绍双向链表的使用方法,DDK为我们提供了标准的双向链表LIST_ENTRY,但这个链表里面没有数据,不能直接使用,我们需要自己定义一个结构体类型,然后将LIST_ENTRY作为结构体的一个子域,如下所示:

typedef struct _MYDATASTRUCT{
    ULONG number;
    LIST_ENTRY ListEntry;
} MYDATASTRUCT, *PMYDATASTRUCT;

    实际上把LIST_ENTRY放在结构体的第一个子域才是较好的做法,此处我们不过多地关心,反正用法都是大同小异。下面我们就在驱动程序中创建一个链表,使用刚刚定义的结构体作为节点类型。代码如下所示:

VOID  LinkListTest()
{
    LIST_ENTRY linkListHead;  // 链表
    PMYDATASTRUCT pData;  // 节点数据
    ULONG i = 0;     // 计数

    //初始化
    InitializeListHead(&linkListHead); 
    //向链表中插入10个元素
    KdPrint(("[ProcessList] Begin insert to link list"));
    for (i=0 ; i<10 ; i++)
    {     // pData是我们定义的指针,必须被初始化后才能使用
          pData = (PMYDATASTRUCT)ExAllocatePool(PagedPool,sizeof(MYDATASTRUCT));
          pData->number = i;
          // 将其作为一个节点插入链表
          InsertHeadList(&linkListHead,&pData->ListEntry);
    }
 
     // 从链表中取出所有数据并显示
     KdPrint(("[ProcessList] Begin remove from link list "));
     while(!IsListEmpty(&linkListHead))
     {
           // 取出一个节点
           PLIST_ENTRY pEntry = RemoveTailList(&linkListHead);
           // 获取节点内容
           pData = CONTAINING_RECORD(pEntry, MYDATASTRUCT, ListEntry);
           KdPrint(("%d ",pData->number));
           // 释放节点,ExAllocatePool必须与ExFreePool成对使用
           ExFreePool(pData);
      }
}

    上述代码可以正常地通过编译并运行,但其中存在着一个很大的隐患:它不是多线程安全的。如果有多个线程同时操作同一个链表的话,可能会引发不可预料的后果,我们可以通过使用自旋锁来避免,修改后的代码如下所示:

VOID  LinkListTest()
{
    LIST_ENTRY linkListHead;  // 链表
    PMYDATASTRUCT pData;  // 节点数据
    ULONG i = 0;     // 计数
    KSPIN_LOCK spin_lock; // 自旋锁
    KIRQL  irql;    // 中断级别

    // 初始化
    InitializeListHead(&linkListHead);
    KeInitializeSpinLock(&spin_lock);
 
    //向链表中插入10个元素
    KdPrint(("[ProcessList] Begin insert to link list"));
    // 锁定,注意这里的irql是个指针
    KeAcquireSpinLock(&spin_lock, &irql);
    for (i=0 ; i<10 ; i++)
    {
         pData = (PMYDATASTRUCT)ExAllocatePool(PagedPool,sizeof(MYDATASTRUCT));
         pData->number = i;
         InsertHeadList(&linkListHead,&pData->ListEntry);
    }
    // 解锁,注意这里的irql不是指针
    KeReleaseSpinLock(&spin_lock, irql);
 
    //从链表中取出所有数据并显示
    KdPrint(("[ProcessList] Begin remove from link list "));
    // 锁定
    KeAcquireSpinLock(&spin_lock, &irql);
    while(!IsListEmpty(&linkListHead))
    {
         PLIST_ENTRY pEntry = RemoveTailList(&linkListHead);
         pData = CONTAINING_RECORD(pEntry, MYDATASTRUCT, ListEntry);
         KdPrint(("%d ",pData->number));
         ExFreePool(pData);
    }
    // 解锁
    KeReleaseSpinLock(&spin_lock, irql);
}

    上述代码介绍了自旋锁的使用方法,但需要注意的是:上面这段代码在实际应用中是没有任何价值的。因为在上述代码我们定义的锁是一个局部变量,被分配在栈中,这样每个线程在调用该函数的时候,都会重新初始化一个锁,因此这个锁就失去了本来的作用。在实际的编程中,我们应该把锁定义成一个全局变量,或者静态(static)变量,或者将其创建在堆空间中。

    另外,我们还可以为每个链表都定义并初始化一个锁,在需要向该链表插入或移除节点时不使用前面介绍的普通函数,而是使用如下方法:

ExInterlockedInsertHeadList(&linkListHead, &pData->ListEntry, &spin_lock);
pData = (PMYDATASTRUCT)ExInterlockedRemoveHeadList(&linkListHead, &spin_lock);

    此时在向链表中插入或移除节点时会自动调用关联的锁进行加锁操作,有效地保证了多线程安全性。上述代码的运行效果如下图所示。



相关TAG标签
上一篇:瑞星全功能安全软件2009 深度评测
下一篇:驱动笔记1:第一个驱动程序
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站