技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 1014|回复: 8
收起左侧

【多线程】在Windows内核中玩多线程编程

[复制链接]

38

主题

108

帖子

4789

积分

用户组: 管理员

UID
1043
精华
19
威望
223 点
宅币
3679 个
贡献
461 次
宅之契约
0 份
在线时间
743 小时
注册时间
2015-8-15
发表于 2019-6-20 17:55:55 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
记得很久以前,毛利鸣人在群里问过我在Windows内核里玩多线程的事。我当时说:内核里线程概念没那么重了。
emmmm,这话该说是对是错我还说不清楚。因为在高中断请求级下,就没有线程的概念了。所谓的线程同步在高中断请求级下应该称之为多核同步。
不过在Windows内核里,玩玩多线程还是可行的,这里通过类比POSIX线程做个简单的介绍。


系统线程的创建与终止:
在Windows内核中,通过导出函数PsCreateSystemThread来实现创建系统线程。类似于POSIX线程中pthread_create函数。函数原型如下:
[C] 纯文本查看 复制代码
NTSTATUS PsCreateSystemThread(OUT PHANDLE ThreadHandle,IN ULONG DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,IN HANDLE ProcessHandle OPTIONAL,OUT PCLIENT_ID ClientId OPTIONAL,IN PKSTART_ROUTINE StartRoutine,IN PVOID StartContext);

参数介绍如下:
ThreadHandle:                用于接收被创建的线程句柄。句柄不再需要使用之后请关闭句柄。
DesiredAccess:                句柄对线程所拥有的权限,它是一个ACCESS_MASK类型的掩码。
ObjectAttributes:        用于描述线程对象句柄的属性。不可以设置为OBJ_PERMANENT, OBJ_EXCLUSIVE, 和 OBJ_OPENIF。从Windows XP起,如果该系统线程不被归入系统进程的上下文,则必须设置OBJ_KERNEL_HANDLE属性。在Windows 2000/98/Me中,该函数必须在系统进程上下文中调用。从Windows Vista起,该函数返回的句柄必然是内核句柄。
ProcessHandle:                用于指定被创建的系统线程归入的进程的句柄。该参数可选,填入NULL表示系统进程(PID=4)。
ClientId:                用于接收被创建线程的PID和TID。驱动创建线程时,应当设置为NULL。
StartRoutine:                线程起始函数地址。
StartContext:                线程函数的参数。
返回值为NTSTATUS错误码。当线程被成功创建时,函数返回STATUS_SUCCESS。
值得注意的是,ObjectAttributes是一个结构体,当指定OBJ_KERNEL_HANDLE属性时,方法如下:
[C] 纯文本查看 复制代码
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa,NULL,OBJ_KERNEL_HANDLE,NULL,NULL);

线程被创建后,会执行其线程函数。线程函数的原型为:
KSTART_ROUTINE ThreadStart;
void ThreadStart(IN PVOID StartContext)
{...}
原型和POSIX线程一样,只不过没有返回值罢了。


线程完成操作时,应当主动退出线程。在POSIX线程中,通过pthread_exit函数退出线程,在Windows内核系统线程中,通过PsTerminateSystemThread函数退出线程。函数原型如下:
[C] 纯文本查看 复制代码
NTSTATUS PsTerminateSystemThread(IN NTSTATUS Status);

通过填入一个NTSTATUS的代码表示线程任务的状态。通常填入STATUS_SUCCESS表示执行成功。


多线程编程中,需要等待一个线程执行完成。在POSIX线程中,通过pthread_join函数来等待线程完成。在Windows内核中,由于我们获取了线程句柄,因此使用ZwWaitForSingleObject来等待线程完成。函数原型如下:
[C] 纯文本查看 复制代码
NTSTATUS ZwWaitForSingleObject(IN HANDLE Handle,IN BOOLEAN Alertable,IN PLARGE_INTEGER Timeout OPTIONAL);

