hnu操作系统-lab7

lab7 信号量与同步

信号量结构初始化

新建 lab7/src/include/prt_sem_external.h 头文件

  • 宏定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef PRT_SEM_EXTERNAL_H
#define PRT_SEM_EXTERNAL_H

#include "prt_sem.h"
#include "prt_task_external.h"
#if defined(OS_OPTION_POSIX)
#include "bits/semaphore_types.h"
#endif

#define OS_SEM_UNUSED 0  //信号量未使用
#define OS_SEM_USED   1  //信号量已使用

#define SEM_PROTOCOL_PRIO_INHERIT 1  // 优先级继承协议(防止优先级反转)
#define SEM_TYPE_BIT_WIDTH        0x4U  // 信号量类型占用的比特位数(4位)
#define SEM_PROTOCOL_BIT_WIDTH    0x8U  // 协议类型占用的比特位数(8位)

#define OS_SEM_WITH_LOCK_FLAG    1  // 带锁的信号量
#define OS_SEM_WITHOUT_LOCK_FLAG 0  // 不带锁的信号量

#define MAX_POSIX_SEMAPHORE_NAME_LEN    31  // POSIX信号量名称最大长度

//通过链表指针获取信号控制块(TagSemCb)的地址
#define GET_SEM_LIST(ptr) LIST_COMPONENT(ptr, struct TagSemCb, semList)
//通过信号量ID或任务ID快速访问对应的控制块。
#define GET_SEM(semid) (((struct TagSemCb *)g_allSem) + (semid))
#define GET_SEM_TSK(semid) (((SEM_TSK_S *)g_semTsk) + (semid))
#define GET_TSK_SEM(tskid) (((TSK_SEM_S *)g_tskSem) + (tskid))
//从semType中提取信号量类型、互斥锁类型和协议类型(通过位掩码操作)。
#define GET_SEM_TYPE(semType) (U32)((semType) & ((1U << SEM_TYPE_BIT_WIDTH) - 1))
#define GET_MUTEX_TYPE(semType) (U32)(((semType) >> SEM_TYPE_BIT_WIDTH) & ((1U << SEM_TYPE_BIT_WIDTH) - 1))
#define GET_SEM_PROTOCOL(semType) (U32)((semType) >> SEM_PROTOCOL_BIT_WIDTH)
  • 信号控制块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//信号控制块
struct TagSemCb {
    /* 是否使用 OS_SEM_UNUSED/OS_SEM_USED */
    U16 semStat;
    /* 核内信号量索引号 */
    U16 semId;
#if defined(OS_OPTION_SEM_RECUR_PV)
    /* 二进制互斥信号量递归P计数,计数型信号量和二进制同步模式信号量无效 */
    U32 recurCount;
#endif
    /* 当该信号量已用时,其信号量计数 */
    U32 semCount;
    /* 挂接阻塞于该信号量的任务 */
    struct TagListObject semList;
    /* 挂接任务持有的互斥信号量,计数型信号量信号量无效 */
    struct TagListObject semBList;

    /* Pend到该信号量的线程ID */
    U32 semOwner;
    /* 信号量唤醒阻塞任务的方式 */
    enum SemMode semMode;
    /* 信号量,计数型或二进制 */
    U32 semType;
#if defined(OS_OPTION_POSIX)
    /* 信号量名称 */
    char name[MAX_POSIX_SEMAPHORE_NAME_LEN + 1]; // + \0
    /* sem_open 句柄 */
    sem_t handle;
#endif
};

[!NOTE]

  • ​关键字段​​:
    • semCount:信号量的计数值(例如,二进制信号量为0或1)。
    • semList:阻塞在该信号量上的任务链表(当信号量不可用时挂起任务)。
    • semOwner:记录当前持有信号量的任务(用于互斥锁)。
    • semType:通过位掩码存储信号量类型和协议。
  • 全局变量声明和函数声明
1
2
3
4
5
6
7
8
9
10
11
12
/* 模块间全局变量声明 */
extern U16 g_maxSem; // 最大信号量数量

