POSIX 互斥信号量
POSIX 互斥信号量是对共享资源进行互斥访问的“POSIX”接口函数,实现的作用与 SylixOS 信号量相同。
POSIX 互斥信号量的类型为 pthread_mutex_t。使用时需要定义一个 pthread_mutex_t 类型的变量,如:
pthread_mutex_t mutex;
一个 POSIX 互斥信号量使用以前必须首先进行初始化,可以把 mutex 初始值设置为 PTHREAD_MUTEX_INITIALIZER(静态初始化),也可以调用 pthread_mutex_init 函数进行动态初始化。
线程如果需要等待一个互斥信号量,可以调用 pthread_mutex_lock 函数。释放一个互斥信号量使用 pthread_mutex_unlock 函数。
当一个互斥信号量使用完毕后,应该调用 pthread_mutex_destroy 函数将其删除,SylixOS 会回收该互斥信号量占用的内核资源。需要注意的是,如果试图再次使用一个被删除的信号量,将出现未知的错误。
同线程具有属性对象一样,POSIX 互斥信号量同样具有自己的属性对象,创建一个 POSIX 互斥信号量需要使用 POSIX 互斥信号量属性块。POSIX 互斥信号量属性块的类型为 pthread_mutexattr_t。使用时需要定义一个 pthread_mutexattr_t 类型的变量,如:
pthread_mutexattr_t mutexattr;
互斥信号量属性块
互斥信号量属性块的初始化和删除
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *pmutexattr);
函数 pthread_mutexattr_init 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
#include <pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *pmutexattr);
函数 pthread_mutexattr_destroy 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
设置和获取互斥信号量属性块的类型
#include <pthread.h>
int pthread_mutexattr_settype(pthread_mutexattr_t *pmutexattr,
int type);
函数 pthread_mutexattr_settype 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 参数 type 是 POSIX 互斥信号量属性块的类型。
互斥信号量属性块的类型可以使用如下表所示的宏。
宏名 | 含义 |
---|---|
PTHREAD_MUTEX_NORMAL | 递归加锁时产生死锁 |
PTHREAD_MUTEX_ERRORCHECK | 递归加锁时返回错误 |
PTHREAD_MUTEX_RECURSIVE | 支持递归加锁 |
PTHREAD_MUTEX_FAST_NP | 等同 PTHREAD_MUTEX_NORMAL |
PTHREAD_MUTEX_ERRORCHECK_NP | 等同 PTHREAD_MUTEX_ERRORCHECK |
PTHREAD_MUTEX_RECURSIVE_NP | 等同 PTHREAD_MUTEX_RECURSIVE |
PTHREAD_MUTEX_DEFAULT | 等同 PTHREAD_MUTEX_RECURSIVE |
#include <pthread.h>
int pthread_mutexattr_gettype(const pthread_mutexattr_t *pmutexattr,
int *type);
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 输出参数 type 是 POSIX 互斥信号量属性块的类型。
设置和获取互斥信号量属性块的算法类型
#include <pthread.h>
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *pmutexattr,
int protocol);
函数 pthread_mutexattr_setprotocol 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 参数 protocol 是 POSIX 互斥信号量属性块的算法类型。
互斥信号量属性块的算法类型可以使用如下表所示的宏。
宏名 | 含义 |
---|---|
PTHREAD_PRIO_NONE | 优先级继承算法,按先入先出顺序等待 |
PTHREAD_PRIO_INHERIT | 优先级继承算法,按优先级顺序等待 |
PTHREAD_PRIO_PROTECT | 优先级天花板 |
#include <pthread.h>
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *pmutexattr,
int protocol);
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 参数 protocol 是 POSIX 互斥信号量属性块的算法类型。
设置和获取互斥信号量属性块的天花板优先级
#include <pthread.h>
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *pmutexattr,
int prioceiling);
函数 pthread_mutexattr_setprioceiling 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 参数 prioceiling 是 POSIX 互斥信号量属性块的天花板优先级。
#include <pthread.h>
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *pmutexattr,
int *prioceiling);
函数 pthread_mutexattr_setprioceiling 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 输出参数 prioceiling 是 POSIX 互斥信号量属性块的天花板优先级。
设置和获取互斥信号量属性块的进程共享属性
#include <pthread.h>
int pthread_mutexattr_setpshared(pthread_mutexattr_t *pmutexattr,
int pshared);
函数 pthread_mutexattr_setpshared 原型分析:
- 此函数返回 0。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 参数 pshared 标识了 POSIX 互斥信号量属性块是否进程共享。
进程共享参数可以使用如下表所示宏。
宏名 | 含义 |
---|---|
PTHREAD_PROCESS_SHARED | 进程共享 |
PTHREAD_PROCESS_PRIVATE | 进程私有 |
#include <pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *pmutexattr,
int *pshared);
- 此函数返回 0。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针。
- 输出参数 pshared 标识了 POSIX 互斥信号量属性块是否进程共享。
需要注意的是,SylixOS 总是进程私有的(PTHREAD_PROCESS_PRIVATE)。
互斥信号量
互斥信号量的初始化和删除
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *pmutex,
const pthread_mutexattr_t *pmutexattr);
函数 pthread_mutex_init 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutex 是 POSIX 互斥信号量的指针。
- 参数 pmutexattr 是 POSIX 互斥信号量属性块的指针,可以为 NULL。
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *pmutex);
函数 pthread_mutex_destroy 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutex 是 POSIX 互斥信号量的指针。
互斥信号量的等待
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *pmutex);
int pthread_mutex_trylock(pthread_mutex_t *pmutex);
int pthread_mutex_timedlock(pthread_mutex_t *pmutex,
const struct timespec *abs_timeout);
int pthread_mutex_reltimedlock_np(pthread_mutex_t pmutex,
const struct timespec *rel_timeout);
以上函数原型分析:
- 函数成功返回 0,失败返回错误号。
- 参数 pmutex 是 POSIX 互斥信号量的指针。
- 参数 abs_timeout 是等待的绝对超时时间。
- 参数 rel_timeout 是等待的相对超时时间。
pthread_mutex_trylock 是 pthread_mutex_lock 的“尝试等待”版本,在 POSIX 互斥信号量已经被占有时,pthread_mutex_lock 将阻塞直至被唤醒,而 pthread_mutex_trylock 将立即返回。
pthread_mutex_timedlock 是 pthread_mutex_lock 的带等待超时时间的版本,abs_timeout 为等待的绝对超时时间,使用时在当前时间的基础上再加上一个相对超时时间就能得到绝对超时时间 abs_timeout。
pthread_mutex_reltimedlock_np 是 pthread_mutex_timedlock 的非 POSIX 标准版本,参数 rel_timeout 为等待的相对超时时间。
互斥信号量的释放
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *pmutex);
函数 pthread_mutex_unlock 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutex 是 POSIX 互斥信号量的指针。
设置和获取互斥信号量的天花板优先级
#include <pthread.h>
int pthread_mutex_setprioceiling(pthread_mutex_t *pmutex,
int prioceiling);
函数 pthread_mutex_setprioceiling 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutex 是 POSIX 互斥信号量的指针。
- 参数 prioceiling 是 POSIX 互斥信号量的天花板优先级。
#include <pthread.h>
int pthread_mutex_getprioceiling(pthread_mutex_t *pmutex,
int *prioceiling);
函数 pthread_mutex_getprioceiling 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 pmutex 是 POSIX 互斥信号量的指针。
- 输出参数 prioceiling 是 POSIX 互斥信号量的天花板优先级。
下面程序展示了 POSIX 互斥信号量的使用,程序创建两个线程和一个 POSIX 互斥信号量,两个线程分别对变量 count 进行自增操作并打印,使用 POSIX 互斥信号量作为访问变量 count 的互斥手段,其中互斥信号量使用优先级继承算法,并按优先级顺序等待。
程序清单 POSIX互斥信号量的使用(POSIX_Mutex_Semaphore)
#include <stdio.h>
#include <pthread.h>
static pthread_mutex_t lock;
static int count = 0;
static void *thread_a (void *arg)
{
while (1) {
pthread_mutex_lock(&lock);
count++;
printf("thread_a(): count = %d\n", count);
pthread_mutex_unlock(&lock);
usleep(500 * 1000);
}
return (NULL);
}
static void *thread_b (void *arg)
{
while (1) {
pthread_mutex_lock(&lock);
count++;
printf("thread_b(): count = %d\n", count);
pthread_mutex_unlock(&lock);
usleep(500 * 1000);
}
return (NULL);
}
int main (int argc, char *argv[])
{
pthread_mutexattr_t mutexattr;
pthread_t threada_tid;
pthread_t threadb_tid;
int ret;
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL);
pthread_mutexattr_setprotocol(&mutexattr, PTHREAD_PRIO_INHERIT);
ret = pthread_mutex_init(&lock, &mutexattr);
if (ret != 0) {
fprintf(stderr, "mutex create failed.\n");
return (-1);
}
pthread_mutexattr_destroy(&mutexattr);
ret = pthread_create(&threada_tid, NULL, thread_a, NULL);
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
ret = pthread_create(&threadb_tid, NULL, thread_b, NULL);
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
pthread_join(threada_tid, NULL);
pthread_join(threadb_tid, NULL);
pthread_mutex_destroy(&lock);
return (0);
}
在 SylixOS Shell 下运行程序:
# ./POSIX_Mutex_Semaphore
thread_a(): count = 1
thread_b(): count = 2
thread_a(): count = 3
thread_b(): count = 4
thread_a(): count = 5
thread_b(): count = 6
......
# ts
NAME TID PID PRI STAT LOCK SAFE DELAY PAGEFAILS FPU CPU
--------------------- ------- ----- --- ---- ---- ---- ---------- --------- --- ---
POSIX_Mutex_Semaphore 4010078 37 200 JOIN 0 0 1 USE 0
pthread 4010079 37 200 SLP 0 268 0 0
pthread 401007a 37 200 SLP 0 268 0 0
上面程序设置了互斥量属性的类型为 PTHREAD_MUTEX_NORMAL,该类型不会进行递归加锁的检查(如下图所示的一种可能的递归加锁),如果出现如下图所示的锁递归情况程序将发生死锁(我们将在“死锁”章节详细讨论死锁概念)。因此在实际的应用中这种类型的锁不建议使用,SylixOS 建议应该设置锁的类型为 PTHREAD_MUTEX_ERRORCHECK,该类型的锁在加锁时会自动检查锁的递归情况,如果出现锁递归将返回错误 EDEADLK。
程序清单 POSIX互斥信号量的使用(POSIX_Mutex_Semaphore)可以简化为以下伪代码,这段代码在不同的线程上下文对共享资源进行加锁操作,这样很好地避免了递归锁的出现。
thread_a ()
{
加锁(lock)
count++
解锁(lock)
}
thread_b ()
{
加锁(lock)
count++
解锁(lock)
}
main ()
{
创建锁(lock)
创建线程 thread_a thread_b
}