文件位置:bionic/libc/bionic/semaphore.c
头文件位于bionic/libc/include/semaphore.h中
下面是头文件定义的接口:
__BEGIN_DECLS
#define SEM_FAILED NULL
extern int sem_init(sem_t *sem, int pshared, unsigned int value);
extern int sem_close(sem_t *);
extern int sem_destroy(sem_t *);
extern int sem_getvalue(sem_t *, int *);
extern int sem_init(sem_t *, int, unsigned int);
extern sem_t *sem_open(const char *, int, ...);
extern int sem_post(sem_t *);
extern int sem_trywait(sem_t *);
extern int sem_unlink(const char *);
extern int sem_wait(sem_t *);
struct timespec;
extern int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
__END_DECLS
#endif /* _SEMAPHORE_H */
0. 信号量sem_t
先看结构:
typedef struct {
volatile unsigned int count;
} sem_t;
32位无符号整形;
其中第一位表示shared flag
其他31位表示counter
下面的文字描述:
value值:
-1表示正在等待信号,抢夺资源中;
0或者大于0表示处于非等待状态;
-2表示运行时无效;
/* In this implementation, a semaphore contains a
* 31-bit signed value and a 1-bit 'shared' flag
* (for process-sharing purpose).
*
* We use the value -1 to indicate contention on the
* semaphore, 0 or more to indicate uncontended state,
* any value lower than -2 is invalid at runtime.
*
* State diagram:
*
* post(1) ==> 2
* post(0) ==> 1
* post(-1) ==> 1, then wake all waiters
*
* wait(2) ==> 1
* wait(1) ==> 0
* wait(0) ==> -1 then wait for a wake up + loop
* wait(-1) ==> -1 then wait for a wake up + loop
*
*/
/* Use the upper 31-bits for the counter, and the lower one
* for the shared flag.
*/
1. __sem_dec
将信号值原子减1,然后返回先前的旧值。
/* Decrement a semaphore's value atomically,
* and return the old one. As a special case,
* this returns immediately if the value is
* negative (i.e. -1)
*/
static int
__sem_dec(volatile unsigned int *pvalue)
{
unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
unsigned int old, new;
int ret;
do {
old = (*pvalue & SEMCOUNT_VALUE_MASK);
ret = SEMCOUNT_TO_VALUE(old);
if (ret < 0)
break;
new = SEMCOUNT_DECREMENT(old);
}
while (__bionic_cmpxchg((int)(old|shared),
(int)(new|shared),
(volatile int *)pvalue) != 0);
return ret;
}
先将old去出来,然后右移一位,得到counter值,(因为第一位是flag);
若得到的ret < 0,那么跳出循环,返回;否则就减去1;
2. __sem_inc
原子加1,返回旧值
/* "Increment" the value of a semaphore atomically and
* return its old value. Note that this implements
* the special case of "incrementing" any negative
* value to +1 directly.
*
* NOTE: The value will _not_ wrap above SEM_VALUE_MAX
*/
static int
__sem_inc(volatile unsigned int *pvalue)
{
unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
unsigned int old, new;
int ret;
do {
old = (*pvalue & SEMCOUNT_VALUE_MASK);
ret = SEMCOUNT_TO_VALUE(old);
/* Can't go higher than SEM_MAX_VALUE */
if (ret == SEM_MAX_VALUE)
break;
/* If the counter is negative, go directly to +1,
* otherwise just increment */
if (ret < 0)
new = SEMCOUNT_ONE;
else
new = SEMCOUNT_INCREMENT(old);
}
while ( __bionic_cmpxchg((int)(old|shared),
(int)(new|shared),
(volatile int*)pvalue) != 0);
return ret;
}
取出old,然后取出counter, 若ret达到最大值了,则返回,
若ret < 0 , 那么将new 设置为1;
否则,在old之上加1;
3. __sem_trydec
跟2相似,只是如果value已经小于或者等于0,那么返回old,不做任何其他事情;
/* Same as __sem_dec, but will not touch anything if the
* value is already negative *or* 0. Returns the old value.
*/
static int
__sem_trydec(volatile unsigned int *pvalue)
{
unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
unsigned int old, new;
int ret;
do {
old = (*pvalue & SEMCOUNT_VALUE_MASK);
ret = SEMCOUNT_TO_VALUE(old);
if (ret <= 0)
break;
new = SEMCOUNT_DECREMENT(old);
}
while (__bionic_cmpxchg((int)(old|shared),
(int)(new|shared),
(volatile int *)pvalue) != 0);
return ret;
}
与2不同点似乎就在于,如果ret == 0了,它也立即返回了。并不做-1操作;
4. sem_timedwait
计时等待,调用了__futex_wait_ex
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
{
int ret;
unsigned int shared;
if (sem == NULL) {
errno = EINVAL;
return -1;
}
/* POSIX says we need to try to decrement the semaphore
* before checking the timeout value. Note that if the
* value is currently 0, __sem_trydec() does nothing.
*/
if (__sem_trydec(&sem->count) > 0) {
ANDROID_MEMBAR_FULL();
return 0;
}
/* Check it as per Posix */
if (abs_timeout == NULL ||
abs_timeout->tv_sec < 0 ||
abs_timeout->tv_nsec < 0 ||
abs_timeout->tv_nsec >= 1000000000)
{
errno = EINVAL;
return -1;
}
shared = SEM_GET_SHARED(sem);
for (;;) {
struct timespec ts;
int ret;
/* Posix mandates CLOCK_REALTIME here */
clock_gettime( CLOCK_REALTIME, &ts );
ts.tv_sec = abs_timeout->tv_sec - ts.tv_sec;
ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec;
if (ts.tv_nsec < 0) {
ts.tv_nsec += 1000000000;
ts.tv_sec -= 1;
}
if (ts.tv_sec < 0 || ts.tv_nsec < 0) {
errno = ETIMEDOUT;
return -1;
}
/* Try to grab the semaphore. If the value was 0, this
* will also change it to -1 */
if (__sem_dec(&sem->count) > 0) { //如果是0,那么这个操作可能将其变为-1,不过先减去1
ANDROID_MEMBAR_FULL();
break;
}
/* Contention detected. wait for a wakeup event */
ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts);
/* return in case of timeout or interrupt */
if (ret == -ETIMEDOUT || ret == -EINTR) {
errno = -ret;
return -1;
}
}
return 0;
}
5. sem_wait
等待,先减去1,然后wait事件唤醒,__futex_wait_ex;
/* lock a semaphore */
int sem_wait(sem_t *sem)
{
unsigned shared;
if (sem == NULL) {
errno = EINVAL;
return -1;
}
shared = SEM_GET_SHARED(sem);
for (;;) {
if (__sem_dec(&sem->count) > 0)
break;
__futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL);
}
ANDROID_MEMBAR_FULL();
return 0;
}
6. sem_post
先将信号值+1,然后发送信号,唤醒;切记这次序
/* Unlock a semaphore */
int sem_post(sem_t *sem)
{
unsigned int shared;
int old;
if (sem == NULL)
return EINVAL;
shared = SEM_GET_SHARED(sem);
ANDROID_MEMBAR_FULL();
old = __sem_inc(&sem->count); //加1
if (old < 0) {
/* contention on the semaphore, wake up all waiters */
__futex_wake_ex(&sem->count, shared, INT_MAX); //唤醒
}
else if (old == SEM_MAX_VALUE) {
/* overflow detected */
errno = EOVERFLOW;
return -1;
}
return 0;
}