实验四 进程的同步

上传人:jin****ng 文档编号:204593167 上传时间:2023-04-27 格式:DOCX 页数:10 大小:31.50KB
收藏 版权申诉 举报 下载
实验四 进程的同步_第1页
第1页 / 共10页
实验四 进程的同步_第2页
第2页 / 共10页
实验四 进程的同步_第3页
第3页 / 共10页
资源描述:

《实验四 进程的同步》由会员分享,可在线阅读,更多相关《实验四 进程的同步(10页珍藏版)》请在装配图网上搜索。

1、实验四 进程的同步一、实验目的 使用EOS的信号量编程解决生产者一消费者问题,理解进程同步的意义。 调试跟踪EOS的信号量的工作过程,理解进程同步的原理。 修改 EOS 的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步 的原理。二、预备知识2.1 临界资源和临界区多个并发执行的进程可以同时访问的硬件资源(打印机、磁带机)和软件资源(共享 内存)都是临界资源。由于进程的异步性,当它们争用临界资源时,会给系统造成混乱,所 以必须互斥地对它们进行访问。我们把在每个进程中访问临界资源的那段代码称为临界区 (Critical Section),可以使用互斥体(Mutex)保证各进程互

2、斥地进入自己的临界区Lock mutex and enter critical sectionCritical sectionRelease mutex and exit critical section可以看到进入临界区和退出临界区一定是成对出现的。2.2 进程的同步进程同步的主要任务是使并发执行的各进程之间能有效的共享资源和相互合作。可以使 用互斥体(Mutex)、事件(Event)和信号量(Semophore)等同步对象来解决一系列经典的 进程同步问题,例如“生产者消费者问题”、“读者写者问题”、“哲学家进餐问题”等。2.3 生产者消费者问题生产者消费者问题是一个著名的进程同步问题。它描

3、述的是:有一群生产者进程在 生产某种产品,并将此产品提供给一群消费者进程去消费。为使生产者进程和消费者进程能 并发执行,在他们之间设置了一个具有n个缓冲区的缓冲池,生产者进程可以将它生产的一 个产品放入一个缓冲区中,消费者进程可以从一个缓冲区中取得一个产品消费。尽管所有的 生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费 者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已经装有产品的缓冲区中放入 产品。2.4 EOS内核提供的用于线程同步的内核对象EOS内核提供了三种专门用于线程同步的内核对象,分别是互斥(Mutex)对象、信号 量(Semaphore)对

4、象和事件(Event)对象。另夕卜,EOS中的进程对象和线程对象也支持同 步功能。EOS中所有支持同步功能的对象都有两种状态:signaled和nonsignaled,对处于 nonsignaled状态的对象执行Wait操作将会使线程阻塞,直到对象变为signaled状态。EOS内核为同步对象提供了统一的Wait操作接口,其C语言函数声明如下:ULONG WaitForSingleObject (HANDLE Handle, ULONG Milliseconds);其参数意义如下表:参数描述HandleWait操作对象的句柄,可以是互斥体、信号量、事件、进程、线程等 任意支持同步功能的内核对象

5、。Milliseconds等待的最长时间,单位毫秒。时间终了,即使等待的对象未变为signaled 状态,此函数也会返回。如果此值为0,函数只是测试对象的状态并立 刻返回。如果此值为INFINITE,函数永远阻塞等待,直到对象变为 signaled状态才返回。设定合理的超时值,可以有效避免死锁。如果等待成功,对象变为signaled状态,返回0。如果等待超时,对象仍然为nonsignaled状 态,返回 WAIT_TIMEOUT。下面将分别介绍 Mutex、Semaphore 和 Event 对象。2.4.1 Mutex 对象EOS 提供的 Mutex 对象用于同步多个线程对临界资源的互斥访问

6、。 Mutex 对象包括一 个持有线程指针、一个递归计数器和一个等待队列。typedef struct _MUTEX PVOID OwnerThread;/当前拥有Mutex的线程指针ULONG RecursionCount;/递归拥有Mutex的计数器LIST_ENTRY WaitListHead;/等待队列MUTEX, *PMUTEX;当持有线程指针为NULL时,Mutex不被任何线程持有,此时Mutex处于signaled状态,当 指针指向持有Mutex的线程时,Mutex处于nonsignaled状态。当一个线程对Mutex成功调 用了 WaitForSingleObject后,调用线

