概述

POSIX Threads简称Pthreads,POSIX是“Portable Operating System Interface”(可移植操作系统接口)的缩写,POSIX是IEEE Computer Society为了提高不同操作系统的兼容性和应用程序的可移植性而制定的一套标准。Pthreads是线程的POSIX标准,被定义在POSIX.1c,Threads extensions(IEEE Std1003.1c-1995)标准里,该标准定义了一套C程序语言的类型、函数和常量。定义在pthread.h头文件和一个线程库里,大约有100个API,所有API都带有“pthread_”前缀,可以分为4大类:

  • 线程管理(Thread management):包括线程创建(creating)、分离(detaching)、连接(joining)及设置和查询线程属性的函数等。

  • 互斥锁(Mutex):“mutual exclusion”的缩写,用了限制线程对共享数据的访问,保护共享数据的完整性。包括创建、销毁、锁定和解锁互斥锁及一些用于设置或修改互斥量属性等函数。

  • 条件变量(Condition variable):用于共享一个互斥量的线程间的通信。包括条件变量的创建、销毁、等待和发送信号(signal)等函数。

  • 读写锁(read/writelock)和屏障(barrier):包括读写锁和屏障的创建、销毁、等待及相关属性设置等函数。

  • POSIX信号量(semaphore)和Pthreads一起使用,但不是Pthreads标准定义的一部分,被定义在POSIX.1b,Real-timeextensions(IEEE Std1003.1b-1993)标准里。因此信号量相关函数的前缀是“sem”而不是“pthread”。

  • 消息队列(Message queue)和信号量一样,和Pthreads一起使用,也不是Pthreads标准定义的一部分,被定义在IEEE Std1003.1-2001标准里。消息队列相关函数的前缀是“mq_”。

很多操作系统都支持Pthreads,比如Linux、MacOSX、Android和Solaris,因此使用Pthreads的函数编写的应用程序有很好的可移植性,可以在很多支持Pthreads的平台上直接编译运行。

YoC中使用POSIX

POSIX在YoC中的具体配置定义在posix_config.h中。

main函数开始的地方需要调用posix_init()函数来初始化POSIX。

void main()
{
extern int posix_init(void);

    posix_init();

    ...
}

YoC实现了Pthreads的大部分函数和常量,按照POSIX标准定义在pthread.h、mqueue.h、semaphore.h 和 timer.h 头文件里。Pthreads是libc的一个子库,YoC中的Pthreads是基于YoC内核函数的封装,使其符合 POSIX 标准。

线程

线程句柄

typedef void    *pthread_t;

pthread_t定义在pthread.h头文件里,是指向线程控制块的指针。在创建线程前需要先定义一个pthread_t类型的变量。每个线程都对应了自己的线程控制块,线程控制块是操作系统用于控制线程的一个数据结构,它存放了线程的一些信息,例如优先级,线程名称和线程堆栈地址等。

创建线程

int  pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                    void *(*start_routine)(void *), void *arg);
  • 参数
    • thread:指向线程句柄 (线程标识符) 的指针,不能为 NULL
    • attr:指向线程属性的指针,如果使用 NULL,则使用默认的线程属性
    • start_routine:线程入口函数地址
    • arg:传递给线程入口函数的参数
  • 返回值
    • 0:成功
    • -1:失败

此函数创建一个pthread线程。此函数会动态分配POSIX线程数据块和YoC线程控制块,并把线程控制块的起始地址(线程 ID)保存在参数thread指向的内存里,此线程标识符可用于在其他线程中操作此线程;并把attr指向的线程属性、start_routine指向的线程入口函数及入口函数参数arg保存在线程数据块和线程控制块里。如果线程创建成功,线程立刻进入就绪态,参与系统的调度,如果线程创建失败,则会释放之前线程占有的资源。

创建出 pthread 线程后,如果线程需要重复创建使用,需要设置 pthread 线程为 detach 模式,或者使用 pthread_join 等待创建后的 pthread 线程结束。