参数介绍如下:
Handle:        要等待的对象的句柄,这里直接填入PsCreateSystemThread返回的句柄即可。
Alertable:        该等待是否可以被叫醒。用于表示其他线程是否可以在该线程陷入等待时叫醒线程退出等待。
Timeout:        等待时长。值得注意的是,单位是100纳秒,正数表示绝对系统时间(即系统中的格林尼治时间),负数表示相对于执行该函数时的时间。举例:等待5毫秒时,设置为-50000。当设置为NULL时表示无限等待。
返回值为NTSTATUS错误码,可能情况如下:
STATUS_SUCCESS:        等待成功完成,在等待过程中没有被叫醒。
STATUS_ALERTED:        等待过程中被叫醒。
STATUS_INVALID_HANDLE:        句柄不正确。
STATUS_ACCESS_DENIED:        拒绝访问。一般是PsCreateSystemThread中没有赋予SYNCHRONIZE权限。
STATUS_TIMEOUT:        等待超时。
STATUS_USER_APC:        用户态APC被插入到当前线程。
值得注意的是,当使用NT_SUCCESS宏时,STATUS_SUCCESS,STATUS_ALERTED,STATUS_TIMEOUT,STATUS_USER_APC都被视为执行成功。
等待完成后,线程终止,此时应当使用ZwClose函数关闭句柄以避免句柄泄露。


“睡觉”是一种多线程实现定时器的功能,也可以用于实现简单的自旋锁多线程同步。在用户态里可以用Sleep函数来实现睡觉。在Windows内核中,通过KeDelayExecutionThread实现睡觉。函数原型如下:
[C] 纯文本查看 复制代码
NTSTATUS KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,IN BOOLEAN Alertable,IN PLARGE_INTEGER Interval);

参数介绍如下:
WaitMode:        用于表示等待的模式。在系统线程中,应当用KernelMode。
Alertable:        该睡觉是否可以被叫醒。
Interval:        睡觉时长,单位是100纳秒。正数表示绝对系统时间(即系统中的格林尼治时间),负数表示相对于执行该函数时的时间。
返回值如下:
STATUS_SUCCESS:        睡觉完成,没有被叫醒。
STATUS_ALERTED:        等待过程中被叫醒。
STATUS_USER_APC:        用户态APC被插入到当前线程。
之前提到过睡觉和等待可以被叫醒,叫醒线程使用ZwAlertThread函数实现,函数原型如下:
NTSTATUS ZwAlertThread(IN HANDLE ThreadHandle);
填入线程句柄来叫醒指定的线程。注意这是个非文档化函数,使用这个函数时应当自行声明。
如果线程等待和睡觉时指定不可被叫醒,则这个函数不能叫醒线程。


在多线程编程中,线程安全尤为重要。我们需要实现多线程之间的同步。在Windows内核中,有以下简单的线程同步机制:
互斥锁、推拉锁、资源锁、自旋锁。
互斥锁(Mutual Exclusion,Mutex)是一种基本的线程同步机制。任何受互斥锁保护的数据都会在获取时陷入阻塞。
在Windows内核中,可以选择内核互斥锁(Mutex),快速互斥锁(Fast Mutex)和受保护互斥锁(Guarded Mutex)。
他们的区别是:
内核互斥锁性能最差,但线程递归性获取普通互斥锁时不会发生死锁。
快速互斥锁比内核互斥锁快,但线程递归性获取快速互斥锁时会发生死锁。
受保护互斥锁比快速互斥锁拥有更好的性能,但线程递归性获取受保护互斥锁时会发生死锁。
推拉锁(Push Lock)是一种支持读写分离的线程同步机制。推拉锁通过访问数据行为,将获取锁的行为分为共享锁和独占锁。当读取数据时,应当获取共享锁。当写入数据时,应当获取独占锁。
资源锁(Resource)也是一种支持读写分离的线程同步机制。和推拉锁一样,获取锁的行为分为共享锁和独占锁。当读取数据时,应当获取共享锁。当写入数据时,应当获取独占锁。
推拉锁和资源锁的区别在于:线程递归性获取推拉锁会发生死锁,资源锁则不会。而且,推拉锁未被微软官方文档化,是一种Windows内核内部使用的锁。
自旋锁(Spin Lock)是一种高度占用CPU核心的锁,但它是唯一允许在高中断请求级下使用的线程同步机制,因此受自旋锁保护的代码应当GKD。
这些线程同步机制都是基于获取-释放的代码模型。


关于互斥锁,这里对内核互斥锁略过不谈,直接谈快速互斥锁和受保护互斥锁。
快速互斥锁变量类型为FAST_MUTEX,受保护互斥锁变量类型为KGUARDED_MUTEX。在初始化一个互斥锁时,这个互斥锁变量不能是一个驱动中的全局变量,它必须以未分页内存的方式保存变量。通过ExAllocatePool函数,填入NonPagedPool参数分配一段未分页内存。为互斥锁变量分配出内存后,才能以ExInitializeFastMutex或KeInitializeGuardedMutex初始化互斥锁。
初始化互斥锁的函数原型如下:
[C] 纯文本查看 复制代码
void ExInitializeFastMutex(IN PFAST_MUTEX FastMutex);
void KeInitializeGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex);