/* 指向核内信号量控制块 */
extern struct TagSemCb *g_allSem;  // 所有信号量控制块的数组

//创建信号量
extern U32 OsSemCreate(U32 count, U32 semType, enum SemMode semMode, SemHandle *semHandle, U32 cookie);
//检查信号量是否被占用
extern bool OsSemBusy(SemHandle semHandle);

#endif /* PRT_SEM_EXTERNAL_H */

[!NOTE]

  • OsSemCreate​:创建信号量。
    • 参数:初始计数值(count)、信号量类型(semType)、模式(semMode)、返回句柄(semHandle)、可选参数(cookie)。
  • OsSemBusy​:检查信号量是否被占用(例如,互斥锁是否已被任务持有)。

新建 src/kernel/sem/prt_sem_init.c 文件。

  • 头文件,全局变量和外部的内存分配函数
1
2
3
4
5
6
7
8
9
#include "prt_sem_external.h"
#include "os_attr_armv8_external.h"
#include "os_cpu_armv8_external.h"

OS_SEC_BSS struct TagListObject g_unusedSemList;//空闲信号量列表
OS_SEC_BSS struct TagSemCb *g_allSem;//所有信号量控制块的数组

//内存分配函数
extern void *OsMemAllocAlign(U32 mid, U8 ptNo, U32 size, U8 alignPow);
  • 信号量初始化函数
    初始化信号量模块,分配内存并设置所有信号量为空闲状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
* 描述:信号量初始化
*/
OS_SEC_L4_TEXT U32 OsSemInit(void)
{
    struct TagSemCb *semNode = NULL;
    U32 idx;
    U32 ret = OS_OK;
    //内存分配,所有信号量TagSemCb共分到4kb
    g_allSem = (struct TagSemCb *)OsMemAllocAlign((U32)OS_MID_SEM,
                                                0,
                                                4096,
                                                OS_SEM_ADDR_ALLOC_ALIGN);

    if (g_allSem == NULL) {
        return OS_ERRNO_SEM_NO_MEMORY;
    }
    //初始化控制块数组
    g_maxSem = 4096 / sizeof(struct TagSemCb);//计算最大信号量数量

    char *cg_allSem = (char *)g_allSem;
    for(int i = 0; i < 4096; i++)//清零内存,确保初始状态一致
        cg_allSem[i] = 0;

    INIT_LIST_OBJECT(&g_unusedSemList);
    for (idx = 0; idx < g_maxSem; idx++) {
        semNode = ((struct TagSemCb *)g_allSem) + idx; //指针操作
        semNode->semId = (U16)idx;//设置信号量ai
        //构建空闲链表
        // 将每个信号量控制块(TagSemCb)的链表节点挂到g_unusedSemList中。
        ListTailAdd(&semNode->semList, &g_unusedSemList);//加入空闲链表
    }

    return ret;
}
  • 信号量创建函数
    从空闲链表中分配一个信号量,并初始化其属性。