7、程就持有了 Mutex,Mutex变为nonsignaled状态。此时 其它线程再对同一个Mutex调用WaitForSingleObject,将会被阻塞在Mutex的等待队列中, 直到持有线程调用ReleaseMutex释放Mutex使之变为signaled状态。Mutex还支持递归,持 有线程可以对同一 Mutex多次调用WaitForSingleObject而不被阻塞,递归计数器记录了持 有线程调用 WaitForSingleObject 的次数。只有 Mutex 的持有线程可以对 Mutex 调用 ReleaseMutex,持有线程每对Mutex调用一次ReleaseMutex, Mu

8、tex的递归计数器减小1, 当计数器变为0时释放Mutex使之变为signaled状态。关于EOS中Mutex对象的实现,请参看EOS内核源文件mutex.c。Mutex对象的Create函数如下:HANDLE CreateMutex(BOOL InitialOwner,PCSTR Name);其参数意义如下表参数描述InitialOwner为TRUE则初始化新建Mutex对象的持有线程为当前调用线程,为 FALSE则初始化新建 Mutex对象的持有线程为 NULL。如果 CreateMutex执行的是打开已存在的命名Mutex对象则忽略此值。NameMutex对象的名称。如果为NULL则创建

9、一个新的匿名Mutex对象。 如果不为NULL则先尝试打开已存在的命名Mutex对象,若命名 Mutex对象不存在则创建一个新的命名Mutex对象。如果函数执行成功则返回Mutex对象的句柄,否则返回NULL。Mutex对象的Signal操作函数如下:BOOL ReleaseMutex(HANDLE Handle);Handle为线程持有的Mutex对象的句柄。如果函数执行成功则返回TRUE,否则返回FALSE。 2.4.2 Semaphore 对象EOS 内核提供的 Semaphore 对象是记录型信号量,其整形变量大于 0 时处于 signaled 状态,否则处于 nonsignaled

10、状态。不同于标准记录信号量, Semaphore 可以设定其整型变 量的最大取值范围,signal操作可以指定整型变量的增加量而不是默认增加1。typedef struct SEMAPHORE LONG Count;/信号量的整形值LONG MaximumCount;/允许最大值LIST_ENTRY WaitListHead;/等待队列SEMAPHORE, *PSEMAPHORE;关于EOS中Semaphore对象的实现,请参看EOS内核源文件Semaphore Semaphore对象的Create函数如下:HANDLE CreateSemaphore(LONG InitialCount,LO

11、NG MaximumCount,PCSTR Name);其参数的意义如下表参数描述InitialCountSemaphore整形变量的初始值。如果CreateSemaphore执行的是 打开已存在命名Semaphore对象则忽略此值。MaximumCountSemaphore整形变里的允许最大值。如果CreateSemaphore执仃 的是打开已存在命名Semaphore对象则忽略此值。NameSemaphore对象的名称。如果为 NULL则创建一个新的匿名Semaphore对象。如果不为NULL则先尝试打开已存在的命名 Semaphore对象,若命名Semaphore对象不存在则创建一个新的

12、 命名Semaphore对象。如果函数执行成功则返回Semaphore对象的句柄,否则返回NULL。 Semaphore 对象的 Signal 操作函数如下:BOOL ReleaseSemaphore ( HANDLE Handle, LONG ReleaseCount, PLONG PreviousCount);其参数意义如下表参数描述HandleSemaphore对象的句柄。ReleaseCountSemaphore整型变量的增加量,此值必须大于0若此值可能使 整形变量超过预先设定的允许最大值,则整形变量值保持不变, 函数返回FALSEoPreviousCount指向整型变量的指针,用于保

