64內核開發第11講.多線程開發.以及同步.

多線程編程

一丶多線程安全.

1.什麼是多線程

如今的程序基本是不少個線程.不想之前同樣.而進程和線程的關係就是
一對多的關係.windows

進程作外鍵放到線程中. 數據關係是這樣的.安全

簡單理解爲 進程就是一個 容體. 裏面的線程就是它進存儲的任務.多線程

一個線程只能作一個事情. 多個線程能夠作多個事情.函數

2.超線程

超線程是一個硬件的CPU技術.是用來模擬雙核的.inter所研發的.
之前是用軟件模擬的. 英文是HT技術.全名就是 Hyper-Threading.
超線程的意思就是在同一個時刻.應用層可使用CPU的不一樣部分的.spa

3.多線程引入的問題.

同步問題.線程

當線程訪問全局變量.以及資源的時候就會出現問題.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;
}

使用同步以後就不會出現這類問題了.

若是你的線程使用的是局部變量. 你的程序是多線程安全的.就是各個線程
都有本身的局部變量. 不會影響.
可是若是你使用全局資源.就是多線程不安全的.必須使用同步函數(加鎖)
這樣纔會保證你的程序是安全的.

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

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**

相關文章
相關標籤/搜索