当获取互斥锁时,使用ExAcquireFastMutex或KeAcquireGuardedMutex函数。如果互斥锁已被获取,则获取互斥锁将陷入阻塞直到互斥锁被释放。
当释放互斥锁时,使用ExReleaseFastMutex或KeReleaseGuardedMutex函数。互斥锁释放时,其他获取锁的线程将停止阻塞。
如果不希望获取资源锁时发生阻塞,则使用ExTryToAcquireFastMutex或KeTryToAcquireGuardedMutex。获取成功时返回TRUE,反之则FALSE。
以上六个函数原型如下:
[C] 纯文本查看 复制代码
void ExAcquireFastMutex(IN PFAST_MUTEX FastMutex);
BOOLEAN ExTryToAcquireFastMutex(IN PFAST_MUTEX FastMutex);
void ExReleaseFastMutex(IN PFAST_MUTEX FastMutex);
void KeAcquireGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex);
BOOLEAN KeTryToAcquireGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex);
void KeReleaseGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex);



推拉锁支持读写分离,但未被文档化,这里对推拉锁做简单的文档化。
推拉锁的变量类型为EX_PUSH_LOCK,在初始化一个互斥锁时,也必须是未分页内存的方式。通过ExInitializePushLock函数对其初始化,函数原型如下:
[C] 纯文本查看 复制代码
void ExInitializePushLock(OUT PEX_PUSH_LOCK PushLock);

获取推拉锁之前,应当进入临界区,使用KeEnterCriticalRegion函数进入临界区,函数原型如下:
[C] 纯文本查看 复制代码
void KeEnterCriticalRegion(void);

获取锁有共享锁独占锁两种形式,用ExfAcquirePushLockShared获取共享锁,用ExfAcquirePushLockExclusive获取独占锁。函数原型如下:
[C] 纯文本查看 复制代码
void __fastcall ExfAcquirePushLockShared(IN PEX_PUSH_LOCK PushLock);
void __fastcall ExfAcquirePushLockExclusive(IN PEX_PUSH_LOCK PushLock);

注意这里使用的是fastcall调用约定!
当获取共享锁时,如果推拉锁未被获取独占锁,则锁被立即获取,否则发生阻塞。也就是说多个线程可以同时以共享锁的方式获取一个推拉锁。
当获取独占锁时,如果推拉锁未被获取任何形式的锁,则锁被立即获取,否则发生阻塞。
在释放锁时,要对应获取方式实行释放锁,用ExfReleasePushLockShared释放共享锁,用ExfReleasePushLockExclusive释放独占锁。函数原型如下:
[C] 纯文本查看 复制代码
void __fastcall ExfReleasePushLockShared(IN PEX_PUSH_LOCK PushLock);
void __fastcall ExfReleasePushLockExclusive(IN PEX_PUSH_LOCK PushLock);

释放推拉锁后,应当离开临界区,使用KeLeaveCriticalRegion函数离开临界区,函数原型如下:
[C] 纯文本查看 复制代码
void KeLeaveCriticalRegion(void);

再次重申,递归获取推拉锁会引起线程死锁!


资源锁是一种同时支持读写分离且允许递归获取的文档化的线程锁。
资源锁的变量类型为ERESOURCE,在初始化一个资源锁时,也必须是未分页内存的方式。通过ExInitializeResourceLite函数对其初始化,函数原型如下:
[C] 纯文本查看 复制代码
NTSTATUS ExInitializeResourceLite(IN PERESOURCE Resource);

获取资源锁有共享锁和独占锁两种形式,其中共享锁还能细分为插队锁和排队锁两种形式。
获取独占锁时,用ExAcquireResourceExclusiveLite函数获取独占锁,函数原型如下:
[C] 纯文本查看 复制代码
BOOLEAN ExAcquireResourceExclusiveLite(IN PERESOURCE Resource,IN BOOLEAN Wait);

当Wait参数为TRUE时,函数等到所有共享锁和独占锁被释放后进行获取,并返回TRUE。
当Wait参数为FALSE时,如果不能立即获取独占锁,返回FALSE,反之则返回TRUE。
获取插队锁时,用ExAcquireSharedStarveExclusive函数获取插队锁,函数原型如下:
[C] 纯文本查看 复制代码
BOOLEAN ExAcquireSharedStarveExclusive(IN PERESOURCE Resource,IN BOOLEAN Wait);