13、存调用本函数前Semaphore的整型 变量值。如果为NULL则不保存。如果此函数执行成功则返回TRUE,否则返回FALSE。2.4.3 Event 对象Event 对象是 EOS 中最具弹性的同步对象, Event 的两种状态 signaled 和 nonsignaled 可 完全由程序控制,使用非常灵活。typedef struct _EVENT BOOL IsManual;/是否手动类型事件BOOL IsSignaled;/是否处于通知状态LIST_ENTRY WaitListHead;/等待队列EVENT, *PEVENT;对 Event 调用 SetEvent 可使之变为 signa

14、led 状态,调用 ResetEvent 可使之复位为 nonsignaled 状态。 Event 分为手动和 自动两种类型。 线程对手 动类型 Event 调 用 WaitForSingleObject并成功返回会时,Event保持signaled不改变,所有线程再对Event调用 WaitForSingleObject 都将立即成功返回,除非手动调用 ResetEvent 使之复位为 nonsignaled 状态。线程对自动类型Event调用WaitForSingleObject并成功返回时,Event将自动复位为 nonsignaled状态,所有线程再对Event调用WaitForSin

15、gleObject都将会被阻塞。关于EOS中Event对象的实现,请参看EOS内核源文件event.c。Event对象的Create函数如下:HANDLE CreateEvent (BOOL ManualReset,BOOL InitialState,PCSTR Name);其参数意义如下表参数描述ManualReset为TRUE则创建手动类型Event对象,为FALSE则创建自动类 型Event对象。如果CreateEvent执行的是打开已存在的命名 Event对象则忽略此值。InitialState为TRUE则初始化Event对象的状态为signaled,为FALSE则初 始化Event对

16、象的状态为nonsignaled。如果CreateEvent执行的 是打开已存在的命名Event对象则忽略此值。NameEvent对象的名称。如果为NULL则创建一个新的匿名Event对 象。如果不为NULL则先尝试打开已存在的命名Event对象,若 命名Event对象不存在则创建一个新的命名Event对象。如果函数执行成功则返回Event对象的句柄,否则返回NULL。Event对象的SetEvent函数如下:BOOL SetEvent(HANDLE Handle);Handle是要改变为signaled状态的Event对象的句柄。如果函数执行成功则返回TRUE,否 则返回FALSEo如果存在

17、线程正在阻塞等待Event对象,调用SetEvent之后,如果Event 是手动类型的,那么所有等待线程都将成功返回,同时Event保持signaled状态,否则只有 等待队列中的第一个线程成功返回,同时Event自动复位至nonsignaled状态。Event对象的ResetEvent函数如下:BOOL ResetEvent(HANDLE Handle,);Handle 是要改变为 nonsignaled 状态的 Event 对象的句柄。如果函数执行成功则返回 TRUE, 否则返回 FALSE。2.5在EOS应用项目中创建线程由于EOS引入了线程的概念,所以可以在EOS应用项目中创建新线程。

18、EOS提供了创 建线程的API函数:HANDLE CreateThread(SIZE_T StackSize,PTHREAD_START_ROUTINE StartAddr,PVOID ThreadParam,ULONG CreateFlags,PULONG ThreadId);其参数意义如下表参数描述StackSize线程栈的大小。目前没有使用,输入0即可。StartAddr线程开始执行的函数的指针。ThreadParam传递给线程函数的参数。CreateFlags创建参数,目前尚无参数可选,输入0即可。ThreadId指向用于保存线程ID变量的指针,如果为NULL就不获取ID。如果创建线程

19、成功就返回线程对象的句柄,否则返回NULL。线程函数类型PTHREAD_START_ROUTINE的定义如下:typedef ULONG (*PTHREAD_START_ROUTINE)( PVOID ThreadParameter );在调用CreateThread函数创建线程之前,要首先按照线程函数类型的定义,编写一个线程函 数。当线程创建成功后,无论是否调度执行新建线程,CreateThread函数都会立即返回,也 就是说创建者线程和被创建线程是异步执行的。当线程函数返回时,新建线程就结束执行 此时线程对象会由 nonsignaled 状态变为 signaled 状态。注意, EOS 应

