如今的程序基本是不少个线程.不想之前同样.而进程和线程的关系就是
一对多的关系.windows
进程作外键放到线程中. 数据关系是这样的.安全
简单理解为 进程就是一个 容体. 里面的线程就是它进存储的任务.多线程
一个线程只能作一个事情. 多个线程能够作多个事情.函数
超线程是一个硬件的CPU技术.是用来模拟双核的.inter所研发的.
之前是用软件模拟的. 英文是HT技术.全名就是 Hyper-Threading.
超线程的意思就是在同一个时刻.应用层可使用CPU的不一样部分的.spa
同步问题.线程
当线程访问全局变量.以及资源的时候就会出现问题.code
好比咱们要对一个变量++ .正常来讲会 1 + 1 = 2就是等于2.
而多线程就会出现数字不同的状况.对象
以下: ring3代码演示.blog
#include <windows.h> #include <stdlib.h> #include <stdio.h> int g_Value = 0; DWORD MyThread(LPVOID lPparame) { for (DWORD i = 0; i < 10000000; i++) { g_Value++ } return 0; } int main() { HANDLE hThread[2]; hThread[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, NULL); hThread[1]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, NULL); WaitForMultipleObjects(2, hThread, TRUE, INFINITE); printf("%d", g_Value); system("pause"); return 0; }
结果:
数值每次都是随机的.
因此产生了错误.
解决方法就是使用同步函数.
以下:
#include <windows.h> #include <stdlib.h> #include <stdio.h> __int64 g_Value = 0; int g_Count = 0; HANDLE g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); DWORD MyThread(LPVOID lPparame) { WaitForSingleObject(g_hEvent, INFINITE); for (DWORD i = 0; i < 10000000; i++) { g_Value++; } // SetEvent(g_hEvent); return 0; } int main() { HANDLE hThread[2]; hThread[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, NULL); hThread[1]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, NULL); WaitForMultipleObjects(2, hThread, TRUE, INFINITE); printf("%d", g_Value); system("pause"); return 0; }
使用同步以后就不会出现这类问题了.
若是你的线程使用的是局部变量. 你的程序是多线程安全的.就是各个线程
都有本身的局部变量. 不会影响.
可是若是你使用全局资源.就是多线程不安全的.必须使用同步函数(加锁)
这样才会保证你的程序是安全的.
多线程同步:
同步就是两个线程协同作一件事情.
多线程互斥
多个线程是排他性的.排着队访问一个资源.
好比咱们上面的Ring3的例子就是互斥的. 每一个线程必须互斥的访问.
当进入线程的时候.你没有锁的时候就要等那个线程对全局资源访问完毕你的线程才能执行你的代码.
同步就是我操做完变量.我发一个信号.告诉另外一个线程能够进行操做那个变量了.
如如下代码:
DWORD MyThread(LPVOID lPparame) { for (DWORD i = 0; i < 200; i++) { WaitForSingleObject(g_hEvent, INFINITE); g_Value++; SetEvent(g_hEvent); //告诉另外一个线程能够操做了. } // return 0; }
这样两个线程均可以同时操做这个变量. 你操做完毕我告诉另外一个线程你能操做了.
内核中建立线程很简单.
PsCreateSystemThread进行建立的.
跟ring3的CreateThread相似.
以下演示:
PsCreateSystemThread(&hThread, 0, NULL, (HANDLE)0, NULL, MyProc, MyProcParam);
具体查询MSDN
咱们建立线程要注意IRQL级别.
若是做为一个普通线程.运行在的是 PASSIVE级别.
若是当前线程IRQL抬高.能够降到PASSIVE级别.
以下:
if (KeGetCurrentIrql() != PASSIVE_LEVEL) { KfRaiseIrql(PASSIVE_LEVEL); }
KfRaiseIrql.表明下降级别.
KeLowerIrql(); 则是恢复IRQL
主线程等待子线程建立完毕.
在Ring3咱们能够经过WaitForsingObject来等待.
在内核中可使用 KeWaitForSingleObject()来等待.
可是注意,keWaitForSingleObject只是等待一个Object对象.
而不像跟Ring3同样.直接把线程句柄拿过来等待.
因此咱们还须要一组函数.
ObReferenceObjectByHandle(); //根据HANDLE.传出Object对象 ObDereference();//取消对象引用.
完整代码以下:
#include <ntddk.h> #include <ntstrsafe.h> DRIVER_UNLOAD DriverUnLoad; KSTART_ROUTINE MyThredProc; void DriverUnLoad(PDRIVER_OBJECT pDriverObject) { KdPrint(("驱动卸载")); } void MyThredProc( PVOID StartContext ) { DWORD dwCount = 0; while ((dwCount++) <= 10) { KdPrint(("内核线程输出中.第%d次", dwCount)); } } NTSTATUS InitRun() { //建立线程 HANDLE hThread; PVOID ppObject = NULL; NTSTATUS ntSttus; ntSttus = PsCreateSystemThread(&hThread, 0, NULL, (HANDLE)0, NULL, MyThredProc, //你建立函数的回调地址 NULL); //给你建立函数传递的参数值 //等待线程建立完毕. if (!NT_SUCCESS(ntSttus)) return ntSttus; //判断IRQL 下降权限 if (KeGetCurrentIrql() != PASSIVE_LEVEL) ntSttus = KfRaiseIrql(PASSIVE_LEVEL); ntSttus = ObReferenceObjectByHandle(&hThread, THREAD_ALL_ACCESS, NULL, KernelMode, &ppObject, NULL); if (!NT_SUCCESS(ntSttus)) return ntSttus; //等待对象 KeWaitForSingleObject(ppObject, Executive, KernelMode, FALSE, NULL); //由于使用了 ObRefrenceObjceByHandle.因此引用对象+1了.如今须要引用对象-1 ObDereferenceObject(ppObject); //关闭线程句柄 ZwClose(hThread); return ntSttus; } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegPath) { NTSTATUS ntStatus = 0; pDriverObject->DriverUnload = DriverUnLoad; ntStatus = InitRun(); return ntStatus; }
互斥
在内核中有三种互斥锁. 互斥就是AB只能有一我的访问相同的资源.
自旋锁 KSPIN_LOCK
资源执行体锁 ERESOURCE
快速互斥 FAST_MUTEX ,也有MUTEX. 效率过低.微软放弃了.
同步:
A 跟 B 协做执行. A作一件事告诉B. B去作另外一个.
KEVENT 事件
KSEMAPHORE 信号量
KMUTEX
上面是可等待对象.均可以使用函数来等待.
KeWaitForSingleObject
等待的对象还包括
KPROCESS KQUEUE KMUTANT KSEMAPHORE KTHREAD KTIMER ...
FileObject DriverObject 是不能够转换的. 凡是能等待的内核对象.内核头部都会带有 Dispatcher Header结构的
以下:
typedef struct _KEVENT { DISPATCHER_HEADER Header; } KEVENT, *PKEVENT, *PRKEVENT;
** KeWaitForSingleObject ** 最后一个参数是等待时间. 可是注意一点.他是 负数. 而不是跟ring3同样给个秒数就行. 0不等待. NULL 无线等待.
等待多个对象则使用 ** KeWaitForMutiipleObject**