这里对Wait参数和返回值不再赘述,简单讲讲插队锁的特征:
如果资源没有被获取独占锁,插队锁会在队列中的独占锁之前抢占获取共享锁。
获取排队锁时,用ExAcquireResourceSharedLite函数获取排队锁,函数原型如下:
[C] 纯文本查看 复制代码
BOOLEAN ExAcquireResourceSharedLite(IN PERESOURCE Resource,IN BOOLEAN Wait);

相对于插队锁,排队锁会等待资源锁队列中的独占锁——即使资源尚未被获取独占锁,排队锁也会等待队列中排在前的独占锁完成操作。这里假设Wait参数为TRUE。
当Wait参数为FALSE时,如果不能立即获取排队锁,返回FALSE。
操作完成时,应当释放锁,无论是何种锁,释放锁时统一使用ExReleaseResourceLite函数,函数原型如下:
[C] 纯文本查看 复制代码
void ExReleaseResourceLite(IN PERESOURCE Resource);

和推拉锁一样,在获取资源锁前,应当进入临界区,在释放资源锁后,应当离开临界区。
当资源锁不再使用时,应当使用ExDeleteResourceLite删除资源锁。函数原型如下:
[C] 纯文本查看 复制代码
NTSTATUS ExDeleteResourceLite(IN PERESOURCE Resource);

当资源锁被删除后,才能释放掉资源锁的内存。


自旋锁是一种可以在高中断请求级下使用的线程同步(多核同步)机制。它不支持读写分离,且大量占用CPU资源。
自旋锁的变量类型是KSPIN_LOCK,在初始化一个自旋锁时,也必须是未分页内存的方式。通过KeInitializeSpinLock函数对其初始化,函数原型如下:
[C] 纯文本查看 复制代码
void KeInitializeSpinLock(IN PKSPIN_LOCK SpinLock);

关于自旋锁的获取,在WDK7中就有五个函数实现获取,它们分别是:
标准获取自旋锁:        void KeAcquireSpinLock(IN PKSPIN_LOCK SpinLock,OUT PKIRQL OldIrql);
在DPC级获取自旋锁:        void KeAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock);
在DPC级试图获取自旋锁:        BOOLEAN KeTryToAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock);
线程DPC获取自旋锁:        KIRQL KeAcquireSpinLockForDpc(IN PKSPIN_LOCK SpinLock);
提升至DPC级获取自旋锁:        KIRQL KeAcquireSpinLockRaiseToDpc(IN PKSPIN_LOCK SpinLock);
有三个函数实现释放,它们分别是:
标准释放自旋锁:        void KeReleaseSpinLock(IN PKSPIN_LOCK SpinLock,IN KIRQL NewIrql);
从DPC级释放自旋锁:        void KeReleaseSpinLockFromDpcLevel(IN PKSPIN_LOCK SpinLock);
线程DPC释放自旋锁:        void KeReleaseSpinLockForDpc(IN PKSPIN_LOCK SpinLock,IN KIRQL OldIrql);
其中KeAcquireSpinLock和KeAcquireSpinLockRaiseToDpc获取的自旋锁应当用KeReleaseSpinLock释放;
KeAcquireSpinLockAtDpcLevel和KeTryToAcquireSpinLockAtDpcLevel获取的自旋锁应当用KeReleaseSpinLockFromDpcLevel释放;
KeAcquireSpinLockForDpc获取的自旋锁应当用KeReleaseSpinLockForDpc释放。
注意KeAcquireSpinLock返回的OldIrql通常放进局部变量,虽然可以放进全局变量,但绝对不能用于两个不同的锁,否则会引起竞态条件。
在自旋锁中,不要访问分页内存,不要产生异常,并且GKD,获取后尽快释放。对在获取自旋锁时运行的代码应当做到最优优化。


本文介绍了在Windows内核中玩多线程的方法,涉及了线程的创建退出等待,睡觉与叫醒,以及四种可用于线程同步的锁:互斥锁,推拉锁,资源锁,自旋锁。
因为时间关系就不写示例代码了。以后可能会补充。

评分

参与人数 2威望 +30 宅币 +130 贡献 +60 收起 理由
watermelon + 10 + 30 + 10 支持!
美俪女神 + 20 + 100 + 50 赞!

查看全部评分

flowers for Broken spirits - a woman turned into stake will hold the world in the basin of fire.
回复

使用道具 举报

1

主题

57

帖子

203

积分

用户组: 中·技术宅