[!NOTE] 参数

  • count:信号量初始计数值(如二进制信号量为0或1)。
  • semType:信号量类型(如SEM_TYPE_COUNT计数型、SEM_TYPE_BIN二进制型)。
  • semMode:阻塞任务唤醒模式(如SEM_MODE_FIFO先进先出)。
  • semHandle:输出参数,返回创建的信号量句柄。
  • cookie:预留参数(未使用)。
    ``
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
OS_SEC_L4_TEXT U32 OsSemCreate(U32 count, U32 semType, enum SemMode semMode,
                            SemHandle *semHandle, U32 cookie)
{
    uintptr_t intSave;
    struct TagSemCb *semCreated = NULL;
    struct TagListObject *unusedSem = NULL;
    (void)cookie;

    //检查输入
    if (semHandle == NULL) {
        return OS_ERRNO_SEM_PTR_NULL;
    }

    //关中断保护临界区
    intSave = OsIntLock();

    //检查空闲链表
    if (ListEmpty(&g_unusedSemList)) {
        OsIntRestore(intSave);
        return OS_ERRNO_SEM_ALL_BUSY;
        //若无空闲信号量,返回错误码OS_ERRNO_SEM_ALL_BUSY
    }

    /* 在空闲链表中取走一个控制节点 */
    unusedSem = OS_LIST_FIRST(&(g_unusedSemList));
    ListDelete(unusedSem);

    /* 获取到空闲节点对应的信号量控制块,并开始填充控制块 */
    semCreated = (GET_SEM_LIST(unusedSem));//获取控制块地址
    //设置参数
    semCreated->semCount = count;
    semCreated->semStat = OS_SEM_USED;//标记为已使用
    semCreated->semMode = semMode;
    semCreated->semType = semType;
    semCreated->semOwner = OS_INVALID_OWNER_ID;//标记为无持有者
    //如果是二值信号量,初始化互斥链表
    if (GET_SEM_TYPE(semType) == SEM_TYPE_BIN) {
        INIT_LIST_OBJECT(&semCreated->semBList);
#if defined(OS_OPTION_SEM_RECUR_PV)
        //如果是递归互斥锁,初始化递归计数
        if (GET_MUTEX_TYPE(semType) == PTHREAD_MUTEX_RECURSIVE) {
            semCreated->recurCount = 0;
        }
#endif
    }

    //初始化阻塞任务链表
    INIT_LIST_OBJECT(&semCreated->semList);
    //返回句柄为信号量id
    *semHandle = (SemHandle)semCreated->semId;

    OsIntRestore(intSave);//恢复中断
    return OS_OK;
}

[!NOTE] 递归互斥锁
递归互斥锁​​(Recursive Mutex)是一种特殊的互斥锁(Mutex),允许 ​​同一个线程​​ 多次获取(Lock)同一个锁而不会导致死锁。普通互斥锁如果被同一个线程重复加锁,会导致线程阻塞(死锁),但递归互斥锁会记录加锁次数,必须解锁(Unlock)相同次数才能真正释放锁

在 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
2
3
4
5
6
7
#include "prt_sem_external.h"
#include "prt_asm_cpu_external.h"
#include "os_attr_armv8_external.h"
#include "os_cpu_armv8_external.h"

/* 核内信号量最大个数 */
OS_SEC_BSS U16 g_maxSem;
  • OsSemPostErrorCheck(释放信号量前检查)​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 验证信号量是否可被释放(post)
OS_SEC_ALW_INLINE INLINE U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle)
{
    (void)semHandle;
    /* 检查信号量控制块是否UNUSED,排除大部分错误场景 */
    if (semPosted->semStat == OS_SEM_UNUSED) {
        return OS_ERRNO_SEM_INVALID;//信号量未初始化
    }

    /* post计数型信号量的错误场景, 释放计数型信号量且信号量计数大于最大计数 */
    if ((semPosted)->semCount >= OS_SEM_COUNT_MAX) {
        return OS_ERRNO_SEM_OVERFLOW;//计数型信号量值超过最大值
    }

    return OS_OK;
}
  • OsSemPendListPut(挂载阻塞任务到信号量链表)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
OS_SEC_L0_TEXT void OsSemPendListPut(struct TagSemCb *semPended, U32 timeOut)
{
    struct TagTskCb *curTskCb = NULL;
    struct TagTskCb *runTsk = RUNNING_TASK;//获取当前运行任务
    struct TagListObject *pendObj = &runTsk->pendList;//当前任务的信号量链表节点

    OsTskReadyDel((struct TagTskCb *)runTsk);//从就绪队列移除任务

    runTsk->taskSem = (void *)semPended;//关联任务和信号量,当前任务的信号量指针指向信号量

    TSK_STATUS_SET(runTsk, OS_TSK_PEND);//设置任务状态为阻塞
    /* 根据唤醒方式挂接此链表,同优先级再按FIFO子顺序插入 */
    if (semPended->semMode == SEM_MODE_PRIOR) {//优先级模式
        //找到第一个优先级比当前任务低的任务,将当前任务插入到该优先级任务的后面
        LIST_FOR_EACH(curTskCb, &semPended->semList, struct TagTskCb, pendList) {
            if (curTskCb->priority > runTsk->priority) {
                //将优先级高的任务插入信号链表队尾
                ListTailAdd(pendObj, &curTskCb->pendList);
                // goto TIMER_ADD;
                return;
            }
        }
    }
    /* 如果到这里,说明是FIFO方式;或者是优先级方式且挂接首个节点或者挂接尾节点 */
    ListTailAdd(pendObj, &semPended->semList);

}
  •  OsSemPendListGet(从信号量链表唤醒任务)​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
