信号量
Linux提供两种信号量:
- 内核信号量,由内核控制路径使用;
- System V IPC信号量,由用户态进程使用;
内核信号量
1、内核信号量类似于自旋锁,当锁关闭时,不允许内核控制路径继续前进;
2、当内核控制路径想要获取内核信号量所保护的资源时,若资源被占用,则当前进程被挂起,等待;
3、当资源释放时,选择一个或多个等待该资源的进程唤醒,继续执行;
4、只有可以睡眠的函数才能过获取内核信号量;中断或可延迟函数不能使用内核信号量。
内核信号量数据结构:
/*Please don't access any members of this structure directly*/
struct semaphore {
spinlock_t lock;
unsigned count; // 大于0资源空闲;等于0,信号量忙,但无进程等待;小于0,资源不可用,且至少有一个进程等待;
struct list_head wait_list;//等待队列链表,所有等待次资源的进程均放在这个链表;
};
通过init_MUTEX和init_MUTEX_LOCKED来初始化互斥访问所需的信号量:这两个宏,分别把count设置为1和0;
获取和释放一个信号量
1、释放一个信号量
/**
* up- release the semaphore
* @sem: the semaphore to release
* Release the semaphore. Unlike mutexes, up() may be called from any context and even by tasks which have never called down;
**/
void up(struct semaphore *sem) {
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list)))
sem->count++;
else
__up(sem);
spin_unlock_irqrestore(&sem->lock, flags);
}
等价汇编如下:
movl &sem->count, %ecx
lock; incl (%ecx)
jg 1f//判断count是否大于0,若大于0,则表示没有进程等待该资源,结束;若小于0,则需要做更多操作,如唤醒等待该资源的进程;
lea %ecx, %eax
pushl %edx
pushl %ecx
call __up // __up唤醒进程,__up从eax寄存器接受参数;
popl %ecx
popl %edx
1:
__up函数源码:
static noinline void __sched __up(struct semaphore *sem) {
struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
struct semaphore_waiter, list); //将wait_list强制转为waiter
list_del(&waiter->list);// 删除当前节点元素,并将当前元素的前后节点设置为特定值
waiter->up = 1;// 设置唤醒数
wake_up_process(waiter->task); //这里是如何获取进程描述符的?这里由获取信号量的进程填充。
}
其中:list_first_entry调用层级如下:
/**
* list_first_entry - get the first element from a list;
* @ptr: the list head to take the element from
* @type: the type of the struct this is embedded from
* @member: the name of the list_struct within the struct
* Note, that list is expected to be not empty;
*/
#define list_first_entry(ptr, type, member) list_entry((ptr)->next, type, member)
/**
* list_entry - get the struct for this entry 从此项获取结构
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) container_of(ptr, type, member)
/**
* container_of - cast a member of a structure out to the containing structure 将结构的成员强制转换为其上级结构
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*/
#define container_of(ptr, type member) ({
ptr = (&sem->wait_list)->next ⇒ list_head
const typeof(((type *)0)->member) * __mptr = (ptr);
// 1、将0强制转换为type(struct semaphore_waiter)指针类型,获取member(list)的类型(list_head); const typeof(((struct semaphore_waiter *)0)->list) * ⇒ list_head
// 2、__mptr = list_head;
(type *)((char *)__mptr - offsetof(type, member));}) // 结果就是:获取type结构的起始地址。
offsetof(type, member) ((size_t) &((type *)0)->member) //取出list在(struct semaphore_waiter)中的偏移量;
1、获取一个信号量
down获取信号量分不同情况,如下:
down
```
/**
* down - acquire the semaphore
* @sem: the semaphore to be acquired
*
* Acquires the semaphore. If no more tasks are allowed to acquire the
* semaphore, calling this function will put the task to sleep until the
* semaphore is released.
*
* Use of this function is deprecated, please use down_interruptible() or
* down_killable() instead.
*/
void down(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
__down(sem);
spin_unlock_irqrestore(&sem->lock, flags);
}
/**
* down_interruptible - acquire the semaphore unless interrupted 设备驱动中被广泛使用;
* @sem: the semaphore to be acquired
*
* Attempts to acquire the semaphore. If no more tasks are allowed to
* acquire the semaphore, calling this function will put the task to sleep.
* If the sleep is interrupted by a signal, this function will return -EINTR. 返回值时EINTR时,可以放弃I/O操作
* If the semaphore is successfully acquired, this function returns 0.
*/
int down_interruptible(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_interruptible(sem);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
```
/**
* down_killable - acquire the semaphore unless killed
* @sem: the semaphore to be acquired
*
* Attempts to acquire the semaphore. If no more tasks are allowed to
* acquire the semaphore, calling this function will put the task to sleep.
* If the sleep is interrupted by a fatal signal, this function will return
* -EINTR. If the semaphore is successfully acquired, this function returns
* 0.
*/
int down_killable(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_killable(sem);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
/**
* down_trylock - try to acquire the semaphore, without waiting 异步函数可安全使用;
* @sem: the semaphore to be acquired
*
* Try to acquire the semaphore atomically. Returns 0 if the mutex has
* been acquired successfully or 1 if it it cannot be acquired.
*
* NOTE: This return value is inverted from both spin_trylock and
* mutex_trylock! Be careful about this when converting code.
*
* Unlike mutex_trylock, this function can be used from interrupt context,
* and the semaphore can be released by any task or interrupt.
*/
int down_trylock(struct semaphore *sem)
{
unsigned long flags;
int count;
spin_lock_irqsave(&sem->lock, flags);
count = sem->count - 1;
if (likely(count >= 0))
sem->count = count;
spin_unlock_irqrestore(&sem->lock, flags);
return (count < 0);
}
/**
* down_timeout - acquire the semaphore within a specified time
* @sem: the semaphore to be acquired
* @jiffies: how long to wait before failing
*
* Attempts to acquire the semaphore. If no more tasks are allowed to
* acquire the semaphore, calling this function will put the task to sleep.
* If the semaphore is not released within the specified number of jiffies,
* this function returns -ETIME. It returns 0 if the semaphore was acquired.
*/
int down_timeout(struct semaphore *sem, long jiffies)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_timeout(sem, jiffies);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
/*
* Because this function is inlined, the 'state' parameter will be
* constant, and thus optimised away by the compiler. Likewise the
* 'timeout' parameter for the cases without timeouts.
*/
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
struct task_struct *task = current; // 获取当前进程描述符
struct semaphore_waiter waiter; //new一个等待结构体
list_add_tail(&waiter.list, &sem->wait_list); //将waiter.list插入到wait_list尾部
waiter.task = task; //设置
waiter.up = 0; //设置为等待唤醒
for (;;) {
if (signal_pending_state(state, task)) //若state状态为TASK_INTERRUPTIBLE 或TASK_WAKEKILL,则设置进程为挂起状态;
goto interrupted;
if (timeout <= 0)若超时小于0
goto timed_out;
__set_task_state(task, state); //设置任务状态值;
spin_unlock_irq(&sem->lock); // 进入调度之前,需要将锁打开;
timeout = schedule_timeout(timeout); //休眠,直到超时时间到,则唤醒;进程调度
spin_lock_irq(&sem->lock);
if (waiter.up)
return 0;
}
timed_out:
list_del(&waiter.list);
return -ETIME;
interrupted:
list_del(&waiter.list);
return -EINTR;
}
//如下信号量获取函数,均是通过相应标志与超时时间调用__down_common函数;
static noinline void __sched __down(struct semaphore *sem)
{
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
static noinline int __sched __down_interruptible(struct semaphore *sem)
{
return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
static noinline int __sched __down_killable(struct semaphore *sem)
{
return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
}
static noinline int __sched __down_timeout(struct semaphore *sem, long jiffies)
{
return __down_common(sem, TASK_UNINTERRUPTIBLE, jiffies);
}