UID
4683
精华
0
威望
0 点
宅币
146 个
贡献
0 次
宅之契约
0 份
在线时间
25 小时
注册时间
2019-2-11
发表于 2019-6-22 22:43:59 | 显示全部楼层
知识有限,还没能看懂,不过我会努力的。

点评

加油。  发表于 2020-1-27 18:58

36

主题

161

帖子

7373

积分

用户组: 管理员

UID
77
精华
11
威望
125 点
宅币
6765 个
贡献
142 次
宅之契约
0 份
在线时间
121 小时
注册时间
2014-2-22
发表于 2019-6-23 02:56:13 | 显示全部楼层
这个总结太牛逼了。

24

主题

261

帖子

1491

积分

用户组: 上·技术宅

UID
3808
精华
6
威望
53 点
宅币
1004 个
贡献
90 次
宅之契约
0 份
在线时间
261 小时
注册时间
2018-5-6
发表于 2020-1-27 17:07:03 | 显示全部楼层
牛,我看懂了OBJECT_ATTRIBUTES结构体和InitializeObjectAttributes宏,之前python学过多线程里面的mutex lock所以还稍微看懂一些互斥锁,最让我感觉兴奋的是在内核中的实现Sleep函数来着,由于Sleep是winsdk里面用在r3层面的api, 苦恼了好长时间才有人说内核中用KeDelayExecutionThread来实现Sleep功能,要是当初早些时间看到这个帖子就好了
菜鸟一枚,直接指正,不必留情

24

主题

261

帖子

1491

积分

用户组: 上·技术宅

UID
3808
精华
6
威望
53 点
宅币
1004 个
贡献
90 次
宅之契约
0 份
在线时间
261 小时
注册时间
2018-5-6
发表于 2020-1-27 18:55:09 | 显示全部楼层
小弟我简单根据本帖子上的api指导和win32sdk编程中的多线程程序写了一个win7 x64的互斥锁的多线程内核程序的例子,为了便于理解,小弟写了一个python的程序作为对应。
例子采用互斥锁的应用中的给共享资源(这里以一个全局变量为例)进行计数来说明互斥锁的应用,其具体原理就是本帖子中说明的堵塞和解堵塞的原理。
python程序(python 3.7 amd64)
[Python] 纯文本查看 复制代码
import threading
import time

# global parameter.
g_num = 0


def thread1(num):
    global g_num

    mutex.acquire()
    
    for i in range(num):
        g_num += 1

    mutex.release()

    print("thread 1: g_num=%d" % g_num)
    


def thread2(num):
    global g_num

    mutex.acquire()
    
    for i in range(num):
        g_num += 1

    mutex.release()

    print("thread 2: g_num=%d" % g_num)


# Create a mutex lock, and the defaulted is unlocked.
mutex = threading.Lock()


def main():
    th1 = threading.Thread(target=thread1, args=(10000000,))
    th2 = threading.Thread(target=thread2, args=(10000000,))

    th1.start()
    
    th2.start()

    time.sleep(3)
    
if __name__ == '__main__':
    main()
    

运行结果:
2020-01-27_185435.png

C语言 win7 x64 内核程序:
[C] 纯文本查看 复制代码
#include <wdm.h>
#include <windef.h>

// Global parameter.
unsigned long g_number = 0;

void thread1(void *mutex)
{
	unsigned long i;
	PKMUTEX pkMutex = (PKMUTEX)mutex;

	KeWaitForSingleObject(pkMutex,
		Executive,
		KernelMode,
		FALSE,
		NULL);
	DbgPrint("This is Thread 1...\n");

	for (i = 0; i < 10000000; i++)
	{
		g_number++;
	}

	KeReleaseMutex(pkMutex, FALSE);

	// Print the result of g_number in thread 1.
	DbgPrint("Thread 1:g_number = %ld\n", g_number);

	PsTerminateSystemThread(STATUS_SUCCESS);
}

void thread2(void *mutex)
{
	unsigned long i;
	PKMUTEX pkMutex = (PKMUTEX)mutex;

	KeWaitForSingleObject(pkMutex,
		Executive,
		KernelMode,
		FALSE,
		NULL);
	DbgPrint("This is Thread 2...\n");

	for (i = 0; i < 10000000; i++)
	{
		g_number++;
	}

	KeReleaseMutex(pkMutex, FALSE);

	// Print the result of g_number in thread 2.
	DbgPrint("Thread 2:g_number = %ld\n", g_number);

	PsTerminateSystemThread(STATUS_SUCCESS);
}