OS_SEC_L0_TEXT struct TagTskCb *OsSemPendListGet(struct TagSemCb *semPended)
{
    //获取信号量链表的首结点对应的任务块
    struct TagTskCb *taskCb = GET_TCB_PEND(LIST_FIRST(&(semPended->semList)));
    //将其从链表移除
    ListDelete(LIST_FIRST(&(semPended->semList)));
    /* 如果阻塞的任务属于定时等待的任务时候,去掉其定时等待标志位,并将其从去除 */
    if (TSK_STATUS_TST(taskCb, OS_TSK_TIMEOUT)) {
        OS_TSK_DELAY_LOCKED_DETACH(taskCb);
    }

    /* 必须先去除 OS_TSK_TIMEOUT 态,再入队[睡眠时是先出ready队,再置OS_TSK_TIMEOUT态] */
    TSK_STATUS_CLEAR(taskCb, OS_TSK_TIMEOUT | OS_TSK_PEND);//清除阻塞和超市状态
    taskCb->taskSem = NULL;//解除任务和信号量的关联
    /* 如果去除信号量阻塞位后,该任务不处于阻塞态则将该任务挂入就绪队列并触发任务调度 */
    if (!TSK_STATUS_TST(taskCb, OS_TSK_SUSPEND)) {//如果任务未被挂起
        OsTskReadyAddBgd(taskCb);//加入就绪队列
    }

    return taskCb;//返回被唤醒的任务
}
  • OsSemPendParaCheck(Pend操作参数检查)​
1
2
3
4
5
6
7
8
9
10
11
12
//检查PRT_SemPend的输入参数是否合法。
OS_SEC_L0_TEXT U32 OsSemPendParaCheck(U32 timeout)
{
    if (timeout == 0) {//超时时间为0
        return OS_ERRNO_SEM_UNAVAILABLE;
    }

    if (OS_TASK_LOCK_DATA != 0) {//检查是否在任务锁中被调用
        return OS_ERRNO_SEM_PEND_IN_LOCK;
    }
    return OS_OK;//参数合法
}
  • OsSemPendNotNeedSche(快速路径检查)​
1
2
3
4
5
6
7
8
9
10
11
//如果信号量可用,直接获取并返回TRUE
OS_SEC_L0_TEXT bool OsSemPendNotNeedSche(struct TagSemCb *semPended, struct TagTskCb *runTsk)
{
    if (semPended->semCount > 0) {//信号量可用
        semPended->semCount--;//减少计数
        semPended->semOwner = runTsk->taskPid;//设置Pend到该信号量的线程ID

        return TRUE;
    }
    return FALSE;
}
  •  PRT_SemPend(信号量P操作)​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
