基于WRK平台的IPC实验.docx

上传人:b****3 文档编号:5592435 上传时间:2023-05-08 格式:DOCX 页数:17 大小:535.46KB
下载 相关 举报
基于WRK平台的IPC实验.docx_第1页
第1页 / 共17页
基于WRK平台的IPC实验.docx_第2页
第2页 / 共17页
基于WRK平台的IPC实验.docx_第3页
第3页 / 共17页
基于WRK平台的IPC实验.docx_第4页
第4页 / 共17页
基于WRK平台的IPC实验.docx_第5页
第5页 / 共17页
基于WRK平台的IPC实验.docx_第6页
第6页 / 共17页
基于WRK平台的IPC实验.docx_第7页
第7页 / 共17页
基于WRK平台的IPC实验.docx_第8页
第8页 / 共17页
基于WRK平台的IPC实验.docx_第9页
第9页 / 共17页
基于WRK平台的IPC实验.docx_第10页
第10页 / 共17页
基于WRK平台的IPC实验.docx_第11页
第11页 / 共17页
基于WRK平台的IPC实验.docx_第12页
第12页 / 共17页
基于WRK平台的IPC实验.docx_第13页
第13页 / 共17页
基于WRK平台的IPC实验.docx_第14页
第14页 / 共17页
基于WRK平台的IPC实验.docx_第15页
第15页 / 共17页
基于WRK平台的IPC实验.docx_第16页
第16页 / 共17页
基于WRK平台的IPC实验.docx_第17页
第17页 / 共17页
亲,该文档总共17页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

基于WRK平台的IPC实验.docx

《基于WRK平台的IPC实验.docx》由会员分享,可在线阅读,更多相关《基于WRK平台的IPC实验.docx(17页珍藏版)》请在冰点文库上搜索。

基于WRK平台的IPC实验.docx

基于WRK平台的IPC实验

基于WRK平台的IPC实验

实验背景:

Inter-ProcessCommunication(进程间通信)在现在通用的时分操作系统中的进程管理中扮演着重要的角色,可以说没有同步/互斥机制,就不会实现系统的多线程。

在Windows中,内核提供了多种机制防止多个线程对同一个数据结构进行修改。

通过对WRK平台的IPC实验,我们可以更加深入地了解到Windows内部是如何实现线程的同步/互斥的。

第一部分:

阅读代码——了解WRK同步对象管理

1、阅读WRK代码,理解WRK中同步/互斥的对象管理

1.1WRK中的同步对象

Windows提供了一组内核同步对象(KernelDispatcherObject),或者就称为同步对象(DispatcherObject)。

在任何时刻,同步对象都处于两种状态中的一种:

信号态(signaledstate)或者非信号态(nonsignaledstate)。

这些对象包括定时器对象事件对象、信号量对象、临界区对象等。

表1WRK中同步对象数据结构

同步对象

定义位置

数据结构

Event

WRK\base\ntos\inc\Ntosdef.h

(注:

WRK是存放WRK源代码的文件夹,下同)

typedefstruct_KEVENT{

DISPATCHER_HEADERHeader;

}KEVENT,*PKEVENT,*PRKEVENT;

Mutex

WRK\base\ntos\inc\Ke.h

typedefstruct_KMUTANT{

DISPATCHER_HEADERHeader;

LIST_ENTRYMutantListEntry;

struct_KTHREAD*OwnerThread;

BOOLEANAbandoned;

UCHARApcDisable;

}KMUTANT,*PKMUTANT,

*PRKMUTANT,KMUTEX,

*PKMUTEX,*PRKMUTEX;

Semaphore

WRK\base\ntos\inc\Ke.h

typedefstruct_KSEMAPHORE{

DISPATCHER_HEADERHeader;

LONGLimit;

}KSEMAPHORE,

*PKSEMAPHORE,*PRKSEMAPHORE;

WaitableTimer

WRK\base\ntos\inc\Ntosdef.h

typedefstruct_KTIMER{

DISPATCHER_HEADERHeader;

ULARGE_INTEGERDueTime;

LIST_ENTRYTimerListEntry;

struct_KDPC*Dpc;

LONGPeriod;

}KTIMER,*PKTIMER,*PRKTIMER;

观察这些同步对象的数据结构,就会发现第一个成员都是DISPATHER_HEADER,这就是同步对象可以被等待的原因,也是同步对象的英文是DispatcherObject的原因。