脱离线程

int  pthread_detach(pthread_t thread);
  • 参数
    • thread:线程句柄
  • 返回值
    • 0:成功
    • -1:失败

调用此函数,如果pthread线程没有结束,则将thread线程属性的分离状态设置为detached;当thread线程已经结束时,系统将回收pthread线程占用的资源。 使用方法:子线程调用pthread_detach(pthread_self())(pthread_self() 返回当前调用线程的线程句柄),或者其他线程调用pthread_detach(thread_id)。

一旦线程属性的分离状态设置为 detached,该线程不能被 pthread_join() 函数等待或者重新被设置为 detached。

等待线程结束

int  pthread_join(pthread_t thread, void **retval);
  • 参数
    • thread:线程句柄
    • retval:用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取
  • 返回值
    • 0:成功
    • -1:失败

此函数会使调用该函数的线程以阻塞的方式等待线程分离属性为joinable的thread线程运行结束,并获得thread线程的返回值,返回值的地址保存在retval里,并释放thread线程占用的资源。

pthread_join() 和 pthread_detach() 函数功能类似,都是在线程结束后用来回收线程占用的资源。线程不能等待自己结束,thread线程的分离状态必须是joinable,一个线程只对应一次pthread_join()调用。分离状态为joinable的线程仅当有其他线程对其执行了pthread_join()后,它所占用的资源才会释放。因此为了避免内存泄漏,所有会结束运行的线程,分离状态要么已设为detached,要么使用 pthread_join() 来回收其占用的资源。

退出线程

void pthread_exit(void *value_ptr);
  • 参数
    • value_ptr:用户定义的指针,用来存储被等待线程的返回值地址,可由函数 pthread_join() 获取
  • 返回值

pthread线程调用此函数会终止执行,如同进程调用 exit() 函数一样,并返回一个指向线程返回值的指针。线程退出由线程自身发起。

若线程的分离状态为joinable,线程退出后该线程占用的资源并不会被释放,必须调用 pthread_join() 函数释放线程占用的资源。

对两个线程的线程标识号进行比较

int pthread_equal(pthread_t t1, pthread_t t2);
  • 参数
    • t1:线程ID
    • t2:线程ID
  • 返回值
    • 0:识别号不同
    • 非0:识别号相同

设置线程的调度策略和参数

int pthread_setschedparam(pthread_t thread, int policy, 
                          const struct sched_param *pParam);
  • 参数
    • thread:线程句柄
    • policy:使用的调度策略
    • pParam:指向struct sched_param类型的指针
  • 返回值
    • 0:成功
    • 非0:失败

自动清理线程资源

void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);
  • 参数
    • routine:处理程序的函数入口
    • arg:传递给处理函数的形参
    • execute:执行的状态值
  • 返回值

注:pthread_cleanup_push函数与pthread_cleanup_pop函数需要成对调用。

一次性初始化

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
  • 参数
    • once_control:指向一个pthread_once_t对象,需为常量pthread_once_init
    • init_routine:调用的初始化函数
  • 返回值
    • 0:成功
    • 非0:失败

访问线程cpu_time时钟

int pthread_getcpuclockid(pthread_t thread_id, clockid_t *clock_id);
  • 参数
    • thread_id:要获取时钟id的线程id
    • clock_id:指向clockid_t对象的指针,函数可以在其中存储时钟id
  • 返回值
    • 0:成功
    • 非0:失败

获取线程并行级别

int pthread_getconcurrency(void);
  • 参数
  • 返回值
    • 返回当前的并发度

设置线程优先级

int pthread_setschedprio(pthread_t thread, int prio);
  • 参数
    • thread:线程句柄
    • prio:要修改成的优先级
  • 返回值
    • 0:成功
    • -1:失败

设置线程名称

int pthread_setname_np(pthread_t thread, const char *name);
  • 参数
    • thread:线程句柄
    • name:线程名称
  • 返回值
    • 0:成功
    • 非0:失败

连接终止的程序