20、用程序启动执行时会 创建一个主线程,在执行过程中使用CreateThread函数创建的线程可以认为是子线程,当主 线程结束后,进程就会结束,所有的子线程无论是否执行完毕,都会被强制结束执行,所以 主线程应该等待所有的子线程执行完毕后再返回。三、实验内容3.1 准备实验环境启动OS Lab,新建一个EOS Kernel项目。分别使用Debug配置和Release配置生成此 项目,生成完全版本的EOS SDK文件夹。3.2使用EOS的信号量解决生产者一消费者问题本实验提供了一个使用EOS的信号量解决生产者一消费者问题的EOS应用程序项目, 将此项目拷贝到磁盘上,使用3.1中生成的SDK文件夹覆盖此

21、项目中的SDK文件夹,然后 使用OS Lab打开此项目。仔细阅读此项目的源文件pc.c,可以看到此项目中定义了一个有10个缓冲区的缓冲池, 每个缓冲区中可以放入一个整型数据(产品),同时定义了产品数量为30。在main函数中 创建了用于互斥访问缓冲池的 Mutex (MutexHandle), 创建了 empty 信号量 ( EmptySemaphoreHandle ) , 表示缓冲池中空缓冲区的数量, 创建了 full 信号量(FullSemaphoreHandle),表示缓冲池中满缓冲区的数量,注意在创建这两个信号量时向 CreateSemaphore 函数传递的参数是不同的。接下来mai

22、n函数创建了生产者线程,此线程将生产的产品(整型数据)放入缓冲池中, 然后创建了消费者线程,此线程将缓冲池中的产品取出消费。注意,这里使用的是线程而不 是进程,但是同步的概念是一致的。仔细阅读生产者线程函数和消费者线程函数中的源代码, 思考在这两个线程函数中什么是临界资源?在这两个线程函数中分别有哪些代码是临界 区?哪些代码是进入临界区?哪些代码是退出临界区?进入临界区和退出临界区的代码是 否成对出现?按F7生成此项目,然后按F5启动调试,OS Lab会首先弹出一个调试异常对话框,选 择“是”调试异常,调试会中断,按F5继续调试,立即激活虚拟机窗口查看生产者一消费 者同步执行的结果。根据执行的

23、结果并且结合源代码,思考生产者线程和消费者线程是如何 使用Mutex、Empty信号量和Full信号量来实现同步的?这两个线程函数中对这三个同步对 象的操作能够改变顺序吗?按 Shift+F5 停止调试。3.3调试跟踪EOS的信号量的工作过程3.3.1 创建信号量按F5启动调试EOS应用项目,OS Lab会首先弹出一个调试异常对话框,选择“是” 调试异常,调试会中断。在main函数中创建Empty信号量的代码行添加一个断点EmptySemaphoreHandle = CreateSemaphore(BUFFER_SIZE, BUFFER_SIZE, NULL);按F5继续调试,到此断点处中断,

24、按F11调试进入CreateSemaphore函数,可以看到此API 函数只是调用了 PsCreateSemaphoreObject 函数来创建信号量对象,按 F11 调试进入 PsCreateSemaphoreObject 函数。由于信号量对象是 EOS 的核心对象,所以像其它核心对象 一样,在 PsCreateSemaphoreObject 函数中调用了 ObCreateObject 函数来创建核心对象。所 有核心对象在创建时要进行的相同操作(包括为对象分配内存,初始化对象头等)都是在 ObCreateObject函数中来完成的,而各种核心对象独有的部分(对象结构体中的数据)是由 核心对象

25、类型中注册的构造函数完成初始化的。对于信号量对象可以参看semaphore.c文件 中的PspCreateSemaphoreObjectType函数,此函数中的代码Initializer.Create = PspOnCreateSemaphoreObject;就是将函数PspOnCreateSemaphoreObject注册为信号量对象的构造函数,在创建信号量对象 时就会调用此函数来初始化信号量对象所独有的信息。在 semaphore.c 文件中找到PspOnCreateSemaphoreObject 函 数 ( 提 示 : 可 以 在 semaphore.c 文 件 中 选 中 文 本 Ps

26、pOnCreateSemaphoreObject,然后按Ctrl+F3,直到找到此函数的定义,以后可以使用此方 法查找其它函数的定义),在此函数中只是调用了 PsInitializeSemaphore 函数,继续查找到 PsInitializeSemaphore函数的定义,在此函数的第一行代码处设置一个断点,按F5继续调试 到此断点处中断。可以看到在此函数中初始化了信号量对象(SEMAPHORE结构体)的各 个成员,查看用来初始化这些成员的值,应该和调用CreateSemaphore函数时传入参数的值 是一致的。激活“调用堆栈”窗口,查看此时的调用堆栈,依次激活从堆栈底到堆栈顶的各 个调用堆栈