查看下DISPATHER_HEADER的数据结构(WRK\base\ntos\inc\Ntosdef.h)。

typedefstruct_DISPATCHER_HEADER{

//对象类型

UCHARType;

UCHARAbsolute;

//对象体的大小

UCHARSize;

UCHARInserted;

//信号状态

LONGSignalState;

//等待该对象的线程列表

LIST_ENTRYWaitListHead;

}DISPATCHER_HEADER;

1.2线程中同步数据结构

在Windows中,调度的最小单位是线程,进行同步的也是线程,所以,在线程的数据结构中,必然包含有与同步有关的数据结构(WRK\base\ntos\inc\Ke.h)。

typedefstruct_KTHREAD{

DISPATCHER_HEADERHeader;

LONG_PTRWaitStatus;

//stateinformation,reflectingthereasonforendingwhenawaitingisended

union{

PKWAIT_BLOCKWaitBlockList;

//waitblockqueuepointedtothisthread

PKGATEGateObject;

};

BOOLEANAlertable;

BOOLEANWaitNext;

UCHARWaitReason;

KWAIT_BLOCKWaitBlock[THREAD_WAIT_OBJECTS+1];

//THREAD_WAIT_OBJECTS=3,thebuilt-insynchronizationobjectarray

//ifnowaitblockarrayisspecified,usethebuilt-insynchronizationobjectarray

};

其中,KTHREAD中的WaitBlockList是KWAIT_BLOCK对象的列表。

1.3联系线程和同步对象的数据结构

在WRK平台中,线程的同步/互斥是基于同步对象实现的。

那么必然有一个数据结构将二者联系起来,这个数据对象就是KWAIT_BLOCK(WRK\base\ntos\inc\Ke.h)。

typedefstruct_KWAIT_BLOCK{

LIST_ENTRYWaitListEntry;

struct_KTHREAD*Thread;

PVOIDObject;

struct_KWAIT_BLOCK*NextWaitBlock;

USHORTWaitKey;

UCHARWaitType;

UCHARSpareByte;

#ifdefined(_AMD64_)

LONGSpareLong;

#endif

}KWAIT_BLOCK,*PKWAIT_BLOCK,*PRKWAIT_BLOCK;

KWAIT_BLOCK中包含三个重要的指针:

一个是指向等待同步对象的线程指针Thread,一个是指向同步对象的指针(Object),另一个指向它自己NextWaitBlock。

通过这三个指针,线程、同步对象与KWAIT_BLOCK三者有机地联系起来。

下图显示了这三者之间的关系。

图1同步数据之间的关系

在这三个指针的帮助下,整个同步过程中始终保持两个队列。

一个是一个线程等待的同步对象队列,KTHREAD中的WaitBlockList指向这个队列,队列中的节点KWAIT_BLOCK通过自身的NextWaitBlock指针连接起来,通过这个队列,内核可以轻而易举地查看这个线程等待的同步对象。

另一个是等待同一个同步对象的线程队列,同步对象DISPATHER_HEADER中的WaitListEntry指向这个队列,KWAIT_BLOCK对象通过WaitListEntry指针加入到这个队列中。

很明显,同一个KWAIT_BLOCK对象会同时出现在两个队列中。

下图是实验一的情景图,我们很容易看出对象A的队列中只有一个等待对象,线程2,而对象B的队列中有两个等待对象,线程1和2。

线程1的队列里只有一个节点,线程1只需要等待对象B释放就可以进入就绪队列,而线程2的等待队列里面有两个节点,它必须在对象A和B都被释放了才能进入就绪队列。

图2实例

2、分析同步对象的释放和WRK的调度机制

2.1同步API函数

Windows提供的同步API采用一种分层的结构,包含面向用户的API,执行API,内核API。

上层的API通过调用下层对应的API完成其功能。

图3Windows的三层同步API结构

2.2同步对象的释放和WRK的线程调度

等待同步对象是线程的一个操作,线程可以等待一个同步对象或者多个同步对象,分别调用WaitForSingleObject和WaitForMultipleObjects。

2.2.1WaitForSingleObject等待一个同步对象

用户调用WaitForSingleObject函数会间接调用执行层的NtWaitForSingleObject。

在NtWaitForSingleObject中,最核心的操作是ObReferenceObjectByHandle和KeWaitForSingleObject。

