信号量

XWOS的信号量

概述

信号量是操作系统比较底层的同步机制,是一个带有 等待队列 的计数器。

信号量中包含一个整数计数器:

  • 当信号量的值等于 0 时,线程们就在 等待队列 中等待信号量的值大于 0
  • 当信号量的值大于 0 时,可以唤醒一个正在等待的线程。线程被唤醒后会取走一个值,信号量的值减少 1
  • 当信号量的值小于 0 ,信号量处于 冻结 状态,理论中的信号量不存在此状态,这是XWOS的扩张。

任意上下文都可增加信号量的值,这个操作被称为 发布

信号量常常用于在中断中唤醒一个线程,并将耗时较长的操作放在线程中执行。可减少中断上下文的执行时间,增加中断吞吐量,降低中断延迟。

XWOS内核的信号量有两种:

  • 管道信号量(PipelineSemaphore):对所有阻塞在其等待队列中的线程按照先进先出(FIFO)的策略调度, 即当信号量可用时,最先进入等待队列中的线程将优先获得信号量。
  • 实时信号量(ReltimeSemaphore):对所有阻塞在其等待队列中的线程按照优先级进行调度,即高优先级 的线程总是最先获得信号量,同优先级的线程按先进先出(FIFO)的策略调度。

操作系统抽象层(OSAL)的CAPI只封装了一种信号量,当系统配置文件中同时配置了管道信号量与实时信号量时, 优先使用实时信号量

信号量对象与对象描述符描述符

信号量对象是 XWOS对象 struct xwos_object 的派生类 。 类似的,信号量对象也用 信号量对象描述符 xwos_sem_d 来解决有效性和身份合法性的问题。

信号量对象描述符由 信号量对象的指针标签 组成:

typedef struct {
        struct xwos_sem * sem; /**< 信号量对象的指针 */
        xwsq_t tik; /**< 标签 */
} xwos_sem_d;

通过对象描述符引用对象时,首先检测 obj->magic 的值,是否为 0x58574F53U ,由此可确定指针 obj 指向一个有效的 XWOS的对象 。 然后对比标签 obj->tiktik 是否相等,由此可以确定对象的 身份 。 因为对象的 tik 是全局唯一的,当对象被释放后,它的 tik 会被析构函数析构为 0 。 当内存地址被重新构建为新的对象,那么它的 tik 一定与对象描述符的 tik 不一致。

信号量的初始化、销毁与动态创建、删除

信号量创建时需要指定两个参数: 初始值最大值

静态初始化、销毁

  • 静态初始化: xwos_sem_init()
    • 静态 是指用户预先定义线程结构体对象,这些对象在编译期由编译器分配内存。
  • 销毁静态初始化的信号量: xwos_sem_fini()

动态创建、删除

  • 动态创建: xwos_sem_create()
    • 动态 是指程序在运行时,通过内存分配函数申请内存,并在申请的内存上构造对象。
  • 删除动态创建的信号量: xwos_sem_delete()

发布信号量

可以通过 xwos_sem_post()任意 上下文增加信号量的值。 当信号量的值大与 0 时,会唤醒信号量等待队列中的一个线程。被唤醒的线程会取走一个值,使得信号量的计数器减1。

等待信号量

当信号量的值大于 0 时,可以直接取走一个,此时信号量的值减 1 ; 当信号量的值等于 0 时,获取信号量的线程就只能阻塞等待,XWOS提供四种方式:

读取信号量的状态

  • 信号量中计数器的最大值可以通过 xwos_sem_get_max() 读取。这个最大值是在信号量初始化或创建时设置的。
  • 信号量中计数器的值可以通过 xwos_sem_get_value() 读取。此CAPI只读取,不会改变信号量的值,也不会等待信号量。

冻结与解冻

冻结

信号量可以使用 xwos_sem_freeze() 进行 冻结, 被冻结的信号量的值为负数,不影响对信号量的 等待 操作。但不能 发布 信号量。

解冻

通过 xwos_sem_thaw() 可将已经冻结的信号量 解冻 。 信号量 解冻 后,值被重置为0,此时可重新开始发布信号量。

绑定与解绑信号选择器

可以通过 xwos_sem_bind() 将信号量绑定到 信号选择器 上。 当 发布 信号量时,信号量会向 信号选择器 发送一个 选择信号 。此时 信号选择器 会唤醒正在等待的线程。

绑定后的信号量可以通过 xwos_sem_unbind() 解绑。

信号量对象的生命周期管理

信号量对象的基类是 XWOS对象 struct xwos_object 。 信号量对象也有两组生命周期管理的CAPI:

  • 使用 对象指针 访问生命周期管理的CAPI:需要确保调用CAPI时,对象一定是有效的,且不存在 释放-又被申请 为另一个对象的情况。

    • xwos_sem_grab() :增加引用计数。
    • xwos_sem_put() :减少引用计数,当引用计数减少为 0 时,调用垃圾回收函数释放对象。
  • 使用 对象描述符 访问生命周期管理的CAPI:用户无法确保对象一定有效或无法确保对象不会变成另一个对象时使用。

    • xwos_sem_acquire() :通过对象描述符确定对象有效且合法,再增加引用计数。
    • xwos_sem_release() :通过对象描述符确定对象有效且合法,再减少引用计数。 当引用计数减少为 0 时,调用垃圾回收函数释放对象。

CAPI参考