27、帧,观察各个帧对应的函数和参数,理解信号量对象创建的过程。3.3.2 等待、释放信号量 首先删除所有的断点(防止有些断点影响后面的调试),然后在生产者线程函数中等待Empty信号量的代码行WaitForSingleObject(EmptySemaphoreHandle, INFINITE);添加一个断点,按F5继续调试,到此断点处中断。此行代码是等待缓冲池中的空缓冲区, 由于这是第一次调用,所有的缓冲区都是空的,所以此次调用应该不需要等待就可以立即返 回,并且减少一个空缓冲区,下面调试此次等待过程,检验与预期是否一致。 与创建核心对象相同,所有支持同步功能的核心对象都在对象类型中注册了一个用于

28、 等待的函数,信号量对象注册了 PsWaitForSemaphore 函数做为等待函数,注册的代码可以 参看 semaphore.c 文件中的 PspCreateSemaphoreObjectType 函数。所以可以在 semaphore.c 文 件中PsWaitForSemaphore函数的第一行代码处添加一个断点,然后按F5继续调试,到此断 点处中断。激活“调用堆栈”窗口,查看进入PsWaitForSemaphore函数的调用过程,注意 应该是从生产者线程进入的。仔细查看 PsWaitForSemaphore 函数的代码,可以看到在下面 两行代码之间的部分是原子操作IntState = K

29、eEnablelnterrupts(FALSE); / 开始原子操作,关闭中断。KeEnablelnterrupts(IntState); / 原子操作完成,恢复中断。原子操作是一个整体,不会被打断,所以同步对象在改变自身的状态时都会使用原子操作, 在今后的调试中要特别注意。按F10调试,直到完成PsWaitForSemaphore函数中的原子操 作,可以看到此次执行并没有进行等待,只是将Empty信号量的计数减少了 1就返回了, 表示缓冲池中空缓冲区的数量减少了1。在生产者线程函数中释放Full信号量的代码行ReleaseSemaphore(FullSemaphoreHandle, 1, N

30、ULL);添加一个断点,按F5继续调试,到此断点处中断,按F11调试进入此函数,继续按F11调 试进入PsReleaseSemaphoreObject函数,最后调试进入PsReleaseSemaphore函数。仔细查看 PsReleaseSemaphore函数的代码,注意其中的原子操作都包含了哪些操作。按F10调试,直 到完成 PsReleaseSemaphore 函数的原子操作,可以看到此次执行没有唤醒其它线程(因为 此时没有线程在Full信号量上被阻塞),只是将Full信号量的计数增加了 1,表示缓冲池中 满缓冲区的数量增加了 1。生产者线程通过等待Empty信号量使空缓冲区数量减少了 1

31、,通过释放Full信号量使 满缓冲区数量增加了 1,这样就表示生产者线程生产了一个产品并占用了一个缓冲区。同样 的,消费者线程通过等待Full信号量使满缓冲区数量减少1,通过释放Empty信号量使空缓 冲区数量增加了 1,这样就表示消费者线程消费了一个产品并清空了一个缓冲区。可以在消 费者线程函数中对信号量操作的代码处添加两个断点进行调试。由于开始时生产者线程生产产品的速度较快,而消费者线程消费产品的速度较慢,所 以当缓冲池中所有的缓冲区都被产品占用时,生产者再生产新的产品就会等待,也就是在减 少空缓冲区数量时会等待,下面调试这种情况。首先停止之前的调试, 删除所有的断点后重新启动调试, 第一