(这些函数的代码放在WRK\base\ntos\ke\wait.c文件中,有兴趣的话可以阅读)

占用同步对象的线程调用ObReferenceObjectByHandle函数将用户传来的句柄转化成指针,传递给内核。

这个函数保证了指针的安全。

而KeWaitForSingleObject函数是实现同步功能的核心函数。

KeWaitForSingleObject函数通过调用InsertTailList(&Objectx->Header.WaitListHead,&WaitBlock->WaitListEntry),将等待块KWAIT_BLOCK插入该同步对象的等待线程队列中。

从而当占据的线程释放该线程时,就会遍历等待队列通知等待的线程。

最后,等待的线程调用WaitStatus=(NTSTATUS)KiSwapThread(Thread,CurrentPrcb)阻塞自己。

当然,这个函数调用会放在一个循环里面,因为进入阻塞循环的线程可能会被APC调用唤醒。

但是当APC函数执行完之后,线程的状态被改成STATUS_KERNEL_APC,然后线程进入下一个循环继续阻塞自己。

这个循环有三个出口。

首先是当同步对象变成“信号态”,这时已经满足同步条件,则正常跳出循环。

其次是超时,当用户设定了等待时间,则根据等待时间进行跳转。

最后一个是执行KiSwapThread之后的一个判断语句。

当阻塞被警醒后,判断是否是由于APC调用,如果不是则返回状态。

2.2.2WaitForMultipleObjects等待多个同步对象

WaitForMultipleObjects函数调用的过程如下:

WaitForMultiObjects->NtWaitForMultipleObjects->ObpWaitForMultipleObjects->KeWaitForMultipleObjects.

WaitForMultipleObjects函数与WaitForSingleObject类似,只是线程在等待多个同步对象变成“信号态”,因此WaitForMultipleObjects将等待的同步对象放入数组中,并且依次地将这些同步对象放入等待循环中。

线程等待每一个同步对象的过程类似于WaitForSingleObject,这里就不再赘述。

根据等待类型可以将线程分成两个分支。

分支1是WaitAny。

只要有一个等待的同步对象变成“信号态”,这个分支的线程就会结束阻塞,跳出等待的循环。

分支2是WaitAll,这个分支的线程会在等待的所有同步对象都变成“信号态”之后才会结束阻塞。

每次等待的同步对象变成“信号态”,WaitAll线程都会检查Index是否等于等待的同步对象综述,如果小于,线程会继续等待。

2.2.3同步对象的释放和线程调度

ReleaseXxx释放同步对象,ReleaseXxx间接调用NtReleaseXxx。

在NtReleaseXxx中,主要的操作为ObReferenceObjectByHandle和KeReleaseXxx。

这里类似于NtWaitForSingleObject,将用户传进来的句柄转换成内核中使用的指针,起到了保护指针的作用。

最核心的操作就是KeReleaseXxx了,在这里当内核将一个同步对象设置为信号状态时,就会调用KiWaitTest来进行检查,遍历同步对象的等待队列中正在等待的线程。

如果等待线程的等待类型为WaitAny,则直接满足该线程的等待要求,将该线程放入就绪队列中;若等待类型为WaitAll,只有等待的同步对象都被释放后才会进入就绪队列,否则不作任何处理。

最后,调用KiUnwaitThread将等待线程加入到就绪队列中等待调度。

 

第二部分:

联机调试——查看WRK等待队列

1、了解WinDbg的一些常用命令

1.1在winDbg中常用到的WinDbg命令:

Kd>!

process

Kd>dtnt!

_kthread

Kd>dtnt!

_kwait_block

Kd>dtnt!

_dispatcher_header

Kd>!

process00

Kd>!

process#thread

1.2!

process命令

主要用于查看进程状态(如图)。

1.3dt命令

显示类型,主要用dt来解释线程、等待块(如图)。

2、使用WinDbg联机查看一个线程等待的所有同步对象

(1)在系统初始化时,按下debug的break,进入断点。

并通过!

process查看当前所有线程的状态。

(2)选择其中一个线程查看,比如817a0020,可以看到它正在等待两个事件。

(3)用dtnt!

_kthread查看这个线程的全部信息。

可以看到等待的列表头为0x80899b28_KWAIT_BLOCK

(4)查看下一个等待块信息。

0x80899b28的具体信息如下:

3、使用WinDbg联机查看等待一个同步对象的所有线程

(1)用!

process命令,得到当前的线程信息,如图,看到8089eb3c被7个线程同时等待。

(2)用dt命令来解释该对象(8089eb3c)的分发器头:

可以看出有多个线程在等待这个对象。

(3)解释等待块0x817a70c8.

(4)解释等待块0x817a7e58。

(5)解释等待块0x817a7be8。

(6)解释等待块0x817a7978

(7)解释等待块0x817a7708。

发现NextWaitBlock就是自己,所以等待该对象的队列遍历完成。

(8)测试发现8x817a7798已经不是等待块了。

第三部分:

生产者-消费者问题在WRK平台上的实现与调试

1、充分理解“生产者-消费者”算法,编写“生产者-消费者”程序

1.1“生产者-消费者”问题描述

如上图所示,生产者把产品生产出来,送入仓库队列中。

给消费者发信号,消费者得到信号后,到仓库取产品,取走产品后给生产者发信号。

某时刻,只允许有一个生产者或消费者访问仓库队列,同时在仓库队列中进行操作将导致不可预知的错误。

1.2“生产者-消费者”算法。

1.2.1“生产者-消费者”问题分析

生产者-消费者问题是相互合作进程关系的一种抽象,其中,生产者作为系统中释放资源的进程,而消费者作为系统中使用同类资源的进程。

通过对该问题的分析,可知,消费者想接收数据时,有界缓冲区中至少有一个单元是满的,即对于“消费者”而言,缓冲区空则应等待。

而生产者想发送数据时,有界缓冲区中至少有一个单元是空的,即对于“生产者”而言,缓冲区满则应等待。

这便表现为一个同步问题。

而由于缓冲区是临界资源,所以生产者和消费者之间必须互斥的访问临界资源。

即任何时刻,只能有一个进程在临界区中操作。

这便表现为一个互斥问题。

为此,对于同步问题,引入同步信号量“empty”,为0表示缓冲区满及同步信号量“full”,为0表示缓冲区空,对于互斥问题,引入互斥信号量(二元信号量),信号量为0,表明已有进程进入临界区。

1.2.2“生产者-消费者”算法

设:

互斥信号量(Mutex)m_S_Queue表示缓冲区的个数初值为1。

(消费者)同步信号量(Semaphore)m_S_Consumer表示有界缓冲区中非空单元数初值为0。

(生产者)同步信号量(Semaphore)m_S_Producer表示有界缓冲区中空的单元数初值为n。

生产者:

Product(data):

begin

P(m_S_Producer)检查缓冲区中是否有空单元执行后n-1

P(m_S_Queue)检查缓冲区是否可用执行后mutex=0

送数据入缓冲区

V(m_S_Queue)释放缓冲区中的资源

V(m_S_Consumer)执行后,非空单元数加10+1=1

end

消费者:

Consume(data):

begin

P(m_S_Consumer)

P(m_S_Queue)

取缓冲区中某单元数据

V(m_S_Queue)

V(m_S_Producer)

end

3)在宿主机上编写“生产者-消费者”程序,并运行生成PC.exe,放入主机共享的工作目录c:

\wrk下。

2、“生产者-消费者”问题在WRK平台下调试

1)将生成的“生产者-消费者”可执行程序PC.exe放入虚拟机。

2)调试并查看PC.exe进程中所有线程等待队列中等待块信息。

注:

不同的时间点观察到的三个线程状态不一样,可另取时间点

3、修改WRK代码,增加同步/互斥数据结构的输出

KeWaitForSingleObject中的InitializeWaitSingle及KeWaitForMultipleObject中的InitializeWaitMultiple实现了对等待块的赋值操作,等待块负责将线程和等待的同步对象联系起来。

我们可以在等待块赋值之后遍历线程的等待队列,添加调试语句DbgPrint,线程调用WaitforSingalObject或WaitforMultipleObject就会自动向调试窗口WinDbg输出线程,等待块和同步对象的数据结构信息,包括线程的地址,线程等待同步对象队列中等待块的地址。

根据输出的调试信息,我们可以轻松地查看到线程等待的同步对象队列,而不需要对WRK的调试。

可以使用任意IDE打开并修改内核\base\ntos\ke的wait.c文件中代码。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > IT计算机 > 电脑基础知识

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2