版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、溫州大學(xué)物理與電子信息工程學(xué)院 楊衛(wèi)波嵌入式系統(tǒng)原理與應(yīng)用第12章 任務(wù)間通信與同步*嵌入式系統(tǒng)原理與應(yīng)用2pRTX配置下是有并發(fā)的,但任一個時刻點上只有一個程序在處理機上運行。實際上并行是會涉及很多問題的,如多人同時在干活,他們需要共享到一些資源,那就涉及到溝通協(xié)調(diào)的額外付出了。pRTX的進程間通訊主要依賴于四種機制:事件(Event)、互斥鎖(Mutex)、信號量(Semaphore)、和郵箱(Mailbox)。前三種機制側(cè)重進程間的同步,郵箱側(cè)重進程間的數(shù)據(jù)通訊。前言任務(wù)間的同步任務(wù)間的同步任務(wù)間的同步 - 各任務(wù)運行的先后、觸發(fā)等關(guān)系備注:數(shù)據(jù)采集任務(wù)A 和數(shù)據(jù)處理任務(wù)B 之間存在同步
2、關(guān)系任務(wù)間的互斥任務(wù)間的互斥任務(wù)間的互斥 - 共享資源的申請使用備注:數(shù)據(jù)采集任務(wù)A 和數(shù)據(jù)處理任務(wù)B 之間存在互斥關(guān)系12.1 幾個重要的概念p臨界段:代碼的臨界段也稱為臨界區(qū),一旦這部分代碼開始執(zhí)行,則不允許任何中斷打斷。為確保臨界段代碼的執(zhí)行不被中斷,在進入臨界段之前須關(guān)中斷,而臨界段代碼執(zhí)行完畢后,要立即開中斷。 p由于Cortex-M3/M4的RTX內(nèi)核庫中沒有關(guān)閉中斷的操作,也就是說 RTX 的源碼中不存在臨界段。p用戶寫應(yīng)用的時候也有臨界段的問題,比如以下兩種: u讀取或者修改變量(特別是任務(wù)間通信的全局變量)的代碼,一般來說這是最常見的臨界代碼。u調(diào)用公共函數(shù)的代碼,特別是不可
3、重入的函數(shù),如果多個任務(wù)都訪問這個函數(shù),結(jié)果是可想而知的。 幾個重要的概念p中斷鎖:因為Cortex-M3/M4的RTX源碼中沒有關(guān)閉中斷的操作,所以也就沒有提供開關(guān)中斷函數(shù)。p用戶在自己的應(yīng)用代碼采用裸機時如何開關(guān)中斷的,在使用了RTX后仍然使用以前的開關(guān)中斷函數(shù)即可。任務(wù)鎖p任務(wù)鎖:為了防止當(dāng)前任務(wù)的執(zhí)行被其它高優(yōu)先級的任務(wù)打斷而提供的鎖機制就是任務(wù)鎖任務(wù)鎖。實現(xiàn)任務(wù)鎖可以通過給調(diào)度器加鎖或者直接關(guān)閉RTOS內(nèi)核定時器(就是前面一直說的系統(tǒng)滴答定時器)來實現(xiàn)。 u通過給調(diào)度器加鎖實現(xiàn)給調(diào)度器加鎖的話,就無法實現(xiàn)任務(wù)切換,高優(yōu)先級任務(wù)也就無法搶占低優(yōu)先級任務(wù)的執(zhí)行,同時高優(yōu)先級任務(wù)也是無法向
4、低優(yōu)先級任務(wù)切換的。這種方式只是禁止了調(diào)度器工作,并沒有關(guān)閉任何中斷。u通過關(guān)閉 RTOS 內(nèi)核定時器實現(xiàn)關(guān)閉了 RTOS 內(nèi)核定時器的話,也就關(guān)閉了通過 RTOS 內(nèi)核定時器中斷實現(xiàn)任務(wù)切換的功能,因為在退出定時器中斷時需要檢測當(dāng)前需要執(zhí)行的最高優(yōu)先級任務(wù),如果有高優(yōu)先級任務(wù)就緒的話需要做任務(wù)切換。RTX 操作系統(tǒng)是采用的這種方式實現(xiàn)任務(wù)鎖的。12.2 RTX 任務(wù)鎖的實現(xiàn)函數(shù)原型: voidvoid tsk_lock ( tsk_lock (voidvoid); ); 函數(shù)描述:禁止 RTX 內(nèi)核定時器中斷,因此也就禁止了任務(wù)切換。 注意以下問題: p函數(shù) tsk_lock 不支持嵌套調(diào)用
5、;p不允許在中斷服務(wù)程序中調(diào)用 tsk_lock;pRTX 內(nèi)核定時器被關(guān)閉期間,RTX 內(nèi)核任務(wù)調(diào)度器和需要時間片調(diào)度的任務(wù)被阻塞。p設(shè)置的任務(wù)延遲時間不再工作。p因此,強烈建議關(guān) RTX 內(nèi)核定時器中斷的時間越短越好。RTX 任務(wù)鎖的實現(xiàn)函數(shù)原型: voidvoid tsk_unlock ( tsk_unlock (voidvoid); ); 函數(shù)描述: 用于使能 RTX 內(nèi)核定時器中斷,因此也就重新開啟任務(wù)切換。tsk_unlock 一定要跟 tsk_lock 配套使用。 使用這個函數(shù)要注意以下問題:函數(shù) tsk_lock 不支持嵌套調(diào)用;不允許在中斷服務(wù)程序中調(diào)用 tsk_lock。使
6、用舉例: #include#include voidvoid protect_critical_op() protect_critical_op() tsk_lock(); tsk_lock(); do_critical_op(); do_critical_op(); tsk_unlock();tsk_unlock(); 12.2.3 例程說明實驗內(nèi)容: 1. K1 按鍵按下,串口打印。2. 在調(diào)用 printf 函數(shù)的地方都加上任務(wù)鎖,防止多個任務(wù)調(diào)用此函數(shù)造成沖突,以至于串口打印出現(xiàn)亂碼。3. 各個任務(wù)實現(xiàn)的功能如下: AppTaskUserIF任務(wù) :按鍵消息處理。 AppTaskLED
7、 任務(wù):LED 閃爍,并串口打印任務(wù)正在運行。 AppTaskMsgPro 任務(wù):消息處理,用作 LED 閃爍和串口打印任務(wù)正在運行。 AppTaskStart 任務(wù):啟動任務(wù),最高優(yōu)先級任務(wù),實現(xiàn)按鍵掃描。RTX配置四個 RTX 任務(wù)的實現(xiàn)_task _task voidvoid AppTaskMsgPro( AppTaskMsgPro(voidvoid) ) whilewhile (1) (1) bsp_LedToggle(1); bsp_LedToggle(1); bsp_LedToggle(4);bsp_LedToggle(4);/ /* * 開啟任務(wù)鎖開啟任務(wù)鎖 */tsk_lock
8、();tsk_lock();printf(printf( 任務(wù)任務(wù)AppTaskMsgPro正在運行正在運行rn););/ /* * 關(guān)閉任務(wù)鎖關(guān)閉任務(wù)鎖 */tsk_unlock();tsk_unlock();os_dly_wait(300);os_dly_wait(300); RTX 任務(wù)調(diào)試信息12.3 事件標(biāo)志組事件標(biāo)志組是實現(xiàn)多任務(wù)同步的有效機制之一。初學(xué)者會問采用事件標(biāo)志組多麻煩,搞個全局變量不是更簡單,其實不然。在裸機編程時,使用全局變量的確比較方便,但相比事件標(biāo)志組主要有如下三個問題: p使用事件標(biāo)志組可以讓 RTOS 內(nèi)核有效的管理任務(wù),全局變量是無法做到的,任務(wù)的超時等機制
9、需要用戶自己去實現(xiàn)。p使用了全局變量就要防止多任務(wù)的訪問沖突,使用事件標(biāo)志組已經(jīng)處理好了這個問題。用戶無需擔(dān)心。 p使用事件標(biāo)志組可以有效的解決中斷服務(wù)程序和任務(wù)之間的同步問題。12.3.1 事件標(biāo)志組的實現(xiàn)創(chuàng)建 2 個任務(wù) Task1 和 Task2,運行過程描述如下: p任務(wù) Task1 運行過程中調(diào)用函數(shù) os_evt_wait_and,等待事件標(biāo)志位被設(shè)置,任務(wù) Task1 由運行態(tài)進入到掛起態(tài)p任務(wù) Task2 設(shè)置了任務(wù) Task1 的事件標(biāo)志,任務(wù) Task1 由掛起態(tài)進入到就緒態(tài),在調(diào)度器的作用下由就緒態(tài)又進入到運行態(tài)。各個任務(wù)之間使用事件標(biāo)志組實現(xiàn)任務(wù)的通信或者同步。RTX
10、每個任務(wù)創(chuàng)建的時候,會自動創(chuàng)建 16 個事件標(biāo)志,事件標(biāo)志被存儲到每個任務(wù)的任務(wù)控制塊中,即每個任務(wù)支持 16 個事件標(biāo)志。12.3.2 中斷方式事件標(biāo)志組的實現(xiàn) 指中斷函數(shù)和 RTX 任務(wù)之間使用事件標(biāo)志運行條件:創(chuàng)建 1 個任務(wù)和一個串口接收中斷運行過程描述如下: p任務(wù) Task1 運行過程中調(diào)用函數(shù) os_evt_wait_and,等待事件標(biāo)志位被設(shè)置,任務(wù) Task1 由運行態(tài)進入到掛起態(tài)pTask1 掛起的情況下,串口接收到數(shù)據(jù)進入到了串口中斷服務(wù)程序,在串口中斷服務(wù)程序中設(shè)置 Task1 的事件標(biāo)志,任務(wù) Task1 由掛起態(tài)進入到就緒態(tài),在調(diào)度器的作用下由就緒態(tài)又進入到運行態(tài)。
11、12.3.2 中斷方式事件標(biāo)志組的實現(xiàn) 實際應(yīng)用中,中斷方式的消息機制切記注意以下四個個問題: p中斷函數(shù)的執(zhí)行時間越短越好,防止其它低于這個中斷優(yōu)先級的異常不能得到及時響應(yīng)。p實際應(yīng)用中,建議不要在中斷中實現(xiàn)消息處理,用戶可以在中斷服務(wù)程序里面發(fā)送消息通知任務(wù),在任務(wù)中實現(xiàn)消息處理,這樣可以有效的保證中斷服務(wù)程序的實時響應(yīng)。同時此任務(wù)也需要設(shè)置為高優(yōu)先級,以便退出中斷函數(shù)后任務(wù)可以得到及時執(zhí)行。p中斷服務(wù)程序中一定要調(diào)用專用于中斷的事件標(biāo)志設(shè)置函數(shù) isr_evt_setisr_evt_set。p在 RTX 操作系統(tǒng)中實現(xiàn)中斷函數(shù)跟裸機編程是一樣的。中斷方式*嵌入式系統(tǒng)原理與應(yīng)用18p節(jié)拍時
12、鐘任務(wù)不僅僅在時鐘節(jié)拍中斷產(chǎn)生時運行,而且當(dāng)一個中斷調(diào)用isr_函數(shù)時也執(zhí)行。這是因為中斷不能使當(dāng)前任務(wù)等待,因此也不能進行任務(wù)的切換。p中斷可以產(chǎn)生事件、信號量或是消息(使用isr_庫函數(shù))來表明一個更高優(yōu)先級的任務(wù)正在等待。p更高優(yōu)先級的任務(wù)將搶占當(dāng)前的任務(wù),但必須在中斷函數(shù)結(jié)束后才能進行。這樣就強制產(chǎn)生在當(dāng)前中斷結(jié)束后運行的時鐘節(jié)拍中斷,而且激發(fā)任務(wù)調(diào)度。任務(wù)調(diào)度處理所有的任務(wù),并將就緒的最高優(yōu)先級的任務(wù)投入運行,最高優(yōu)先級的任務(wù)因此可以持續(xù)的運行。*嵌入式系統(tǒng)原理與應(yīng)用19PendSV中斷12.3.3 事件標(biāo)志組 API 函數(shù)使用如下6個函數(shù)可以實現(xiàn) RTX 的事件標(biāo)志組: pos_e
13、vt_clros_evt_clr();();pos_evt_getos_evt_get();();pos_evt_setos_evt_set();();pos_evt_wait_andos_evt_wait_and();();pos_evt_wait_oros_evt_wait_or();();pisr_evt_setisr_evt_set();();函數(shù) os_evt_setvoidvoid os_evt_set ( U16 event_flags, /* 16位的事件標(biāo)志設(shè)置位的事件標(biāo)志設(shè)置 */ OS_TID task ); /* 要設(shè)置事件標(biāo)志的任務(wù)要設(shè)置事件標(biāo)志的任務(wù)ID */ 函數(shù)
14、描述:用于設(shè)置指定任務(wù)的事件標(biāo)志。 p第1個參數(shù)表示16個可設(shè)置的事件標(biāo)志位。因為RTX的每個任務(wù)創(chuàng)建時有16個可設(shè)置的事件標(biāo)志,這里用 U16 類型的變量 event_flag 就可以表示,變量 event_flag 的某個位設(shè)置為 1,那么指定 RTX 任務(wù)的事件標(biāo)志相應(yīng)位就設(shè)置為 1。變量 event_flag 設(shè)置為 0 的位對 RTX 任務(wù)的事件標(biāo)志相應(yīng)位沒有影響。比如設(shè)置變量 event_flag = 0 x0003 就表示將 RTX 任務(wù)事件標(biāo)志的位 0 和位 1 設(shè)置為 1,其余位沒有變化。p第 2 個參數(shù)是任務(wù) ID。函數(shù) os_evt_set使用這個函數(shù)要注意:此函數(shù)是用于
15、任務(wù)代碼中調(diào)用的,故不可以在中斷服務(wù)程序中調(diào)用此函數(shù),中斷服務(wù)程序中使用的是 isr_evt_set#include #include _task void task1 (_task void task1 (voidvoid) . ) . os_evt_set (0 x0003, tsk2); os_evt_set (0 x0003, tsk2); . . 函數(shù) isr_evt_set voidvoid isr_evt_set ( U16 event_flags, /* 16位的事件標(biāo)志設(shè)置位的事件標(biāo)志設(shè)置 */ OS_TID task ); /* 要設(shè)置事件標(biāo)志的任務(wù)要設(shè)置事件標(biāo)志的任務(wù)ID
16、 */ 函數(shù)描述函數(shù)描述:用于設(shè)置指定任務(wù)的事件標(biāo)志。 p第1個參數(shù)表示16個可設(shè)置的事件標(biāo)志位。p第 2 個參數(shù)是任務(wù) ID。使用這個函數(shù)要注意以下問題: 1.此函數(shù)是用于中斷服務(wù)程序中調(diào)用的。2. 調(diào)用不能太頻繁,太頻繁的話會大大增加系統(tǒng)內(nèi)核的開銷,會造成事件標(biāo)志得不到及時處理從而造成丟失事件標(biāo)志的情況。#include #include voidvoid EXTI0_IRQHandler ( EXTI0_IRQHandler (voidvoid) . ) . isr_evt_set (0 x0003, tsk2); . isr_evt_set (0 x0003, tsk2); . 函數(shù)
17、os_evt_wait_andOS_RESULT os_evt_wait_and ( U16 wait_flags, /* 16位的事件標(biāo)志等待位的事件標(biāo)志等待 */ U16 timeout ); /* 超時時間設(shè)置超時時間設(shè)置 */ 函數(shù)描述函數(shù)描述:用于等待事件標(biāo)志被設(shè)置。 p第1 個參數(shù)表示任務(wù)等待的事件標(biāo)志位。因為 RTX 的每個任務(wù)創(chuàng)建時有 16 個可以設(shè)置的事件標(biāo)志,這里用 U16 類型的變量 event_flag 就可以設(shè)置,變量 event_flag 的那位設(shè)置為 1,那么 RTX 任務(wù)的事件標(biāo)志就等待那個位被設(shè)置為 1。而且要所有要求的位都被設(shè)置為 1 才可以。比如設(shè)置變量
18、event_flag = 0 x0003 就表示 RTX 任務(wù)在等待事件標(biāo)志的位 0 和位 1 都被設(shè)置為 1。p第 2 個參數(shù)表示設(shè)在的等待時間,范圍 0-0 xFFFF,當(dāng)參數(shù)設(shè)置為 0-0 xFFFE 時,表示等這么多個時鐘節(jié)拍,參數(shù)設(shè)置為 0 xFFFF 時表示無限等待直到事件標(biāo)志滿足要求。函數(shù) os_evt_wait_andp函數(shù)返回 OS_R_EVT 表示等待的事件標(biāo)志位都被設(shè)置了,也就是返回成功。返回 OS_R_TMO 表示超時。使用這個函數(shù)要注意以下問題: 1. 當(dāng)要求的事件標(biāo)志位都被設(shè)置為 1 時或者設(shè)置的超時時間溢出時,函數(shù) os_evt_wait_and 才會返回。 2.
19、 如果函數(shù) os_evt_wait_and 返回前所要求的事件標(biāo)志位都設(shè)置了,那么此函數(shù)會在返回前將相應(yīng)的事件標(biāo)志位清零,其它位不受此影響。函數(shù)os_evt_wait_oros_evt_wait_or(U16wait_flags,U16timeout);與事件等待,參數(shù)意義與和事件等待相同。這兩個操作的差別在于(其實應(yīng)該比較一目了然)對于和事件等待而言,所有標(biāo)示都滿足了,進程才會被喚醒,對于與事件等待而言,任一標(biāo)示狀態(tài)滿足了就會喚醒進程。 例子:os_evt_wait_and (0 xABCD, 0 xFFFF);os_evt_wait_or (0 xABCD, 0 xFFFF);p第一個語句
20、的話,進程會等待15,13,11,9,8,7,6,3,2,0位(0 xABCD)的標(biāo)示全部被設(shè)為1時才會被喚醒;p第二個語句而言,15,13,11,9,8,7,6,3,2,0位中的任一一位標(biāo)示被設(shè)為1,進程就會被喚醒。使用舉例#include#include #define#define BIT_0BIT_0 (1 0) (1 0) #define#define BIT_1BIT_1 (1 1) (1 1) #define#define BIT_ALLBIT_ALL ( (BIT_0BIT_0 | | BIT_1BIT_1) ) _task _task voidvoid AppTaskMsgP
21、ro( AppTaskMsgPro(voidvoid) ) OS_RESULT xResult;OS_RESULT xResult;constconst uint16_t usMaxBlockTime = 500; uint16_t usMaxBlockTime = 500; / /* * 延遲周期延遲周期 */whilewhile (1) (1) xResult = os_evt_wait_and(xResult = os_evt_wait_and(BIT_ALLBIT_ALL, usMaxBlockTime);, usMaxBlockTime);switchswitch (xResult)
22、 (xResult) casecase OS_R_EVT: OS_R_EVT:/ /* * 接收到接收到bit1和和bit0都被設(shè)置的消息都被設(shè)置的消息 */printf(printf( 接收到接收到bit0和和bit1都被設(shè)置的消息都被設(shè)置的消息rn););breakbreak; ;casecase OS_R_TMO: OS_R_TMO: / /* * 超時超時 */bsp_LedToggle(1);bsp_LedToggle(1);bsp_LedToggle(4);bsp_LedToggle(4);breakbreak; ;defaultdefault: : / /* * 其他值不處理其他
23、值不處理 */breakbreak; ; *嵌入式系統(tǒng)原理與應(yīng)用28os_evt_get()12.3.4 例程說明(任務(wù)間通信)內(nèi)容: 1. K1 按鍵按下,串口打印。2. K2 鍵按下,直接發(fā)送事件標(biāo)志給任務(wù)AppTaskMsgPro,設(shè)置 bit0 3. K3 鍵按下,直接發(fā)送事件標(biāo)志給任務(wù) AppTaskMsgPro,設(shè)置 bit1 4. 任務(wù) AppTaskMsgPro 只有接收到 bit0 和 bit1 都被設(shè)置了才執(zhí)行串口打印信息。各個任務(wù)實現(xiàn)的功能如下: AppTaskUserIF 任務(wù):按鍵消息處理。 AppTaskLED 任務(wù):LED 閃爍。 AppTaskMsgPro 任務(wù)
24、:消息處理,等待任務(wù) AppTaskUserIF 發(fā)來的事件標(biāo)志。 AppTaskStart 任務(wù) :啟動任務(wù),最高優(yōu)先級任務(wù),實現(xiàn)按鍵掃描。 RTX 配置RTX 任務(wù)調(diào)試信息Event Value:任務(wù) AppTaskMsgPro 當(dāng)前的事件標(biāo)志數(shù)值。Event Mask:任務(wù) AppTaskMsgPro 等待的事件標(biāo)志數(shù)值。12.3.4 例程說明(中斷方式通信) 內(nèi)容: 1. K1 按鍵按下,串口打印。2. K2鍵按下,啟動單次定時器中斷,50ms后在定時器中斷給任務(wù)AppTaskMsgPro發(fā)送事件標(biāo)志,設(shè)置 bit0。3. K3鍵按下,啟動單次定時器中斷,50ms后在定時器中斷給任務(wù)A
25、ppTaskMsgPro發(fā)送事件標(biāo)志,設(shè)置 bit1。 4. 任務(wù) AppTaskMsgPro 只有接收到 bit0 和 bit1 都被設(shè)置了才執(zhí)行串口打印信息。各個任務(wù)實現(xiàn)的功能如下: AppTaskUserIF 任務(wù):按鍵消息處理。 AppTaskLED 任務(wù):LED 閃爍。 AppTaskMsgPro 任務(wù) :消息處理,等待定時器中斷發(fā)來的事件標(biāo)志。 AppTaskStart 任務(wù) :啟動任務(wù),也是最高優(yōu)先級任務(wù),這里實現(xiàn)按鍵掃描。*嵌入式系統(tǒng)原理與應(yīng)用33#define#define EVT_KEYEVT_KEY 0 x0001 0 x0001OS_TID pr_task;OS_TID
26、 pr_task;intint num_ints; num_ints;/ /* *- External 0 Interrupt Service Routine- External 0 Interrupt Service Routine-* */ /voidvoid ext0_int( ext0_int(voidvoid) _irq ) _irq isr_evt_set(isr_evt_set(EVT_KEYEVT_KEY, pr_task); , pr_task); / /* * Send event to process_task Send event to process_task* */
27、 /. _task _task voidvoid process_task( process_task(voidvoid) ) num_ints = 0;num_ints = 0;whilewhile (1) (1) os_evt_wait_or(os_evt_wait_or(EVT_KEYEVT_KEY, 0 xffff);, 0 xffff);num_ints+;num_ints+; voidvoid init_task( init_task(voidvoid) _task ) _task .pr_task = os_tsk_create(process_task,100);pr_task
28、 = os_tsk_create(process_task,100);os_tsk_delete_self(); os_tsk_delete_self(); / /* * Terminate this task Terminate this task * */ / RTX核中使用中斷任務(wù)process_task僅是記錄發(fā)生中斷的次數(shù)*嵌入式系統(tǒng)原理與應(yīng)用34分析程序的功能#include RTL.hOS_TID id1, id2;_task void task1 (void);_task void task2 (void);_task void task1 (void) id1 = os_ts
29、k_self (); id2 = os_tsk_create (task2, 0); for (;) os_evt_set (0 x0004, id2); os_evt_wait_or (0 x0004, 0 xffff); os_dly_wait (5); _task void task2 (void) for (;) os_evt_wait_or (0 x0004, 0 xffff); os_dly_wait (2); os_evt_set (0 x0004, id1); int main (void) os_sys_init (task1);12.4 信號量信號量(semaphores)
30、是20世紀(jì)60年代中期Edgser Dijkstra發(fā)明的。最初目的是為了給共享資源建立一個表示該共享資源被占用情況的標(biāo)志。當(dāng)一個任務(wù)在訪問共享資源前,先對這個標(biāo)志進行查詢,在了解資源被占用的情況后,再來決定自己的行為。 如有個 30 人的電腦機房,創(chuàng)建信號量的初始化值是30,表示30 個可用資源。要求一個同學(xué)使用一臺電腦,有一個同學(xué)使用一臺電腦,信號量的數(shù)值就減一。當(dāng)信號量的數(shù)值為 0時,還有同學(xué)沒有電腦可以使用,那么這個同學(xué)就得等待,直到有同學(xué)離開,有一個同學(xué)離開,那么信號量的數(shù)值就加 1,依次類推。剛才沒有電腦用的同學(xué)此時就有電腦可以用了。這么一個過程就是使用信號量來管理共享資源的過程。
31、 12.4 信號量平時使用信號量主要實現(xiàn)以下兩個功能: p兩個任務(wù)或者中斷函數(shù)跟任務(wù)之間的同步功能,和事件標(biāo)志組是類似的。其實就是共享資源為 1 的時候。p多個共享資源的管理,就像上面機房上機的例子。 簡單說來,信號量有三個部分:u第一部分就是一個代幣容器(一個整形變量),記錄著可用的資源數(shù);u第二部分就是取用資源(P,嘗試的荷蘭語)的操作;u第三個就是還回資源(V, 增加的荷蘭語)的操作。當(dāng)資源數(shù)量為0時任何取用資源的操作都會被阻斷,直到資源數(shù)量增加。12.4.2 RTX 任務(wù)間信號量的實現(xiàn)指各個任務(wù)之間使用信號量實現(xiàn)任務(wù)的同步或者資源共享功能。運行過程描述如下: pTask1運行中調(diào)用os
32、_sem_wait獲取信號量資源,如果信號量沒有被Task2占用,Task1 將直接獲取資源。如果信號被 Task2 占用,Task1 將由運行態(tài)轉(zhuǎn)到掛起狀態(tài),等待資源可用。一旦獲取了資源并使用完畢后會通過函數(shù) os_sem_send 釋放掉資源pTask2運行中調(diào)用os_sem_wait獲取信號量資源,如果信號量沒有被任務(wù)Task2占用,Task1將直接獲取資源。如果信號被Task2 占用,Task1將由運行態(tài)轉(zhuǎn)到掛起狀態(tài),等待資源可以。一旦獲取了資源并使用完畢后會通過函數(shù) os_sem_send 釋放掉資源。運行條件:創(chuàng)建 2 個任務(wù) Task1 和 Task2,創(chuàng)建信號量可用資源為 1。
33、12.4.3 中斷方式信號量的實現(xiàn)指中斷函數(shù)和 RTX 任務(wù)之間使用信號量,主要是用于實現(xiàn)任務(wù)同步。p創(chuàng)建 1 個任務(wù) Task1 和一個串口接收中斷。p信號量的初始值為 0,串口中斷調(diào)用函數(shù) isr_sem_send 釋放信號量,任務(wù) Task1 調(diào)用函數(shù) os_sem_wait 獲取信號量資源。運行過程描述p任務(wù) Task1 運行過程中調(diào)用函數(shù) os_sem_wait,由于信號量的初始值是 0,沒有信號量資源可用,任務(wù) Task1 由運行態(tài)進入到掛起態(tài)。pTask1 掛起的情況下,串口接收到數(shù)據(jù)進入到了串口中斷服務(wù)程序,在串口中斷服務(wù)程序中調(diào)用函數(shù) isr_sem_send 釋放信號量資源
34、,信號量數(shù)值加 1,此時信號量計數(shù)值為 1,任務(wù) Task1 由掛起態(tài)進入到就緒態(tài),在調(diào)度器的作用下由就緒態(tài)又進入到運行態(tài),任務(wù) Task1 獲得信號量后,信號量數(shù)值減1,此時信號量計數(shù)值又變成了 0。p再次循環(huán)執(zhí)行時,任務(wù) Task1 調(diào)用函數(shù) os_sem_wait 由于沒有資源可用再次進入到掛起態(tài),等待串口釋放信號量資源,如此往復(fù)循環(huán)。注意以下四個問題p中斷函數(shù)的執(zhí)行時間越短越好,防止其它低于這個中斷優(yōu)先級的異常不能得到及時響應(yīng)。p實際應(yīng)用中,建議不要在中斷中實現(xiàn)消息處理,用戶可以在中斷服務(wù)程序里面發(fā)送消息通知任務(wù),在任務(wù)中實現(xiàn)消息處理,這樣可以有效的保證中斷服務(wù)程序的實時響應(yīng)。同時此任
35、務(wù)也需要設(shè)置為高優(yōu)先級,以便退出中斷函數(shù)后任務(wù)可以得到及時執(zhí)行。p中斷服務(wù)程序中一定要調(diào)用專用于中斷的信號量設(shè)置函數(shù) isr_sem_send。p在 RTX 操作系統(tǒng)中實現(xiàn)中斷函數(shù)和裸機編程是一樣的。12.4.4 信號量 API 函數(shù)使用如下4個函數(shù)可以實現(xiàn) RTX 的信號量: pos_sem_initos_sem_initpos_sem_sendos_sem_sendpisr_sem_sendisr_sem_sendpu os_sem_wait u os_sem_wait 函數(shù) os_sem_initvoid os_sem_init ( OS_ID semaphore, /* os_sem類
36、型變量類型變量 */ U16 token_count ); /* 信號量初始值信號量初始值 */ 函數(shù)描述函數(shù)描述:用于信號量的初始化并設(shè)置初始值 p第1個參數(shù)填寫數(shù)據(jù)類型為OS_SEM的變量,同時也作為ID標(biāo)識;p第2個參數(shù)是信號量初始值,也就是可用資源個數(shù)。使用舉例使用舉例: #include #include OS_SEM semaphore; OS_SEM semaphore; static void AppObjCreate(void)static void AppObjCreate(void) /* 創(chuàng)建信號量計數(shù)值是創(chuàng)建信號量計數(shù)值是0, 用于任務(wù)同步用于任務(wù)同步 */os_se
37、m_init(&semaphore, 0);os_sem_init(&semaphore, 0); 為什么變量semaphore 前面加一個取地址符&?函數(shù) os_sem_sendOS_RESULT os_sem_send ( OS_ID semaphore ); /* OS_SEM類型變量類型變量 */ 函數(shù)描述:函數(shù)描述:用于釋放信號量,調(diào)用后信號量計數(shù)值加 1。 p第 1 個參數(shù)參數(shù)填寫數(shù)據(jù)類型為 OS_SEM 的變量,同時也作為 ID 標(biāo)識。p返回值永遠是 OS_R_OK。p使用此函數(shù)前一定要調(diào)用函數(shù) os_sem_init 進行初始化。使用舉例:使用舉例: #include #inc
38、lude OS_SEM semaphore; OS_SEM semaphore; _task void task1 (void) . _task void task1 (void) . os_sem_init (&semaphore, 0); os_sem_init (&semaphore, 0); os_sem_send (&semaphore); . os_sem_send (&semaphore); . 函數(shù) isr_sem_sendvoid isr_sem_send ( OS_ID semaphore ); /* OS_SEM類型變量類型變量 */ 函數(shù)描述:函數(shù)描述:用于釋放信號量,
39、調(diào)用后信號量計數(shù)值加 1。 p第 1 個參數(shù)參數(shù)填寫數(shù)據(jù)類型為 OS_SEM 的變量,同時也作為 ID 標(biāo)識。p使用此函數(shù)前一定要調(diào)用函數(shù) os_sem_init 進行初始化。使用舉例:使用舉例: #include #include OS_SEM semaphore; OS_SEM semaphore; void EXTI0_IRQHandler (void) . void EXTI0_IRQHandler (void) . isr_sem_send (&semaphore1); . isr_sem_send (&semaphore1); . 函數(shù) os_sem_waitOS_RESULT o
40、s_sem_wait ( OS_ID semaphore, /* OS_SEM類型變量類型變量 */ U16 timeout ); /* 超時時間設(shè)置超時時間設(shè)置 */ 函數(shù)描述:用于獲取信號量,如果當(dāng)前的信號量計數(shù)值大于函數(shù)描述:用于獲取信號量,如果當(dāng)前的信號量計數(shù)值大于 0,那么調(diào)用函數(shù),那么調(diào)用函數(shù) os_sem_wait 后可以成功獲取信號量,并將信號量的計數(shù)值減后可以成功獲取信號量,并將信號量的計數(shù)值減 1。如果信號。如果信號量計數(shù)值等于量計數(shù)值等于 0,調(diào)用此函數(shù)的任務(wù)將由運行態(tài)轉(zhuǎn)到掛起態(tài),等待信號量資源,調(diào)用此函數(shù)的任務(wù)將由運行態(tài)轉(zhuǎn)到掛起態(tài),等待信號量資源可用,也就是等待信號量計
41、數(shù)值大于可用,也就是等待信號量計數(shù)值大于 0。 p第 1 個參數(shù)參數(shù)填寫數(shù)據(jù)類型為 OS_SEM 的變量,同時也作為 ID 標(biāo)識。p第 2 個參數(shù)表示設(shè)置的等待時間,范圍 0-0 xFFFF,當(dāng)參數(shù)設(shè)置為 0-0 xFFFE 時,表示等待這么多個時鐘節(jié)拍,參數(shù)設(shè)置為 0 xFFFF 時表示無限等待直到有信號量資源可用。p函數(shù)返回 OS_R_SEM 表示函數(shù)設(shè)置的超時時間范圍內(nèi)收到信號量可用資源。函數(shù)返回 OS_R_TMO 表示超時。函數(shù)返回 OS_R_OK 表示無需等待,立即獲得可用信號量資源。使用舉例#include OS_SEM semaphore;_task void AppTaskMs
42、gPro(void) OS_RESULT xResult;const uint16_t usMaxBlockTime = 200; /* 延遲周期 */while (1)xResult = os_sem_wait(&semaphore, usMaxBlockTime);switch (xResult)case OS_R_OK:printf(無需等待接受到信號量同步信號rn);break;case OS_R_SEM:printf(信號量不可用,內(nèi)收到信號量同步信號rn);break;case OS_R_TMO: /* 超時 */bsp_LedToggle(1);bsp_LedToggle(4);
43、 break; default:break;使用舉例os_semsemaphore;os_semsemaphore;_taskvoid task1(_taskvoid task1(voidvoid) ) os_sem_init(semaphore, 0);os_sem_init(semaphore, 0);whilewhile (1) (1) Function1();Function1();os_sem_send(semaphore);os_sem_send(semaphore); _taskvoid task2(_taskvoid task2(voidvoid) ) whilewhile (
44、1) (1) os_sem_wait(semaphore, 0 xFFFF);os_sem_wait(semaphore, 0 xFFFF);Function2();Function2(); 信號量的作用就是確保在每一次調(diào)用Function2之前,F(xiàn)unction1都有一次完整的調(diào)用。使用舉例os_semArrived1, Arrived2;os_semArrived1, Arrived2;_task void task1(_task void task1(voidvoid) ) os_sem_init(Arrived1, 0);os_sem_init(Arrived1, 0);os_sem_
45、init(Arrived2, 0);os_sem_init(Arrived2, 0);whilewhile (1) (1) FunctionA1();FunctionA1();os_sem_send(Arrived1);os_sem_send(Arrived1);os_sem_wait(Arrived2, 0 xFFFF);os_sem_wait(Arrived2, 0 xFFFF);FunctionA2();FunctionA2(); _task void task2(_task void task2(voidvoid) ) whilewhile (1) (1) FunctionB1();F
46、unctionB1();os_sem_send(Arrived2);os_sem_send(Arrived2);os_sem_wait(Arrived1, 0 xFFFF);os_sem_wait(Arrived1, 0 xFFFF);FunctionB2();FunctionB2(); 目的是讓FunctionA1,functionB1都完成以后,再執(zhí)行FunctionA2和FunctionB2使用舉例os_semMultiplex;os_semMultiplex;_taskvoid task(_taskvoid task(voidvoid) ) os_sem_init(Multiplex,
47、 5);os_sem_init(Multiplex, 5);whilewhile (1) (1) os_sem_wait(Multiplex, 0 xFFFF);os_sem_wait(Multiplex, 0 xFFFF);Function();Function();os_sem_send(Multiplex);os_sem_send(Multiplex); 保證最多只有五個進程能夠同時調(diào)用Function()。12.4.5 實驗例程說明(任務(wù)間通信)目的: 學(xué)習(xí) RTX 的信號量實驗內(nèi)容: 1. K1 按鍵按下,串口打印。2. K2 鍵按下,直接發(fā)送信號量同步信號給任務(wù) AppTaskMs
48、gPro。任務(wù) AppTaskMsgPro 接收到消息后進行消息處理。3. 各個任務(wù)實現(xiàn)的功能如下: AppTaskUserIF 任務(wù):按鍵消息處理。 AppTaskLED 任務(wù):LED 閃爍。 AppTaskMsgPro 任務(wù) :消息處理,等待任務(wù) AppTaskUserIF 發(fā)來的信號量同步信號。 AppTaskStart 任務(wù) :啟動任務(wù),也是最高優(yōu)先級任務(wù),這里實現(xiàn)按鍵掃描。 RTX 配置RTX 任務(wù)調(diào)試信息12.4 .5例程說明(中斷方式通信)目的: 學(xué)習(xí) RTX 的信號量(中斷方式)實驗內(nèi)容: 1. K1 按鍵按下,串口打印。2. K2 鍵按下,啟動單次定時器中斷,50ms 后在定
49、時器中斷給任務(wù) AppTaskMsgPro 發(fā)送信號量同步信號。任務(wù) AppTaskMsgPro 接收到消息后進行消息處理。 3. 各個任務(wù)實現(xiàn)的功能如下: AppTaskUserIF 任務(wù) :按鍵消息處理。 AppTaskLED 任務(wù):LED 閃爍。 AppTaskMsgPro 任務(wù):消息處理,等待定時器中斷發(fā)來的信號量同步信號。 AppTaskStart 任務(wù):啟動任務(wù),最高優(yōu)先級任務(wù),實現(xiàn)按鍵掃描。定時器中斷回調(diào)函數(shù)中發(fā)送信號量同步信號:定時器中斷的初始化和中斷函數(shù)在 bsp_timer.c 文件中實現(xiàn), RTX 的信號量函數(shù)在中斷服務(wù)程序中使用:staticstatic voidvoi
50、d TIM_CallBack1( TIM_CallBack1(voidvoid) ) / /* * 發(fā)送信號量同步信號發(fā)送信號量同步信號 */isr_sem_send(&semaphore);isr_sem_send(&semaphore); RTX 任務(wù)調(diào)試信息*嵌入式系統(tǒng)原理與應(yīng)用56課堂作業(yè)建立兩個任務(wù) AppTaskPost 和 AppTaskPend。按鍵按下時任務(wù) AppTaskPost 當(dāng)每隔 200ms 向等待的任務(wù) AppTaskPend 發(fā)送一個任務(wù)信號量,并通過串口打印出任務(wù)信號量的值。當(dāng)按鍵松開時任務(wù)AppTaskPend每隔 200ms請求一個任務(wù)信號量,紅色 LED
51、 閃爍表示請求到一個任務(wù)信號量,任務(wù)信號量的值減小,任務(wù)AppTaskPost通過串口打印任務(wù)信號量的值。當(dāng)任務(wù)信號量的值減小到 0時,任務(wù)AppTaskPend請求不到任務(wù)信號量,紅色LED不再閃爍。12.5 互斥信號量p互斥信號量就是信號量的一種特殊形式,也就是信號量初始值為 1 的情況。有些 RTOS 中也將信號量初始值設(shè)置為 1 的情況稱之為二值信號量。為什么叫二值信號量呢?因為信號量資源被獲取了,信號量值就是 0,信號量資源被釋放,信號量值就是 1,把這種只有 0 和 1 兩種情況的信號量稱之為二值信號量。p互斥信號量的主要作用就是對資源實現(xiàn)互斥訪問。運行條件p讓兩個任務(wù) Task1
52、 和 Task2 都有運行串口打印 printf,對函數(shù) printf 通過二值信號量實現(xiàn)互斥訪問。如果不對函數(shù) printf 進行互斥訪問,串口打印容易出現(xiàn)亂碼。p用信號量實現(xiàn)二值信號量只需將信號量的初始值設(shè)置為 1 即可。創(chuàng)建二值信號量創(chuàng)建二值信號量OS_SEM semaphore;OS_SEM semaphore;staticstatic voidvoid AppObjCreate( AppObjCreate(voidvoid) ) / /* * 創(chuàng)建二值信號量,實現(xiàn)對互斥資源的獨享創(chuàng)建二值信號量,實現(xiàn)對互斥資源的獨享 */os_sem_init(semaphore, 0);os_sem
53、_init(semaphore, 0); 二值信號量實現(xiàn)互斥訪問_task _task voidvoid AppTaskLED( AppTaskLED(voidvoid) ) constconst uint16_t usFrequency = 1000; uint16_t usFrequency = 1000; os_itv_set(usFrequency);os_itv_set(usFrequency);whilewhile (1) (1) / /* * 永久等待互斥資源可用永久等待互斥資源可用 */os_sem_wait(&semaphore, 0 xffff);os_sem_wait(&
54、semaphore, 0 xffff);printf(printf( 任務(wù)任務(wù)AppTaskLED正在運行正在運行rn););/ /* * 釋放互斥資源釋放互斥資源 */os_sem_send(&semaphore);os_sem_send(&semaphore);/ /* * os_itv_wait os_itv_wait是絕對延遲,是絕對延遲,os_dly_wait是相對延遲。是相對延遲。*/os_itv_wait();os_itv_wait(); _task void AppTaskMsgPro(void)const uint16_t usFrequency = 1000; /* 延遲周
55、期 */os_itv_set(usFrequency); /* 設(shè)置延遲周期 */while (1)/* 永久等待互斥資源可以 */printf(任務(wù)AppTaskMsgPro正在運行rn);/* 釋放互斥資源 */os_sem_send(&semaphore);/* os_itv_wait是絕對延遲,os_dly_wait是相對延遲。*/os_itv_wait();12.5.2 優(yōu)先級翻轉(zhuǎn)問題 p3 個任務(wù) Task1,Task2 和 Task3,優(yōu)先級分別為 3,2,1。pTask1和Task3采用二值信號實現(xiàn)互斥訪問串口打印printf。p起初 Task3 通過二值信號量正在調(diào)用 pri
56、ntf,被任務(wù) Task1 搶占,開始執(zhí)行任務(wù) Task1,也就是圖的起始位置。優(yōu)先級翻轉(zhuǎn)問題pTask1 運行的過程需要調(diào)用printf,發(fā)現(xiàn)Task3 正在調(diào)用,Task1 會被掛起,等待 Task3 釋放函數(shù) printf。p在調(diào)度器的作用下,Task3 得到運行,Task3 運行的過程中,由于Task2 就緒,搶占了 Task3 的運行。優(yōu)先級翻轉(zhuǎn)問題就出在這里了,從任務(wù)執(zhí)行的現(xiàn)象上看,高優(yōu)先級任務(wù) Task1 需要等待低優(yōu)先級任務(wù) Task2 執(zhí)行完畢才有機會得到執(zhí)行,這個與搶占式調(diào)度正好反了。pTask2 執(zhí)行完畢后,Task3 恢復(fù)執(zhí)行,Task3 釋放互斥資源后,Task1得到
57、互斥資源,從而可以繼續(xù)執(zhí)行。優(yōu)先級翻轉(zhuǎn)問題(2)考慮三個進程,T1,T2,T3。T3的優(yōu)先度大于T2,T2的優(yōu)先度大于T1,采用的調(diào)度算法算法是優(yōu)先度高的可以打斷優(yōu)先度低的進程,臨界區(qū)等待可以阻斷高優(yōu)先度的進程。問題:進程3被什么阻斷了?答:進程3因為要求資源R的臨界區(qū),而此時資源R臨界區(qū)被進程1占用。所以,進程3實際上是被進程2阻斷了,低優(yōu)先度的進程阻斷了高優(yōu)先度的進程,這就是優(yōu)先度翻轉(zhuǎn)問題。優(yōu)先度繼承優(yōu)先度繼承的意思是,將臨界區(qū)內(nèi)的進程的優(yōu)先度提高為該進程阻斷的進程里的優(yōu)先級最高進程的優(yōu)先級。當(dāng)進程3要求進入R臨界區(qū)執(zhí)行時,因為被進程1阻斷了,進程1此時在臨界區(qū)內(nèi),而且它阻斷的是進程3,那
58、么此時它的優(yōu)先度就會提升為進程3的優(yōu)先度,也就是說繼承了進程3的優(yōu)先度。當(dāng)退出臨界執(zhí)行區(qū)后,進程1優(yōu)先度又會降為原先的優(yōu)先度。這就很好地解決了高優(yōu)先度進程被無條件阻斷的問題死鎖優(yōu)先度繼承的意思是,將臨界區(qū)內(nèi)的進程的優(yōu)先度提高為該進程阻斷的進程里的優(yōu)先級最高進程的優(yōu)先級。T1被創(chuàng)造T1要求進入資源A臨界區(qū)T2被創(chuàng)建T2要講求進入資源B臨界區(qū)T2要求進入資源A臨界區(qū)T1要求進入資源B臨界區(qū)當(dāng)進程2要求資源A臨界區(qū)時,因為進程1正在資源A臨界區(qū)內(nèi),所以其優(yōu)先級繼承了進程2的優(yōu)先級,但進程1執(zhí)行了一段時間后又要求進入資源B臨界區(qū)以完成當(dāng)前任務(wù),退出臨界區(qū)A。兩個進程此時都在等待對方退出臨界區(qū),而自己卻
59、不會主動退出自己占有的臨界區(qū),所以死鎖。優(yōu)先度天花板具體說來就是,為每個資源定義優(yōu)先度天花板,資源的優(yōu)先度天花板是所有可能要互斥使用該資源的進程的最高優(yōu)先度。這種策略只允許滿足一個條件的進程去進入臨界區(qū),該進程的優(yōu)先度要大于(等于不足夠)所有其他進程占用的資源的優(yōu)先度天花板的最高優(yōu)先度。同時保留優(yōu)先度繼承策略。當(dāng)進程2想進入資源B臨界區(qū)時,被阻斷了,因為它優(yōu)先度并不大于其他進程(進程1)占用資源(A)的優(yōu)先度天花板(進程2的優(yōu)先度),被進程1阻斷,與此同時,進程1的優(yōu)先度提升為進程2的優(yōu)先度,繼續(xù)執(zhí)行,直到退出兩個臨界區(qū)。任務(wù)任務(wù)A申請共享資源申請共享資源S S是否被占用是否被占用任務(wù)任務(wù)A獲
60、得共享資源獲得共享資源S S被任務(wù)被任務(wù)B占用,任務(wù)占用,任務(wù)A被掛起被掛起 Priority(B)Priority( A)?提升提升B的優(yōu)先級到的優(yōu)先級到PIP B釋放資源釋放資源s并恢復(fù)原來的優(yōu)先級并恢復(fù)原來的優(yōu)先級 等待隊列等待隊列中優(yōu)先級最高的任務(wù)獲得中優(yōu)先級最高的任務(wù)獲得s任務(wù)任務(wù)A繼續(xù)執(zhí)行繼續(xù)執(zhí)行NYNY優(yōu)先級繼承的主要思想當(dāng)高優(yōu)先級任務(wù)因申請某共享資源失敗被阻塞時,把當(dāng)前擁有該資源的、且優(yōu)先級較低的任務(wù)的優(yōu)先級提升,提升的高度等于這個高優(yōu)先級任務(wù)的優(yōu)先級(可以指定一個PIP) 12.5.3 RTX 互斥信號量的實現(xiàn)RTX 互斥信號量是怎么實現(xiàn)的呢?其實相比二值信號量就是解決了一下
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024版?zhèn)€人存款協(xié)議范本詳解版B版
- 2025年度三人餐飲店裝修及設(shè)備采購合同3篇
- 2025年度健康體檢與健康管理服務(wù)合同2篇
- 2024輸電線路施工安全風(fēng)險評價及管控協(xié)議3篇
- 2024版教練職務(wù)錄用合同示范
- 2025年度廢玻璃回收與深加工協(xié)議3篇
- 2024羅琳與哈利波特版權(quán)分割離婚協(xié)議書2篇
- 2025年倉儲設(shè)施租賃與環(huán)保評估合同3篇
- 酒店聘用退休服務(wù)員合同模板
- 投資型二手房買賣合同協(xié)議書
- 2022閥門制造作業(yè)指導(dǎo)書
- 科技創(chuàng)新社團活動教案課程
- 建筑結(jié)構(gòu)加固工程施工質(zhì)量驗收規(guī)范表格
- 部編版語文六年級上冊作文總復(fù)習(xí)課件
- SHS5230三星指紋鎖中文說明書
- 無水氯化鈣MSDS資料
- 專利產(chǎn)品“修理”與“再造”的區(qū)分
- 氨堿法純堿生產(chǎn)工藝概述
- 健康管理專業(yè)建設(shè)規(guī)劃
- 指揮中心大廳及機房裝修施工組織方案
- 真心英雄合唱歌詞
評論
0/150
提交評論