ESP32移植Openharmony设备开发---(7)Semp信号量

发布于:2024-10-18 ⋅ 阅读:(12) ⋅ 点赞:(0)

Semp二值信号量

官方文档:OpenAtom OpenHarmony

基本概念

信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。

一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况:

  • 0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。
  • 正值,表示该信号量当前可被获取。

信号量可用于同步或者互斥。以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同:

  • 用作互斥时,初始信号量计数值不为0,表示可用的共享资源个数。在需要使用共享资源前,先获取信号量,然后使用一个共享资源,使用完毕后释放信号量。这样在共享资源被取完,即信号量计数减至0时,其他需要获取信号量的任务将被阻塞,从而保证了共享资源的互斥访问。另外,当共享资源数为1时,建议使用二值信号量,一种类似于互斥锁的机制。
  • 用作同步时,初始信号量计数值为0。任务1因获取不到信号量而阻塞,直到任务2或者某中断释放信号量,任务1才得以进入Ready或Running态,从而达到了任务间的同步。

运作原理

信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,通过LOSCFG_BASE_IPC_SEM_LIMIT宏实现,按产品实际需要设定),并把所有信号量初始化成未使用,加入到未使用链表中供系统使用。

信号量创建,从未使用的信号量链表中获取一个信号量,并设定初值。

信号量申请,若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。

信号量释放,若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。

信号量删除,将正在使用的信号量置为未使用信号量,并挂回到未使用链表。

信号量允许多个任务在同一时刻访问共享资源,但会限制同一时刻访问此资源的最大任务数目。当访问资源的任务数达到该资源允许的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。

轻量系统信号量运作示意图

BUILD.gn

# Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import("//kernel/liteos_m/liteos.gni")

module_name = get_path_info(rebase_path("."), "name")
kernel_module(module_name){
    sources = [
        "LOS_Semp_example.c"
    ]
    
    include_dirs = [
        "//kernel/liteos_m/kernel/include"
    ]
}

LOS_Semp_example.c