int pthread_timedjoin_np(pthread_t thread, void **retval, 
                         const struct timespec *abstime);
  • 参数
    • thread:线程句柄
    • retval:用户定义的指针,用来存储被等待线程的返回值地址
    • abstime:阻塞时间
  • 返回值
    • 0:成功
    • 非0:失败

互斥锁

互斥锁又叫相互排斥的信号量,是一种特殊的二值信号量。互斥锁用来保证共享资源的完整性,保证在任一时刻,只能有一个线程访问该共享资源,线程要访问共享资源,必须先拿到互斥锁,访问完成后需要释放互斥锁。嵌入式的共享资源包括内存、IO、SCI、SPI等,如果两个线程同时访问共享资源可能会出现问题,因为一个线程可能在另一个线程修改共享资源的过程中使用了该资源,并认为共享资源没有变化。

互斥锁的操作只有两种上锁或解锁,同一时刻只会有一个线程持有某个互斥锁。当有线程持有它时,互斥量处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放它时,将对互斥量进行开锁,失去它的所有权。当一个线程持有互斥量时,其他线程将不能够对它进行解锁或持有它。

对互斥锁的主要操作包括:调用 pthread_mutex_init() 初始化一个互斥锁,调用 pthread_mutex_destroy() 销毁互斥锁,调用 pthread_mutex_lock() 对互斥锁上锁,调 pthread_mutex_unlock() 对互斥锁解锁。

使用互斥锁会导致一个潜在问题是线程优先级翻转。高优先级的任务获取mutex时,如果该mutex被某低优先级的任务占用,YoC系统会动态提升该低优先级任务的优先级等于高优先级,并且将该优先级值依次传递给该低优先级任务依赖的互斥量关联的任务,以此递归下去。当某任务释放mutex时,会查找该任务的基础优先级,以及获取到的互斥量所阻塞的最高优先级的任务的优先级,取两者中高的优先级来重新设定此任务的优先级。总的原则就是,高优先级任务被mutex阻塞时,会将占用该mutex的低优先级任务临时提高;mutex释放时,相应任务的优先级需要恢复。

互斥锁控制块

每个互斥锁对应一个互斥锁控制块,包含对互斥锁进行的控制的一些信息。创建互斥锁前必须先定义一个 pthread_mutex_t 类型的变量,pthread_mutex_t 是 pthread_mutex 的重定义,pthread_mutex 数据结构定义在 pthread.h 头文件里,数据结构如下:

typedef struct pthread_mutex {
    uint8_t              flag;
    void                *mutex;
    pthread_mutexattr_t  attr;
} pthread_mutex_t;

初始化互斥锁

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
  • 参数
    • mutex:互斥锁句柄,不能为 NULL
    • attr:指向互斥锁属性的指针,若该指针 NULL,则使用默认的属性
  • 返回值
    • 0:成功
    • -1:失败

此函数会初始化 mutex 互斥锁,并根据 attr 指向的互斥锁属性对象设置 mutex 属性,成功初始化后互斥锁处于未上锁状态,线程可以获取,此函数是对 krhino_mutex_dyn_create() 函数的封装。

除了调用 pthread_mutex_init() 函数创建一个互斥锁,还可以用宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER(结构体常量),等同于调用 pthread_mutex_init() 时 attr 指定为 NULL。

销毁互斥锁

int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • 参数
    • mutex:互斥锁句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数会销毁 mutex 互斥锁。销毁后互斥锁 mutex 处于未初始化状态。销毁以后互斥锁的属性和控制块参数将不在有效,但可以调用 pthread_mutex_init() 对销毁后的互斥锁重新初始化。但不需要销毁使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化的互斥锁。

当确定互斥锁没有被锁住,且没有线程阻塞在该互斥锁上,才可以销毁该互斥锁。