* 描述:指定信号量的P操作
*/
OS_SEC_L0_TEXT U32 PRT_SemPend(SemHandle semHandle, U32 timeout)
{
    uintptr_t intSave;
    U32 ret;
    struct TagTskCb *runTsk = NULL;
    struct TagSemCb *semPended = NULL;

    if (semHandle >= (SemHandle)g_maxSem) {//检查信号量id是否合法
        return OS_ERRNO_SEM_INVALID;
    }

    semPended = GET_SEM(semHandle);//获取信号量id对应的控制块

    intSave = OsIntLock();//关中断
    if (semPended->semStat == OS_SEM_UNUSED) {//检查信号量是否初始化
        OsIntRestore(intSave);
        return OS_ERRNO_SEM_INVALID;
    }

    if (OS_INT_ACTIVE) {//检查是否在中断时调用
        OsIntRestore(intSave);
        return OS_ERRNO_SEM_PEND_INTERR;
    }

    runTsk = (struct TagTskCb *)RUNNING_TASK;//获取当前任务

    if (OsSemPendNotNeedSche(semPended, runTsk) == TRUE) {//获取当前信号量,如果当前信号量可用,-1并返回
        OsIntRestore(intSave);//恢复中断状态
        return OS_OK;
    }

    ret = OsSemPendParaCheck(timeout);//参数检查
    if (ret != OS_OK) {
        OsIntRestore(intSave);
        return ret;
    }
    /* 把当前任务挂接在信号量链表上 */
    OsSemPendListPut(semPended, timeout);//如果当前信号量不可用则阻塞当前任务
    if (timeout != OS_WAIT_FOREVER) {
        OsIntRestore(intSave);
        return OS_ERRNO_SEM_FUNC_NOT_SUPPORT;
    } else {
        /* 恢复ps的快速切换 */
        OsTskScheduleFastPs(intSave);//触发任务调度

    }

    OsIntRestore(intSave);//恢复中断
    return OS_OK;

}
  • OsSemPostSchePre(Post操作预处理)
1
2
3
4
5
6
7
8
//post操作预处理
OS_SEC_ALW_INLINE INLINE void OsSemPostSchePre(struct TagSemCb *semPosted)
{
    struct TagTskCb *resumedTask = NULL;

    resumedTask = OsSemPendListGet(semPosted);//唤醒第一个任务
    semPosted->semOwner = resumedTask->taskPid;//设置信号量持有者
}
  • OsSemPostIsInvalid(Post操作无效检查)​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
* 描述:判断信号量post是否有效
* 备注:以下情况表示post无效,返回TRUE: post互斥二进制信号量,若该信号量被嵌套pend或者已处于空闲状态
*/
OS_SEC_ALW_INLINE INLINE bool OsSemPostIsInvalid(struct TagSemCb *semPosted)
{
    if (GET_SEM_TYPE(semPosted->semType) == SEM_TYPE_BIN) {
        /* 释放互斥二进制信号量且信号量已处于空闲状态 */
        if ((semPosted)->semCount == OS_SEM_FULL) {
            return TRUE;
        }
    }
    return FALSE;
}
  • PRT_SemPost(信号量V操作)​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
* 描述:指定信号量的V操作
*/
OS_SEC_L0_TEXT U32 PRT_SemPost(SemHandle semHandle)
{
    U32 ret;
    uintptr_t intSave;
    struct TagSemCb *semPosted = NULL;

    if (semHandle >= (SemHandle)g_maxSem) {//检查信号量id
        return OS_ERRNO_SEM_INVALID;
    }

    semPosted = GET_SEM(semHandle);
    intSave = OsIntLock();//关中断

    ret = OsSemPostErrorCheck(semPosted, semHandle);//错误检查
    if (ret != OS_OK) {
        OsIntRestore(intSave);
        return ret;
    }

    /* 信号量post无效,不需要调度 */
    if (OsSemPostIsInvalid(semPosted) == TRUE) {
        OsIntRestore(intSave);
        return OS_OK;
    }

    /* 如果有任务阻塞在信号量上,就激活信号量阻塞队列上的首个任务 */
    if (!ListEmpty(&semPosted->semList)) {
        OsSemPostSchePre(semPosted);//唤醒信号量阻塞队列上的首个任务
        /* 相当于快速切换+中断恢复 */
        OsTskScheduleFastPs(intSave);//优先级调度
    } else {//无阻塞
        semPosted->semCount++;//增加空闲信号量计数
        semPosted->semOwner = OS_INVALID_OWNER_ID;//清除持有者
    }

    OsIntRestore(intSave);
    return OS_OK;
}