void DriverUnload(IN PDRIVER_OBJECT pDrvObj)
{
	DbgPrint("DriverUnload....\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING RegistryPath)
{
	NTSTATUS status = STATUS_SUCCESS;
	pDrvObj->DriverUnload = DriverUnload;

	// TODO: mutex-lock thread test.

	HANDLE hThread1, hThread2;
	KMUTEX mutex;

	// Create the mutex lock.
	KeInitializeMutex(&mutex, 0);

	// Create the thread.
	PsCreateSystemThread(&hThread1,
		0,
		NULL,
		NtCurrentProcess(),
		NULL,
		thread1, // thread proc
		&mutex);

	PsCreateSystemThread(&hThread2,
		0,
		NULL,
		NtCurrentProcess(),
		NULL,
		thread2, // thread2 proc
		&mutex);

	void *Queue[2];
	ObReferenceObjectByHandle(hThread1,
		0,
		NULL,
		KernelMode,
		&Queue[0],
		NULL);
	ObReferenceObjectByHandle(hThread2,
		0,
		NULL,
		KernelMode,
		&Queue[1],
		NULL);

	
	KeWaitForMultipleObjects(2,
		Queue,
		WaitAll,
		Executive,
		KernelMode,
		FALSE,
		NULL,
		NULL);

	// Subtract the count the object reference and
	// release the system resources.
	ObDereferenceObject(Queue[0]);
	ObDereferenceObject(Queue[1]);

	// Sleep 2 seconds, wait for the threads completely
	// finish their work.
	LARGE_INTEGER sleep;
	sleep.QuadPart = -20 * 1000 * 1000;
	KeDelayExecutionThread(KernelMode,
		FALSE,
		&sleep);

	DbgPrint("The process is terminated...\n");
	return status;
}

运行结果:
result.PNG
菜鸟一枚,直接指正,不必留情

38

主题

108

帖子

4789

积分

用户组: 管理员

UID
1043
精华
19
威望
223 点
宅币
3679 个
贡献
461 次
宅之契约
0 份
在线时间
743 小时
注册时间
2015-8-15
 楼主| 发表于 2020-1-27 21:55:51 | 显示全部楼层
watermelon 发表于 2020-1-27 18:55
小弟我简单根据本帖子上的api指导和win32sdk编程中的多线程程序写了一个win7 x64的互斥锁的多线程内核程序 ...

随便说几句吧。
1. 在Windows内核里锁对象必须放在不可换出内存里。也就是说必须要用ExAllocatePool(WithTag)分配一段NonPagedPool给锁对象。每个初始化锁函数的文档都会提到这句话。
2. 我在帖子里没说怎么用内核互斥锁。。。
3. 创建线程没必要引用他们的线程对象,直接ZwWaitForMultipleObjects即可,这是个导出函数。
4. 你虽然记得要解引用以免对象泄漏,但你忘记关闭线程句柄了。
5. 你发这个帖子的时候python3.8.x都出了。。。
flowers for Broken spirits - a woman turned into stake will hold the world in the basin of fire.

24

主题

261

帖子

1491

积分

用户组: 上·技术宅

UID
3808
精华
6
威望
53 点
宅币
1004 个
贡献
90 次
宅之契约
0 份
在线时间
261 小时
注册时间
2018-5-6
发表于 2020-1-27 23:41:02 | 显示全部楼层
tangptr@126.com 发表于 2020-1-27 21:55
随便说几句吧。
1. 在Windows内核里锁对象必须放在不可换出内存里。也就是说必须要用ExAllocatePool(With ...

学习了!不过好像的确是忘记关闭hThread1和hThread2了(汗
菜鸟一枚,直接指正,不必留情

24

主题

261

帖子

1491

积分

用户组: 上·技术宅

UID
3808
精华
6
威望
53 点
宅币
1004 个
贡献
90 次
宅之契约
0 份
在线时间
261 小时
注册时间
2018-5-6
发表于 2020-1-27 23:44:12 | 显示全部楼层
tangptr@126.com 发表于 2020-1-27 21:55
随便说几句吧。
1. 在Windows内核里锁对象必须放在不可换出内存里。也就是说必须要用ExAllocatePool(With ...

是的,最新的python正式发布版本是3.8,并且刚看了一下,python3.9已经出测试版了
菜鸟一枚,直接指正,不必留情

本版积分规则

QQ|申请友链||Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图  

GMT+8, 2020-7-8 07:06 , Processed in 0.110924 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表