阻塞方式对互斥锁上锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
  • 参数
    • mutex:互斥锁句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数对 mutex 互斥锁上锁,此函数是对 krhino_mutex_lock() 函数的封装。如果互斥锁 mutex 还没有被上锁,那么申请该互斥锁的线程将成功对该互斥锁上锁。如果互斥锁 mutex 已经被当前线程上锁,且互斥锁类型为嵌套锁,则该互斥锁的持有计数加 1,当前线程也不会挂起等待(死锁),但线程必须对应相同次数的解锁。如果互斥锁 mutex 被其他线程上锁持有,则当前线程将被阻塞,一直到其他线程对该互斥锁解锁后,等待该互斥锁的线程将按照先进先出的原则获取互斥锁。

非阻塞方式对互斥锁上锁

int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • 参数
    • mutex:互斥锁句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数是 pthread_mutex_lock() 函数的非阻塞版本。区别在于如果互斥锁 mutex 已经被上锁,线程不会被阻塞,而是马上返回错误码。

解锁互斥锁

int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 参数
    • mutex:互斥锁句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

调用此函数给 mutex 互斥锁解锁,是对 krhino_mutex_unlock() 函数的封装。当线程完成共享资源的访问后,应尽快释放占有的互斥锁,使得其他线程能及时获取该互斥锁。只有已经拥有互斥锁的线程才能释放它,每释放一次该互斥锁,它的持有计数就减 1。当该互斥量的持有计数为零时(即持有线程已经释放所有的持有操作),互斥锁才变为可用,等待在该互斥锁上的线程将按先进先出方式被唤醒。如果线程的运行优先级被互斥锁提升,那么当互斥锁被释放后,线程恢复为持有互斥锁前的优先级。

绑定互斥锁

int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *at);
  • 参数
    • mutex:互斥锁句柄,不能为 NULL
    • at:愿意等待的超时时间
  • 返回值
    • 0:成功
    • 非0:失败

获取互斥锁优先级上限

int pthread_mutex_getprioceiling(const pthread_mutex_t *__restrict mutex,
                                 int *__restrict prioceiling);
  • 参数
    • __restrict mutex:指向要设置优先级上限的pthread_mutex_t对象的指针
    • __restrict prioceiling:指向可以存储优先级上限位置的指针
  • 返回值
    • 0:成功
    • 非0:失败

设置互斥锁优先级上限

int pthread_mutex_setprioceiling(pthread_mutex_t *__restrict mutex, 
                                 int prioceiling, int *__restrict 
                                 old_ceiling);
  • 参数
    • __restrict mutex:指向要设置优先级上限的pthread_mutex_t对象的指针
    • prioceiling:新的优先级上限值
    • __restrict old_ceiling:指向可以存储旧址位置的指针
  • 返回值
    • 0:成功
    • 非0:失败

条件变量

条件变量其实就是一个信号量,用于线程间同步。条件变量用来阻塞一个线程,当条件满足时向阻塞的线程发送一个条件,阻塞线程就被唤醒,条件变量需要和互斥锁配合使用,互斥锁用来保护共享数据。

条件变量可以用来通知共享数据状态。比如一个处理共享资源队列的线程发现队列为空,则此线程只能等待,直到有一个节点被添加到队列中,添加后在发一个条件变量信号激活等待线程。

条件变量的主要操作包括:调用 pthread_cond_init() 对条件变量初始化,调用 pthread_cond_destroy() 销毁一个条件变量,调用 pthread_cond_wait() 等待一个条件变量,调用 pthread_cond_signal() 发送一个条件变量。

条件变量控制块

每个条件变量对应一个条件变量控制块,包括对条件变量进行操作的一些信息。初始化一个条件变量前需要先定义一个 pthread_cond_t 条件变量控制块。pthread_cond_t 是 pthread_cond 结构体类型的重定义,定义在 pthread.h 头文件里

typedef struct pthread_cond {
    uint8_t            flag;
    void               *lock;
    int                waiting;
    int                signals;
    void               *wait_sem;
    void               *wait_done;

    pthread_condattr_t *attr;
} pthread_cond_t;