32、次中断后在 PsWaitForSemaphore 函数的PspWait(&Semphore-WaitListHead, INFINITE);代码行添加一个断点,按F5继续调试,激活虚拟机窗口查看输出,刚开始生产者消费者都 不需要等待,同步执行一段时间后才到此断点处中断。激活“调用堆栈”窗口,可以看到此 次调用是从生产者线程函数进入的,激活生产者线程函数所在的堆栈帧,查看此函数中变量 i的值为14,表示生产者线程正在尝试生产14号产品,并且绿色箭头指向等待Empty信号 量的代码行。重新激活 PsWaitForSemaphore 函数的堆栈帧,查看 Empty 信号量计数 (Semphore-C

33、ount)的值为-1,表示在将计数减1之前计数为0,也就是缓冲池中已经没有 空缓冲区了,所以要调用PspWait函数(可以在3.1中创建的EOS Kernel项目文件夹中打开 文件sched.c查看PspWait函数的说明和源代码)将生产者线程放入Empty信号量的等待队 列中进行让权等待,直到消费者线程从缓冲池中消费了一个产品产生了一个空缓冲区,生产 者线程才会被唤醒从而继续生产14号产品。此时激活虚拟机窗口查看输出的结果,可以看 到生产了从0到 13的 14个产品,但是只消费了从0 到3 的4 个产品,有10个产品没有被 消费,所以缓冲池中的10 个缓冲区就都被占用了,这与之前调试的结果是

34、一致的。由于只有当消费者线程从缓冲池中消费了一个产品从而产生了一个空缓冲区后,生产 者线程才会被唤醒从而继续生产14 号产品,所以我们首先删除所有断点,然后在消费者线 程函数的释放Empty信号量的代码行ReleaseSemaphore(EmptySemaphoreHandle, 1, NULL);添加一个断点,按F5继续调试,到此断点处中断。查看消费者线程函数中变量i的值为4, 说明已经消费了 4号产品(之前消费了 0到3号产品)。按F11调试进入,一直调试进入 PsReleaseSemaphore函数,查看Empty信号量计数(Semphore-Count)的值为-1,和之前 在生产者线程

35、中的值是一致的。按F10单步调试PsReleaseSemaphore函数,直到在代码 PspWakeThread(&Semaphore-WaitListHead, STTUS_SUCCESS);处中断,此时Empty信号量计数的值已经由-1增加为了 0,需要调用PspWakeThread函数(可 以在3.1中创建的EOS Kernel项目文件夹中打开文件sched.c查看PspWakeThread函数的说 明和源代码)唤醒 Empty 信号量等待队列中的一个线程(就是之前被阻塞的生产者线程) 到就绪队列中,然后调用PspSchedule函数执行调度,这样之前因为Empty信号量而阻塞的 生产者

36、线程就会继续执行。为了验证这一点,在PsWaitForSemaphore函数的最后一行代码 处添加一个断点,然后按F5继续调试,在此断点处中断,查看Empty信号量的计数值为0, 在“调用堆栈”窗口中可以看到是由生产者线程函数进入的,激活生产者线程函数的堆栈帧, 查看变量i的值为14,表明之前被阻塞的尝试生产14号产品的生产者线程已经从PspWait 函数返回并继续执行了。删除所有断点后,按F5继续执行,观察虚拟机窗口中的输出结果,可以看到在执行的 后半段,由于消费者线程消费产品的速度快于生产者线程生产产品的速度,当消费者线程尝 试消费24号产品时,由于缓冲池中所有的产品都已经被消费了,所以消

37、费者线程会被阻塞, 直到生产者线程生产了 24号产品后,消费者线程才被唤醒继续执行。按 Shift+F5 停止调试。根据之前设置断点和调试的方法,自己设计一个类似的调试方 案来验证消费者线程在消费24号产品时会被阻塞,直到生产者线程生产了24号产品后,消 费者线程才被唤醒继续执行的过程。注意,在重新启动调试之前,必须先删除或者禁用所有 断点,在第一次中断后才能够添加或者启用断点。提示,可以在启动调试并且第一次中断后, 在消费者线程函数中等待Full信号量的代码行WaitForSingleObject(FullSemaphoreHandle, INFINITE);添加一个断点,然后在“断点”窗口