/*
 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "los_sem.h"
#include "ohos_run.h"

#ifndef osWaitForever
#define osWaitForever 0xFFFFFFFFU
#endif
#define TASK_STACK_SIZE 4096
#define TASK_PRIO 10
#define DELAY_100_TICKS 100U
#define DELAY_300_TICKS (3 * DELAY_100_TICKS)
#define DELAY_400_TICKS (4 * DELAY_100_TICKS)
#define SEM_COUNT   3

UINT32 semMutex;
UINT32 semSync;

static char g_str[] = "hello!";

void Thread_SempA(void)
{
    while (1) {
        LOS_SemPend(semMutex, osWaitForever);
        printf("Thread_SempA read g_str = %s \n", g_str);
        LOS_TaskDelay(DELAY_100_TICKS);
        LOS_SemPost(semMutex);
    }
}

void Thread_SempB(void)
{
    while (1) {
        LOS_SemPend(semMutex, osWaitForever);
        printf("Thread_SempB read g_str = %s\n", g_str);
        LOS_TaskDelay(DELAY_100_TICKS);
        LOS_SemPost(semMutex);
    }
}

void Thread_SempC(void)
{
    while (1) {
        LOS_SemPend(semMutex, osWaitForever);
        printf("Thread_SempC read g_str = %s\n", g_str);
        LOS_TaskDelay(DELAY_100_TICKS);
        LOS_SemPost(semMutex);
    }
}

void Thread_SempD(void)
{
    while (1) {
        LOS_SemPend(semMutex, osWaitForever);
        printf("Thread_SempD read g_str = %s\n", g_str);
        LOS_TaskDelay(DELAY_100_TICKS);
        LOS_SemPost(semMutex);
    }
}

void Thread_Semp1(void)
{
    LOS_TaskDelay(DELAY_300_TICKS);
    LOS_SemPost(semSync);
    printf("Thread_Semp1 Release semSync.\n");
    LOS_TaskDelay(DELAY_100_TICKS);
}

void Thread_Semp2(void)
{
    printf("entry Thread_Semp2 : try get semSync, timeout 100 ticks!\n");
    UINT32 ret = LOS_SemPend(semSync, DELAY_100_TICKS);
    if (ret == LOS_OK) {
        printf("Thread_Semp2 : got semSync.\n");
    } else if (ret == LOS_ERRNO_SEM_TIMEOUT) {
        printf("Thread_Semp2: failed to get semSync! and try get semSync wait forever.\n");
    }

    LOS_SemPend(semSync, osWaitForever);
    printf("Thread_Semp2: wait_forever and get semSync.\n");
    LOS_TaskDelay(DELAY_100_TICKS);
}

void semp_example(void)
{
    // 创建信号量,用户线程间同步
    if (LOS_SemCreate(1, &semSync) != LOS_OK) {
        printf("semSync create failed!\n");
        return;
    }

    // 创建信号量,用户线程间互斥
    if (LOS_SemCreate(SEM_COUNT, &semMutex) != LOS_OK) {
        printf("semMutex create failed!\n");
        LOS_SemDelete(semSync);
        return;
    }

    TSK_INIT_PARAM_S attr = {0};
    attr.uwStackSize = TASK_STACK_SIZE;
    attr.usTaskPrio = TASK_PRIO;

    // 创建两个线程,通过信号量实现线程间同步
    LOS_TaskLock();
    attr.pcName = "Thread_Semp1";
    attr.pfnTaskEntry = (TSK_ENTRY_FUNC)Thread_Semp1;
    UINT32 taskID1;
    if (LOS_TaskCreate(&taskID1, &attr) != LOS_OK) {
        printf("create Thread_Semp1 failed!\n");
        LOS_SemDelete(semSync);
        LOS_SemDelete(semMutex);
        return;
    }

    attr.pcName = "Thread_Semp2";
    attr.pfnTaskEntry = (TSK_ENTRY_FUNC)Thread_Semp2;
    UINT32 taskID2;
    if (LOS_TaskCreate(&taskID2, &attr) != LOS_OK) {
        printf("create Thread_Semp2 failed!\n");
        LOS_SemDelete(semSync);
        LOS_SemDelete(semMutex);
        LOS_TaskDelete(taskID1);
    }
    LOS_TaskUnlock();
    LOS_TaskDelay(DELAY_400_TICKS);
    LOS_SemDelete(semSync);
}

OHOS_APP_RUN(semp_example);

编译并烧录

在源码根目录下使用hb工具对写好的代码进行编译

选择mini级系统

同理 产品选择esp公司下的esp32

选择完毕后在源码根目录下执行hb build -f 进行编译

编译完成后会有如下界面,并且编译后的代码固件位于:out\esp32\esp32

验证结果

按下ESP32开发板上的EN键,即可观察到实验现象:

API参考

LOS_SemCreate()
UINT32 LOS_SemCreate(UINT16 count, UINT32 *semHandle);

描述:

信号量创建,从未使用的信号量链表中获取一个信号量,并设定初值count,并将新建的信号量ID赋值给semHandle所指向的空间

注意 :不能在中断服务调用该函数

参数:

名字

描述

count

信号量计数值的初始值,其取值范围:[0, OS_SEM_COUNTING_MAX_COUNT)

semHandle

指针,指向存放信号量ID的对象

LOS_BinarySemCreate()
UINT32 LOS_BinarySemCreate(UINT16 count, UINT32 *semHandle);

描述:

创建二值信号量,从未使用的信号量链表中获取一个二值信号量,并设定初值count,并将新建的信号量ID赋值给semHandle所指向的空间

注意 :不能在中断服务调用该函数

参数:

名字

描述

count

信号量计数值的初始值,其取值范围:[0,1]

semHandle

指针,指向存放信号量ID的对象

LOS_SemPost()
UINT32 LOS_SemPost(UINT32 semHandle);

描述:

释放信号量

注意 :该函数可以在中断服务例程调用

参数:

名字

描述

semHandle

信号量ID

返回 0 - 成功,非0 - 失败

LOS_SemPend()
UINT32 LOS_SemPend(UINT32 semHandle, UINT32 timeout);

描述:

获取信号量

参数:

名字

描述

semHandle

信号量ID

timeout

超时值

返回 0 - 成功,非0 - 失败

LOS_SemGetValue()
UINT32 LOS_SemGetValue(UINT32 semHandle, INT32 *currVal);

描述:

获取当前可被使用的共享资源数

参数:

名字

描述

semHandle

信号量ID

currVal

指针,输出参数,存放当前可被使用的共享资源数

返回 0 - 成功,非0 - 失败