初始化条件变量

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
  • 参数
    • cond:条件变量句柄,不能为 NULL
    • attr:指向条件变量属性的指针,若为 NULL 则使用默认属性值
  • 返回值
    • 0:成功
    • -1:失败

此函数会初始化 cond 条件变量,并根据 attr 指向的条件变量属性设置其属性,此函数是对 krhino_mutex_dyn_create() 和 krhino_sem_dyn_create 函数的一个封装,基于互斥锁和信号量实现。初始化成功后条件变量处于不可用状态。

还可以用宏 PTHREAD_COND_INITIALIZER 静态初始化一个条件变量,方法: pthread_cond_t cond = PTHREAD_COND_INITIALIZER(结构体常量),等同于调用 pthread_cond_init() 时 attr 指定为 NULL。

销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond);
  • 参数
    • cond:条件变量句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数会销毁 cond 条件变量,销毁后 cond 处于未初始化状态。销毁之后条件变量的属性及控制块参数将不在有效,但可以调用 pthread_cond_init() 或者静态方式重新初始化。

销毁条件变量前需要确定没有线程被阻塞在该条件变量上,也不会等待获取、发信号或者广播。

阻塞方式获取条件变量

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • 参数
    • cond:条件变量句柄,不能为 NULL
    • mutex:指向互斥锁控制块的指针,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数会以阻塞方式获取 cond 条件变量。线程等待条件变量前需要先将 mutex 互斥锁锁住,此函数首先解锁 mutex 互斥锁,然后尝试获取一个信号量,当信号量值大于零时,表明信号量可用,线程将获得信号量,也就获得该条件变量,相应的信号量值会减 1。如果信号量的值等于零,表明信号量不可用,线程将阻塞直到信号量可用,之后将对 mutex 互斥锁再次上锁。

指定阻塞时间获取条件变量

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 
                           const struct timespec *abstime);
  • 参数
    • cond:条件变量句柄,不能为 NULL
    • mutex:指向互斥锁控制块的指针,不能为 NULL
    • abstime:指定的等待时间,单位是操作系统时钟节拍(OS Tick)
  • 返回值
    • 0:成功
    • EINVAL:参数无效
    • ETIMEDOUT:超时
    • -1:失败

此函数和 pthread_cond_wait() 函数唯一的差别在于,如果条件变量不可用,线程将被阻塞 abstime 时长,超时后函数将直接返回 ETIMEDOUT 错误码,线程将会被唤醒进入就绪态。

发送满足条件信号量

int pthread_cond_signal(pthread_cond_t *cond);
  • 参数
    • cond:条件变量句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数会发送一个信号且只唤醒一个等待 cond 条件变量的线程,是对 krhino_sem_give() 函数的封装,也就是发送一个信号量。当信号量的值等于零,并且有线程等待这个信号量时,将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量。否则将把信号量的值加 1。

广播

int pthread_cond_broadcast(pthread_cond_t *cond);
  • 参数
    • cond:条件变量句柄,不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

调用此函数将唤醒所有等待 cond 条件变量的线程。

信号量

信号量可以用于进程与进程之间,或者进程内线程之间的通信。每个信号量都有一个不会小于 0 的信号量值,对应信号量的可用数量。调用 sem_init() 或者 sem_open() 给信号量值赋初值,调用 sem_post() 函数可以让信号量值加 1,调用 sem_wait() 可以让信号量值减 1,如果当前信号量为 0,调用 sem_wait() 的线程被挂起在该信号量的等待队列上,直到信号量值大于 0,处于可用状态。

根据信号量的值(代表可用资源的数目)的不同,POSIX 信号量可以分为:

  • 二值信号量:信号量的值只有 0 和 1,初始值指定为 1。这和互斥锁一样,若资源被锁住,信号量的值为 0,若资源可用,则信号量的值为 1。相当于只有一把钥匙,线程拿到钥匙后,完成了对共享资源的访问后需要解锁,把钥匙再放回去,给其他需要此钥匙的线程使用。使用方法和互斥锁一样,等待信号量函数必须和发送信号量函数成对使用,不能单独使用,必须先等待后发送。

  • 计数信号量:信号量的值在 0 到一个大于 1 的限制值(POSIX 指出系统的最大限制值至少要为 32767)。该计数表示可用信号量个数。此时,发送信号量函数可以被单独调用发送信号量,相当于有多把钥匙,线程拿到一把钥匙就消耗了一把,使用过的钥匙不必在放回去。

