实验七 信号量与同步
信号量结构初始化
新建 lab7/src/include/prt_sem_external.h 头文件
1#ifndef PRT_SEM_EXTERNAL_H
2#define PRT_SEM_EXTERNAL_H
3
4#include "prt_sem.h"
5#include "prt_task_external.h"
6#if defined(OS_OPTION_POSIX)
7#include "bits/semaphore_types.h"
8#endif
9
10#define OS_SEM_UNUSED 0
11#define OS_SEM_USED 1
12
13#define SEM_PROTOCOL_PRIO_INHERIT 1
14#define SEM_TYPE_BIT_WIDTH 0x4U
15#define SEM_PROTOCOL_BIT_WIDTH 0x8U
16
17#define OS_SEM_WITH_LOCK_FLAG 1
18#define OS_SEM_WITHOUT_LOCK_FLAG 0
19
20#define MAX_POSIX_SEMAPHORE_NAME_LEN 31
21
22#define GET_SEM_LIST(ptr) LIST_COMPONENT(ptr, struct TagSemCb, semList)
23#define GET_SEM(semid) (((struct TagSemCb *)g_allSem) + (semid))
24#define GET_SEM_TSK(semid) (((SEM_TSK_S *)g_semTsk) + (semid))
25#define GET_TSK_SEM(tskid) (((TSK_SEM_S *)g_tskSem) + (tskid))
26#define GET_SEM_TYPE(semType) (U32)((semType) & ((1U << SEM_TYPE_BIT_WIDTH) - 1))
27#define GET_MUTEX_TYPE(semType) (U32)(((semType) >> SEM_TYPE_BIT_WIDTH) & ((1U << SEM_TYPE_BIT_WIDTH) - 1))
28#define GET_SEM_PROTOCOL(semType) (U32)((semType) >> SEM_PROTOCOL_BIT_WIDTH)
29
30struct TagSemCb {
31 /* 是否使用 OS_SEM_UNUSED/OS_SEM_USED */
32 U16 semStat;
33 /* 核内信号量索引号 */
34 U16 semId;
35#if defined(OS_OPTION_SEM_RECUR_PV)
36 /* 二进制互斥信号量递归P计数,计数型信号量和二进制同步模式信号量无效 */
37 U32 recurCount;
38#endif
39 /* 当该信号量已用时,其信号量计数 */
40 U32 semCount;
41 /* 挂接阻塞于该信号量的任务 */
42 struct TagListObject semList;
43 /* 挂接任务持有的互斥信号量,计数型信号量信号量无效 */
44 struct TagListObject semBList;
45
46 /* Pend到该信号量的线程ID */
47 U32 semOwner;
48 /* 信号量唤醒阻塞任务的方式 */
49 enum SemMode semMode;
50 /* 信号量,计数型或二进制 */
51 U32 semType;
52#if defined(OS_OPTION_POSIX)
53 /* 信号量名称 */
54 char name[MAX_POSIX_SEMAPHORE_NAME_LEN + 1]; // + \0
55 /* sem_open 句柄 */
56 sem_t handle;
57#endif
58};
59
60/* 模块间全局变量声明 */
61extern U16 g_maxSem;
62
63/* 指向核内信号量控制块 */
64extern struct TagSemCb *g_allSem;
65
66extern U32 OsSemCreate(U32 count, U32 semType, enum SemMode semMode, SemHandle *semHandle, U32 cookie);
67extern bool OsSemBusy(SemHandle semHandle);
68
69#endif /* PRT_SEM_EXTERNAL_H */
新建 src/kernel/sem/prt_sem_init.c 文件。
1#include "prt_sem_external.h"
2#include "os_attr_armv8_external.h"
3#include "os_cpu_armv8_external.h"
4
5OS_SEC_BSS struct TagListObject g_unusedSemList;
6OS_SEC_BSS struct TagSemCb *g_allSem;
7
8extern void *OsMemAllocAlign(U32 mid, U8 ptNo, U32 size, U8 alignPow);
9/*
10* 描述:信号量初始化
11*/
12OS_SEC_L4_TEXT U32 OsSemInit(void)
13{
14 struct TagSemCb *semNode = NULL;
15 U32 idx;
16 U32 ret = OS_OK;
17
18 g_allSem = (struct TagSemCb *)OsMemAllocAlign((U32)OS_MID_SEM,
19 0,
20 4096,
21 OS_SEM_ADDR_ALLOC_ALIGN);
22
23 if (g_allSem == NULL) {
24 return OS_ERRNO_SEM_NO_MEMORY;
25 }
26
27 g_maxSem = 4096 / sizeof(struct TagSemCb);
28
29 char *cg_allSem = (char *)g_allSem;
30 for(int i = 0; i < 4096; i++)
31 cg_allSem[i] = 0;
32
33 INIT_LIST_OBJECT(&g_unusedSemList);
34 for (idx = 0; idx < g_maxSem; idx++) {
35 semNode = ((struct TagSemCb *)g_allSem) + idx; //指针操作
36 semNode->semId = (U16)idx;
37 ListTailAdd(&semNode->semList, &g_unusedSemList);
38 }
39
40 return ret;
41}
42
43/*
44* 描述:创建一个信号量
45*/
46OS_SEC_L4_TEXT U32 OsSemCreate(U32 count, U32 semType, enum SemMode semMode,
47 SemHandle *semHandle, U32 cookie)
48{
49 uintptr_t intSave;
50 struct TagSemCb *semCreated = NULL;
51 struct TagListObject *unusedSem = NULL;
52 (void)cookie;
53
54 if (semHandle == NULL) {
55 return OS_ERRNO_SEM_PTR_NULL;
56 }
57
58 intSave = OsIntLock();
59
60 if (ListEmpty(&g_unusedSemList)) {
61 OsIntRestore(intSave);
62 return OS_ERRNO_SEM_ALL_BUSY;
63 }
64
65 /* 在空闲链表中取走一个控制节点 */
66 unusedSem = OS_LIST_FIRST(&(g_unusedSemList));
67 ListDelete(unusedSem);
68
69 /* 获取到空闲节点对应的信号量控制块,并开始填充控制块 */
70 semCreated = (GET_SEM_LIST(unusedSem));
71 semCreated->semCount = count;
72 semCreated->semStat = OS_SEM_USED;
73 semCreated->semMode = semMode;
74 semCreated->semType = semType;
75 semCreated->semOwner = OS_INVALID_OWNER_ID;
76 if (GET_SEM_TYPE(semType) == SEM_TYPE_BIN) {
77 INIT_LIST_OBJECT(&semCreated->semBList);
78#if defined(OS_OPTION_SEM_RECUR_PV)
79 if (GET_MUTEX_TYPE(semType) == PTHREAD_MUTEX_RECURSIVE) {
80 semCreated->recurCount = 0;
81 }
82#endif
83 }
84
85 INIT_LIST_OBJECT(&semCreated->semList);
86 *semHandle = (SemHandle)semCreated->semId;
87
88 OsIntRestore(intSave);
89 return OS_OK;
90}
91
92/*
93* 描述:创建一个信号量
94*/
95OS_SEC_L4_TEXT U32 PRT_SemCreate(U32 count, SemHandle *semHandle)
96{
97 U32 ret;
98
99 if (count > OS_SEM_COUNT_MAX) {
100 return OS_ERRNO_SEM_OVERFLOW;
101 }
102
103 ret = OsSemCreate(count, SEM_TYPE_COUNT, SEM_MODE_FIFO, semHandle, (U32)(uintptr_t)semHandle);
104 return ret;
105}
在 src/bsp/os_cpu_armv8_external.h 加入 定义
1#define OS_SEM_ADDR_ALLOC_ALIGN 2U //按2的幂对齐,即2^2=4字节
新建 src/kernel/sem/prt_sem.c 文件。
1#include "prt_sem_external.h"
2#include "prt_asm_cpu_external.h"
3#include "os_attr_armv8_external.h"
4#include "os_cpu_armv8_external.h"
5
6/* 核内信号量最大个数 */
7OS_SEC_BSS U16 g_maxSem;
8
9
10OS_SEC_ALW_INLINE INLINE U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle)
11{
12 (void)semHandle;
13 /* 检查信号量控制块是否UNUSED,排除大部分错误场景 */
14 if (semPosted->semStat == OS_SEM_UNUSED) {
15 return OS_ERRNO_SEM_INVALID;
16 }
17
18 /* post计数型信号量的错误场景, 释放计数型信号量且信号量计数大于最大计数 */
19 if ((semPosted)->semCount >= OS_SEM_COUNT_MAX) {
20 return OS_ERRNO_SEM_OVERFLOW;
21 }
22
23 return OS_OK;
24}
25
26
27/*
28* 描述:把当前运行任务挂接到信号量链表上
29*/
30OS_SEC_L0_TEXT void OsSemPendListPut(struct TagSemCb *semPended, U32 timeOut)
31{
32 struct TagTskCb *curTskCb = NULL;
33 struct TagTskCb *runTsk = RUNNING_TASK;
34 struct TagListObject *pendObj = &runTsk->pendList;
35
36 OsTskReadyDel((struct TagTskCb *)runTsk);
37
38 runTsk->taskSem = (void *)semPended;
39
40 TSK_STATUS_SET(runTsk, OS_TSK_PEND);
41 /* 根据唤醒方式挂接此链表,同优先级再按FIFO子顺序插入 */
42 if (semPended->semMode == SEM_MODE_PRIOR) {
43 LIST_FOR_EACH(curTskCb, &semPended->semList, struct TagTskCb, pendList) {
44 if (curTskCb->priority > runTsk->priority) {
45 ListTailAdd(pendObj, &curTskCb->pendList);
46 // goto TIMER_ADD;
47 return;
48 }
49 }
50 }
51 /* 如果到这里,说明是FIFO方式;或者是优先级方式且挂接首个节点或者挂接尾节点 */
52 ListTailAdd(pendObj, &semPended->semList);
53
54}
55
56/*
57* 描述:从非空信号量链表上摘首个任务放入到ready队列
58*/
59OS_SEC_L0_TEXT struct TagTskCb *OsSemPendListGet(struct TagSemCb *semPended)
60{
61 struct TagTskCb *taskCb = GET_TCB_PEND(LIST_FIRST(&(semPended->semList)));
62
63 ListDelete(LIST_FIRST(&(semPended->semList)));
64 /* 如果阻塞的任务属于定时等待的任务时候,去掉其定时等待标志位,并将其从去除 */
65 if (TSK_STATUS_TST(taskCb, OS_TSK_TIMEOUT)) {
66 OS_TSK_DELAY_LOCKED_DETACH(taskCb);
67 }
68
69 /* 必须先去除 OS_TSK_TIMEOUT 态,再入队[睡眠时是先出ready队,再置OS_TSK_TIMEOUT态] */
70 TSK_STATUS_CLEAR(taskCb, OS_TSK_TIMEOUT | OS_TSK_PEND);
71 taskCb->taskSem = NULL;
72 /* 如果去除信号量阻塞位后,该任务不处于阻塞态则将该任务挂入就绪队列并触发任务调度 */
73 if (!TSK_STATUS_TST(taskCb, OS_TSK_SUSPEND)) {
74 OsTskReadyAddBgd(taskCb);
75 }
76
77 return taskCb;
78}
79
80OS_SEC_L0_TEXT U32 OsSemPendParaCheck(U32 timeout)
81{
82 if (timeout == 0) {
83 return OS_ERRNO_SEM_UNAVAILABLE;
84 }
85
86 if (OS_TASK_LOCK_DATA != 0) {
87 return OS_ERRNO_SEM_PEND_IN_LOCK;
88 }
89 return OS_OK;
90}
91
92OS_SEC_L0_TEXT bool OsSemPendNotNeedSche(struct TagSemCb *semPended, struct TagTskCb *runTsk)
93{
94 if (semPended->semCount > 0) {
95 semPended->semCount--;
96 semPended->semOwner = runTsk->taskPid;
97
98 return TRUE;
99 }
100 return FALSE;
101}
102
103/*
104* 描述:指定信号量的P操作
105*/
106OS_SEC_L0_TEXT U32 PRT_SemPend(SemHandle semHandle, U32 timeout)
107{
108 uintptr_t intSave;
109 U32 ret;
110 struct TagTskCb *runTsk = NULL;
111 struct TagSemCb *semPended = NULL;
112
113 if (semHandle >= (SemHandle)g_maxSem) {
114 return OS_ERRNO_SEM_INVALID;
115 }
116
117 semPended = GET_SEM(semHandle);
118
119 intSave = OsIntLock();
120 if (semPended->semStat == OS_SEM_UNUSED) {
121 OsIntRestore(intSave);
122 return OS_ERRNO_SEM_INVALID;
123 }
124
125 if (OS_INT_ACTIVE) {
126 OsIntRestore(intSave);
127 return OS_ERRNO_SEM_PEND_INTERR;
128 }
129
130 runTsk = (struct TagTskCb *)RUNNING_TASK;
131
132 if (OsSemPendNotNeedSche(semPended, runTsk) == TRUE) {
133 OsIntRestore(intSave);
134 return OS_OK;
135 }
136
137 ret = OsSemPendParaCheck(timeout);
138 if (ret != OS_OK) {
139 OsIntRestore(intSave);
140 return ret;
141 }
142 /* 把当前任务挂接在信号量链表上 */
143 OsSemPendListPut(semPended, timeout);
144 if (timeout != OS_WAIT_FOREVER) {
145 OsIntRestore(intSave);
146 return OS_ERRNO_SEM_FUNC_NOT_SUPPORT;
147 } else {
148 /* 恢复ps的快速切换 */
149 OsTskScheduleFastPs(intSave);
150
151 }
152
153 OsIntRestore(intSave);
154 return OS_OK;
155}
156
157OS_SEC_ALW_INLINE INLINE void OsSemPostSchePre(struct TagSemCb *semPosted)
158{
159 struct TagTskCb *resumedTask = NULL;
160
161 resumedTask = OsSemPendListGet(semPosted);
162 semPosted->semOwner = resumedTask->taskPid;
163}
164
165/*
166* 描述:判断信号量post是否有效
167* 备注:以下情况表示post无效,返回TRUE: post互斥二进制信号量,若该信号量被嵌套pend或者已处于空闲状态
168*/
169OS_SEC_ALW_INLINE INLINE bool OsSemPostIsInvalid(struct TagSemCb *semPosted)
170{
171 if (GET_SEM_TYPE(semPosted->semType) == SEM_TYPE_BIN) {
172 /* 释放互斥二进制信号量且信号量已处于空闲状态 */
173 if ((semPosted)->semCount == OS_SEM_FULL) {
174 return TRUE;
175 }
176 }
177 return FALSE;
178}
179
180/*
181* 描述:指定信号量的V操作
182*/
183OS_SEC_L0_TEXT U32 PRT_SemPost(SemHandle semHandle)
184{
185 U32 ret;
186 uintptr_t intSave;
187 struct TagSemCb *semPosted = NULL;
188
189 if (semHandle >= (SemHandle)g_maxSem) {
190 return OS_ERRNO_SEM_INVALID;
191 }
192
193 semPosted = GET_SEM(semHandle);
194 intSave = OsIntLock();
195
196 ret = OsSemPostErrorCheck(semPosted, semHandle);
197 if (ret != OS_OK) {
198 OsIntRestore(intSave);
199 return ret;
200 }
201
202 /* 信号量post无效,不需要调度 */
203 if (OsSemPostIsInvalid(semPosted) == TRUE) {
204 OsIntRestore(intSave);
205 return OS_OK;
206 }
207
208 /* 如果有任务阻塞在信号量上,就激活信号量阻塞队列上的首个任务 */
209 if (!ListEmpty(&semPosted->semList)) {
210 OsSemPostSchePre(semPosted);
211 /* 相当于快速切换+中断恢复 */
212 OsTskScheduleFastPs(intSave);
213 } else {
214 semPosted->semCount++;
215 semPosted->semOwner = OS_INVALID_OWNER_ID;
216
217 }
218
219 OsIntRestore(intSave);
220 return OS_OK;
221}
src/include/prt_task_external.h 加入 OsTskReadyAddBgd()
1OS_SEC_ALW_INLINE INLINE void OsTskReadyAddBgd(struct TagTskCb *task)
2{
3 OsTskReadyAdd(task);
4}
src/kernel/task/prt_task.c 加入 OsTskScheduleFastPs()
1// src/core/kernel/task/prt_amp_task.c
2/*
3* 描述:如果快速切换后只有中断恢复,使用此接口
4*/
5OS_SEC_TEXT void OsTskScheduleFastPs(uintptr_t intSave)
6{
7 /* Find the highest task */
8 OsTskHighestSet();
9
10 /* In case that running is not highest then reschedule */
11 if ((g_highestTask != RUNNING_TASK) && (g_uniTaskLock == 0)) {
12 UNI_FLAG |= OS_FLG_TSK_REQ;
13
14 /* only if there is not HWI or TICK the trap */
15 if (OS_INT_INACTIVE) {
16 OsTaskTrapFastPs(intSave);
17 }
18 }
19}
src/bsp/os_cpu_armv8_external.h 加入 OsTaskTrapFastPs()
1OS_SEC_ALW_INLINE INLINE void OsTaskTrapFastPs(uintptr_t intSave)
2{
3 (void)intSave;
4 OsTaskTrap();
5}
加入 src/include/prt_sem.h [下载],该头文件主要是信号量相关的函数声明和宏定义。
提示
将新增文件加入构建系统
验证
1#include "prt_typedef.h"
2#include "prt_tick.h"
3#include "prt_task.h"
4#include "prt_sem.h"
5
6extern U32 PRT_Printf(const char *format, ...);
7extern void PRT_UartInit(void);
8extern U32 OsActivate(void);
9extern U32 OsTskInit(void);
10extern U32 OsSemInit(void);
11
12
13static SemHandle sem_sync;
14
15
16void Test1TaskEntry()
17{
18 PRT_Printf("task 1 run ...\n");
19 PRT_SemPost(sem_sync);
20 U32 cnt = 5;
21 while (cnt > 0) {
22 // PRT_TaskDelay(200);
23 PRT_Printf("task 1 run ...\n");
24 cnt--;
25 }
26}
27
28void Test2TaskEntry()
29{
30 PRT_Printf("task 2 run ...\n");
31
32 PRT_SemPend(sem_sync, OS_WAIT_FOREVER);
33 U32 cnt = 5;
34 while (cnt > 0) {
35 // PRT_TaskDelay(100);
36 PRT_Printf("task 2 run ...\n");
37 cnt--;
38 }
39}
40
41S32 main(void)
42{
43 // 任务模块初始化
44 OsTskInit();
45 OsSemInit(); // 参见demos/ascend310b/config/prt_config.c 系统初始化注册表
46
47 PRT_UartInit();
48
49 PRT_Printf(" _ _ _____ _ _ _ _ _ _ _ _ \n");
50 PRT_Printf(" _ __ ___ (_)_ __ (_) ____| _| | ___ _ __ | |__ _ _ | | | | \\ | | | | | ___ _ __ \n");
51 PRT_Printf(" | '_ ` _ \\| | '_ \\| | _|| | | | |/ _ \\ '__| | '_ \\| | | | | |_| | \\| | | | |/ _ \\ '__|\n");
52 PRT_Printf(" | | | | | | | | | | | |__| |_| | | __/ | | |_) | |_| | | _ | |\\ | |_| | __/ | \n");
53 PRT_Printf(" |_| |_| |_|_|_| |_|_|_____\\__,_|_|\\___|_| |_.__/ \\__, | |_| |_|_| \\_|\\___/ \\___|_| \n");
54 PRT_Printf(" |___/ \n");
55
56 PRT_Printf("ctr-a h: print help of qemu emulator. ctr-a x: quit emulator.\n\n");
57
58 U32 ret;
59 ret = PRT_SemCreate(0, &sem_sync);
60 if (ret != OS_OK) {
61 PRT_Printf("failed to create synchronization sem\n");
62 return 1;
63 }
64
65 struct TskInitParam param = {0};
66
67 // task 1
68 // param.stackAddr = 0;
69 param.taskEntry = (TskEntryFunc)Test1TaskEntry;
70 param.taskPrio = 35;
71 // param.name = "Test1Task";
72 param.stackSize = 0x1000; //固定4096,参见prt_task_init.c的OsMemAllocAlign
73
74 TskHandle tskHandle1;
75 ret = PRT_TaskCreate(&tskHandle1, ¶m);
76 if (ret) {
77 return ret;
78 }
79
80 ret = PRT_TaskResume(tskHandle1);
81 if (ret) {
82 return ret;
83 }
84
85 // task 2
86 // param.stackAddr = 0;
87 param.taskEntry = (TskEntryFunc)Test2TaskEntry;
88 param.taskPrio = 30;
89 // param.name = "Test2Task";
90 param.stackSize = 0x1000; //固定4096,参见prt_task_init.c的OsMemAllocAlign
91
92 TskHandle tskHandle2;
93 ret = PRT_TaskCreate(&tskHandle2, ¶m);
94 if (ret) {
95 return ret;
96 }
97
98 ret = PRT_TaskResume(tskHandle2);
99 if (ret) {
100 return ret;
101 }
102
103 // 启动调度
104 OsActivate();
105
106 // while(1);
107 return 0;
108
109}
lab7 作业
作业1
各种并发问题模拟,至少3种。