博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
64内核开发第11讲.多线程开发.以及同步.
阅读量:4963 次
发布时间:2019-06-12

本文共 4854 字,大约阅读时间需要 16 分钟。

目录

多线程编程

一丶多线程安全.

1.什么是多线程

现在的程序基本是很多个线程.不想以前一样.而进程和线程的关系就是

一对多的关系.

进程做外键放到线程中. 数据关系是这样的.

简单理解为 进程就是一个 容体. 里面的线程就是它进存储的任务.

一个线程只能做一个事情. 多个线程可以做多个事情.

2.超线程

超线程是一个硬件的CPU技术.是用来模拟双核的.inter所研发的.

以前是用软件模拟的. 英文是HT技术.全名就是 Hyper-Threading.
超线程的意思就是在同一个时刻.应用层可以使用CPU的不同部分的.

3.多线程引入的问题.

同步问题.

当线程访问全局变量.以及资源的时候就会出现问题.

比如我们要对一个变量++ .正常来说会 1 + 1 = 2就是等于2.

而多线程就会出现数字不一样的情况.

如下: ring3代码演示.

#include 
#include
#include
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;}

结果:

1197364-20190608121111907-2098892702.png
数值每次都是随机的.
所以产生了错误.
解决方法就是使用同步函数.

如下:

#include 
#include
#include
__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;}

使用同步之后就不会出现这类问题了.

1197364-20190608122110393-338480745.png

如果你的线程使用的是局部变量. 你的程序是多线程安全的.就是各个线程

都有自己的局部变量. 不会影响.
但是如果你使用全局资源.就是多线程不安全的.必须使用同步函数(加锁)
这样才会保证你的程序是安全的.

4.多线程的同步与互斥

多线程同步:

同步就是两个线程协同做一件事情.

多线程互斥

多个线程是排他性的.排着队访问一个资源.
比如我们上面的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 
#include
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;}

2.同步与互斥,以及等待函数.

互斥

在内核中有三种互斥锁. 互斥就是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**

转载于:https://www.cnblogs.com/iBinary/p/11026655.html

你可能感兴趣的文章
PHPCMS V9{loop subcat(0,0,0,$siteid) $r}怎么解释?
查看>>
避免内存重叠memmove()性能
查看>>
【ASP.NET】从服务器端注册客户端脚本
查看>>
Infix to Postfix Expression
查看>>
SELECT LOCK IN SHARE MODE and FOR UPDATE
查看>>
Perl/Nagios – Can’t locate utils.pm in @INC
查看>>
目录导航「深入浅出ASP.NET Core系列」
查看>>
简易爬虫(爬取本地数据)
查看>>
python 进程间通信
查看>>
深拷贝 vs 浅拷贝 释放多次
查看>>
Javascript 有用参考函数
查看>>
点群的判别(三)
查看>>
GNSS 使用DFT算法 能量损耗仿真
查看>>
【转】Simulink模型架构指导
查看>>
MYSQL数据库的导出的几种方法
查看>>
SQL Server-5种常见的约束
查看>>
硬件之美
查看>>
[转载]java开发中的23种设计模式
查看>>
表格的拖拽功能
查看>>
函数的形参和实参
查看>>