POSIX 信号量又分为有名信号量和无名信号量:

  • 有名信号量:其值保存在文件中,一般用于进程间同步或互斥。YoC暂时未实现有名信号量。

  • 无名信号量:其值保存在内存中,一般用于线程间同步或互斥。

YoC 系统的 POSIX 信号量主要是基于 YoC 内核信号量的一个封装,主要还是用于系统内线程间的通讯。使用方式和 YoC 内核的信号量差不多。

无名信号量

无名信号量的值保存在内存中,一般用于线程间同步或互斥。在使用之前,必须先调用 sem_init() 初始化。

初始化无名信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 参数
    • sem:信号量句柄
    • pshared:YoC未实现参数
    • value:信号量初始值,表示信号量资源的可用数量
  • 返回值
    • 0:成功
    • -1:失败

此函数初始化一个无名信号量 sem,根据给定的或默认的参数对信号量相关数据结构进行初始化,并把信号量放入信号量链表里。初始化后信号量值为给定的初始值 value。此函数是对 krhino_sem_dyn_create() 函数的封装。

销毁无名信号量

int sem_destroy(sem_t *sem);
  • 参数
    • sem:信号量句柄
  • 返回值
    • 0:成功
    • -1:失败

此函数会销毁一个无名信号量 sem,并释放信号量占用的资源。

有名信号量

有名信号量,其值保存在文件中,一般用于进程间同步或互斥。两个进程可以操作相同名称的有名信号量。在YoC系统中未实现。

创建或打开有名信号量

sem_t *sem_open(const char *name, int oflag, ...);

未实现。

分离有名信号量

int sem_unlink(const char *name);

未实现。

关闭有名信号量

int sem_close(sem_t *sem);

未实现。

获取信号量值

int sem_getvalue(sem_t *sem, int *sval);
  • 参数
    • sem:信号量句柄
    • sval:保存获取的信号量值地址, 不能为 NULL
  • 返回值
    • 0:成功
    • -1:失败

此函数可以获取 sem 信号量的值,并保存在 sval 指向的内存里,可以知道信号量的资源数量。

阻塞方式等待信号量

int sem_wait(sem_t *sem);
  • 参数
    • sem:信号量句柄
  • 返回值
    • 0:成功
    • -1:失败

线程调用此函数获取信号量,是 krhino_sem_take() 函数的封装。若信号量值大于零,表明信号量可用,线程获得信号量,信号量值减 1。若信号量值等于 0,表明信号量不可用,线程阻塞进入挂起状态,并按照先进先出的方式排队等待,直到信号量可用。

非阻塞方式获取信号量

int sem_trywait(sem_t *sem);
  • 参数
    • sem:信号量句柄
  • 返回值
    • 0:成功
    • -1:失败

此函数是 sem_wait() 函数的非阻塞版,是 krhino_sem_take(sem,0) 函数的封装。当信号量不可用时,线程不会阻塞,而是直接返回。

指定阻塞时间等待信号量

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  • 参数
    • sem:信号量句柄
    • abs_timeout:指定的等待时间,单位是操作系统时钟节拍(OS Tick)
  • 返回值
    • 0:成功
    • -1:失败

此函数和 sem_wait() 函数的区别在于,若信号量不可用,线程将阻塞 abs_timeout 时长,超时后函数返回 - 1,线程将被唤醒由阻塞态进入就绪态。

发送信号量

int sem_post(sem_t *sem);
  • 参数
    • sem:信号量句柄
  • 返回值
    • 0:成功
    • -1:失败