[!NOTE] 关键函数及其操作
(1) OsSemPendListPut(挂载阻塞任务)​
void OsSemPendListPut(struct TagSemCb *semPended, U32 timeOut);
- 将当前任务(RUNNING_TASK)挂到信号量的阻塞队列(semList)。
- 根据 semMode 决定插入顺序:
- SEM_MODE_PRIOR:按任务优先级插入(高优先级在前)。
- SEM_MODE_FIFO:插入队尾。

  • ​关键操作​​:
    • 从就绪队列移除任务(OsTskReadyDel)。
    • 设置任务状态为 OS_TSK_PEND(阻塞态)。
      ​(2) OsSemPendListGet(唤醒阻塞任务)​
      struct TagTskCb *OsSemPendListGet(struct TagSemCb *semPended);
  • ​功能​​:
    • 从 semList 取出第一个任务,加入就绪队列(OsTskReadyAddBgd)。
    • 清除任务的阻塞状态(OS_TSK_PEND)。
    • 返回被唤醒的任务指针。
      ​(3) OsSemPostErrorCheck(释放前检查)​
      U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle);
  • ​检查项​​:
    1. 信号量是否未初始化(semStat == OS_SEM_UNUSED)。
    2. 计数型信号量是否溢出(semCount >= OS_SEM_COUNT_MAX)。


src/include/prt_task_external.h 加入 OsTskReadyAddBgd()
将指定任务块加入就绪队列

1
2
3
4
OS_SEC_ALW_INLINE INLINE void OsTskReadyAddBgd(struct TagTskCb *task)
{
OsTskReadyAdd(task);
}

src/kernel/task/prt_task.c 加入 OsTskScheduleFastPs()
触发任务调度`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/core/kernel/task/prt_amp_task.c
/*
* 描述:如果快速切换后只有中断恢复,使用此接口
*/
OS_SEC_TEXT void OsTskScheduleFastPs(uintptr_t intSave)
{
/* Find the highest task */
OsTskHighestSet();//查找最高优先级任务,更新全局变量g_highestTask

/* In case that running is not highest then reschedule */
//如果当前运行的任务不是最高优先级任务,且系统未处于任务锁定状态(允许调度)
if ((g_highestTask != RUNNING_TASK) && (g_uniTaskLock == )) {
UNI_FLAG |= OS_FLG_TSK_REQ;//设置调度请求标志

/* only if there is not HWI or TICK the trap */
if (OS_INT_INACTIVE) {
OsTaskTrapFastPs(intSave);//触发调度
}
}
}

src/bsp/os_cpu_armv8_external.h 加入 OsTaskTrapFastPs()

1
2
3
4
5
OS_SEC_ALW_INLINE INLINE void OsTaskTrapFastPs(uintptr_t intSave)
{
(void)intSave;
OsTaskTrap();
}

加入 src/include/prt_sem.h [下载],该头文件主要是信号量相关的函数声明和宏定义。

验证

  • ​Test1Task​​:
    • 打印消息后释放信号量 (PRT_SemPost)。
    • 循环打印 5 次 task 1 run ...
  • ​Test2Task​​:
    • 尝试获取信号量 (PRT_SemPend),因初始值为 0 而阻塞。
    • 被 Test1Task 唤醒后,循环打印 5 次 task 2 run ...
  • 高优先级任务 (Test2Task) 优先执行,但通过信号量实现可控阻塞。
  • task2的优先级>task1的优先级,task2先执行,但是我们对信号量初始为0,task2执行p操作,信号量-1,此时信号量<0,task2被阻塞,task1开始运行,调用v操作,信号量+1,此时信号量=0,唤醒信号量阻塞队列的第一个任务task2,执行task2后在执行task1,所以输出

作业

  1. 违反原子性缺陷
    我们设置一个share变量,在未加锁的情况下,我们期待看到task1修改了task2输入的2值,使task2输出1
    可以看到,task2原本要输出2,但是他执行到一半被中断了,切换到task1,task1把share修改为1,所以task2输出为1
  2. 违反顺序缺陷 两个内存访问的预期顺序被打破
  3. 死锁 两个进程相互等待
  4. 可以看到taskA获取信号量A后等待,此时中断,B获取信号B后等待,两个进程接下来都各自希望获取对方的信号量,陷入了死锁