38、(按Alt+F9打开)中此断点的名称上点击右键,在弹 出的快捷菜单中选择“条件”,在弹出的“断点条件”对话框(按 F1 获得帮助)的表达式 编辑框中输入表达式“i = = 24 ”,然后点击“确定”按钮,按F5继续调试,这样只有当消 费者线程尝试消费24号产品时才会在此条件断点处中断。3.4修改EOS的信号量算法3.4.1 要求 通过之前对信号量对象创建、等待和释放等功能的调试以及对相关代码的阅读,已经 对 EOS 提供的信号量对象有了一定的认识。可以发现 PsWaitForSemaphore 函数的 Milliseconds 参数目前只能是 INFINITE,并且 PsReleaseSema

39、phore 函数的 ReleaseCount 参数 目前只能是1,这就造成EOS提供的信号量对象还不能支持超时等待功能。现在要求在之 前对信号量对象认识的基础上,尝试修改 PsWaitForSemaphore 函数和 PsReleaseSemaphore 函数中的代码,使这两个参数能够真正起到作用,使信号量对象支持超时等待功能。使用修改完毕的EOS Kernel项目生成完全版本的SDK文件夹,并覆盖到之前的生产者 消费者项目中。将生产者线程函数中等待Empty信号量的代码行WaitForSingleObject(EmptySemaphoreHandle, INFINITE);修改为while(

40、WAIT_TIMEOUT = WaitForSingleObject(EmptySemaphoreHandle, 300) printf(Producer wait for empty semaphore timeoutn);将消费者线程函数中等待Full信号量的代码行WaitForSingleObject(FullSemaphoreHandle, INFINITE);修改为while(WAIT_TIMEOUT = WaitForSingleObject(FullSemaphoreHandle, 300) printf(Consumer wait for full semaphore time

41、outn); 然后启动调试新的生产者消费者项目,查看在虚拟机中输出的结果,验证信号量超时等待 功能是否能够正常执行。如果有错误,可以调试内核代码来查找错误,然后在内核项目中修 改,重新生成 SDK 文件夹。如果超时等待功能已经能够正常执行,可以考虑将消费者线程修改为一次消费两个产 品,来测试 ReleaseCount 参数是否能够正常使用。3.4.2 提示 对于支持超时等待功能的信号量,其计数值只能是大于等于0,不能再像之前代码中可 以将计数值减为负值。当计数值大于0时,表示信号量为signaled状态;当计数值等于0时, 表示信号量为 nonsignaled 状态。在修改PsWaitForS

42、emaphore函数时要注意,不能像修改之前的代码那样先减少计数值, 再用计数值与0比较,如果计数值小于0就将线程阻塞。而是应该先用计数值和0比较,当 计数值大于0时,减少计数值后直接返回成功;当计数值等于0时,线程应该调用PspWait 函数从而被阻塞,并将参数Milliseconds传入PspWait函数做为超时时间,注意返回值应该 根据PspWait函数的返回值而确定。关于将线程阻塞在信号量的等待队列中的函数PspWait, 可以在 EOS Kernel 项目中打开文件 sched.c 查看此函数的说明和源代码。在修改 PsReleaseSemaphore 函数时应该考虑使用循环语句,并

43、且使用 ReleaseCount 参 数做为循环计数器。由于原子操作是一个整体,所以具体如何循环并不重要,重要的是在原 子操作结束后满足下面的结果:如果在原子操作开始之前,被阻塞的线程数量大于等于 ReleaseCount,则原子操作结束后,有ReleaseCount个线程会被唤醒,而且信号量计数的值 仍然为0;如果在原子操作开始之前,被阻塞的线程数量(可以为0)小于ReleaseCount, 则原子操作结束后,所有被阻塞的线程都会被唤醒,并且信号量的计数值ReleaseCount 之前被阻塞线程的数量之前信号量的计数值。关于将在信号量等待队列中的线程唤醒的函 数PspWakeThread,可以在EOS Kernel项目中打开文件sched.c查看此函数的说明和源代码。 在循环的过程中可以使用宏定义函数ListlsEmpty判断信号量的等待队列是否为空,例如 ListlsEmpty(&Semaphore-WaitListHead) 可以在EOS Kernel项目中打开文件rtl.h查看此宏定义的说明和源代码。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!