此函数将释放一个 sem 信号量,是 krhino_sem_give() 函数的封装。若等待该信号量的线程队列不为空,表明有线程在等待该信号量,第一个等待该信号量的线程将由挂起状态切换到就绪状态,等待系统调度。若没有线程等待该信号量,该信号量值将加 1。

消息队列

消息队列是另一种常用的线程间通讯方式,它能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。

消息队列主要操作包括:通过函数 mq_open() 创建或者打开,调用 mq_send() 发送一条消息到消息队列,调用 mq_receive() 从消息队列获取一条消息,当消息队列不在使用时,可以调用 mq_unlink() 删除消息队列。

POSIX 消息队列主要用于进程间通信,YoC系统的 POSIX 消息队列主要是基于 YoC 内核消息队列的一个封装,主要还是用于系统内线程间的通讯。使用方式和 YoC 内核的消息队列差不多。

创建或打开消息队列

mqd_t mq_open(const char *name, int oflag, ...);
  • 参数
    • name:消息队列名称
    • oflag:消息队列打开方式(YoC未使用参数)
  • 返回值
    • 消息队列句柄:成功
    • NULL:失败

此函数会根据消息队列的名字 name 创建一个新的消息队列或者打开一个已经存在的消息队列。

分离消息队列

int mq_unlink(const char *name);

未实现。

关闭消息队列

int mq_close(mqd_t mqdes);
  • 参数
    • mqdes:消息队列句柄
  • 返回值
    • 0:成功
    • -1:失败

阻塞方式发送消息

int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, 
            unsigned msg_prio);
  • 参数
    • mqdes:消息队列句柄
    • msg_ptr:指向要发送的消息的指针
    • msg_len:发送的消息的长度
    • msg_prio:未实现参数
  • 返回值
    • 消息长度:成功
    • 0:失败

此函数用来向 mqdes 消息队列发送一条消息,是 krhino_buf_queue_send() 函数的封装。此函数把 msg_ptr 指向的消息添加到 mqdes 消息队列中,发送的消息长度 msg_len 必须小于或者等于创建消息队列时设置的最大消息长度。

指定阻塞时间发送消息

int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, 
                 unsigned msg_prio, const struct timespec *abs_timeout);
  • 参数
    • mqdes:消息队列句柄
    • msg_ptr:指向要发送的消息的指针
    • msg_len:发送的消息的长度
    • msg_prio:未实现参数
    • abs_timeout:指定的等待时间,单位是操作系统时钟节拍(OS Tick)
  • 返回值
    • 消息长度:成功
    • 0:失败

YoC暂不支持timeout发送,此函数效果同mq_send()。

阻塞方式接受消息

ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, 
                   unsigned *msg_prio);
  • 参数
    • mqdes:消息队列句柄
    • msg_ptr:指向要发送的消息的指针
    • msg_len:发送的消息的长度
    • msg_prio:未实现参数
  • 返回值
    • 消息长度:成功
    • 0:失败

指定阻塞时间接受消息

ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, 
                        unsigned *msg_prio, 
                        const struct timespec *abs_timeout);
  • 参数
    • mqdes:消息队列句柄
    • msg_ptr:指向要发送的消息的指针
    • msg_len:发送的消息的长度
    • msg_prio:未实现参数
    • abs_timeout:指定的等待时间,单位是操作系统时钟节拍(OS Tick)
  • 返回值
    • 消息长度:成功
    • 0:失败

设置消息队列属性

int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, 
               struct mq_attr *omqstat);
  • 参数
    • mqdes:消息队列句柄
    • mqstat:指向要设置的消息队列属性结构体指针
    • omqstat:指向旧的消息队列属性结构体指针
  • 返回值
    • 0:成功
    • -1:失败

获取消息队列属性

int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);
  • 参数
    • mqdes:消息队列句柄
    • mqstat:指向消息队列属性结构体的指针
  • 返回值
    • 0:成功
    • -1:失败

results matching ""

    No results matching ""