信号量特点
1.由于竞争信号量的时候,未能拿到信号的进程会进入睡眠,所以信号量可以适用于长时间持有。
2.由于睡眠的特性,只能在进程上下文进行调用,无法再中断上下文中使用信号量。
3.期望去占用一个信号量的同时,不允许持有自旋锁,因为企图去获取信号量的时候,可能导致睡眠,而自旋锁不允许睡眠。
信号量的定义
1、semaphore 结构体
/* Please don't access any members of this structure directly */
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
2、semaphore 初始化
static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
3、获取semaphore
/**
* 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.
* If the semaphore is successfully acquired, this function returns 0.
*/
int down_interruptible(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_interruptible(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
EXPORT_SYMBOL(down_interruptible);
static noinline int __sched __down_interruptible(struct semaphore *sem)
{
return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
/*
* 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 semaphore_waiter waiter;
list_add_tail(&waiter.list, &sem->wait_list);
waiter.task = current;
waiter.up = false;
for (;;) {
if (signal_pending_state(state, current))
goto interrupted;
if (unlikely(timeout <= 0))
goto timed_out;
__set_current_state(state);
raw_spin_unlock_irq(&sem->lock);
timeout = schedule_timeout(timeout);
raw_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;
}
4、释放semaphore
/**
* 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;
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list)))
sem->count++;
else
__up(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);
信号量的使用
1、信号量的初始化
a) 静态初始化
可通过sema_init函数静态地声明信号量,其中name为信号量变量名,count是信号量的使用数量:
struct semaphore name;
sema_init(&name, count);
b) 动态初始化
static DECLARE_MUTEX(name);
2、获取信号量
void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_killable(struct semaphore *sem);
int down_timeout(struct semaphore *sem, long timeout);
3、释放信号量
void __up(struct semaphore *sem);
实例
drivers/video/fbdev/smscufx.c
struct urb_list {
......
struct semaphore limit_sem;
......
};
sema_init(&dev->urbs.limit_sem, i);
static struct urb *ufx_get_urb(struct ufx_data *dev)
{
int ret = 0;
struct list_head *entry;
struct urb_node *unode;
struct urb *urb = NULL;
unsigned long flags;
/* Wait for an in-flight buffer to complete and get re-queued */
ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT);
if (ret) {
atomic_set(&dev->lost_pixels, 1);
pr_warn("wait for urb interrupted: %x available: %d\n",
ret, dev->urbs.available);
goto error;
}
spin_lock_irqsave(&dev->urbs.lock, flags);
BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */
entry = dev->urbs.list.next;
list_del_init(entry);
dev->urbs.available--;
spin_unlock_irqrestore(&dev->urbs.lock, flags);
unode = list_entry(entry, struct urb_node, entry);
urb = unode->urb;
error:
return urb;
}
static void ufx_free_urb_list(struct ufx_data *dev)
{
int count = dev->urbs.count;
struct list_head *node;
struct urb_node *unode;
struct urb *urb;
int ret;
unsigned long flags;
pr_debug("Waiting for completes and freeing all render urbs\n");
/* keep waiting and freeing, until we've got 'em all */
while (count--) {
/* Getting interrupted means a leak, but ok at shutdown*/
ret = down_interruptible(&dev->urbs.limit_sem);
if (ret)
break;
spin_lock_irqsave(&dev->urbs.lock, flags);
node = dev->urbs.list.next; /* have reserved one with sem */
list_del_init(node);
spin_unlock_irqrestore(&dev->urbs.lock, flags);
unode = list_entry(node, struct urb_node, entry);
urb = unode->urb;
/* Free each separately allocated piece */
usb_free_coherent(urb->dev, dev->urbs.size,
urb->transfer_buffer, urb->transfer_dma);
usb_free_urb(urb);
kfree(node);
}
}
static void ufx_release_urb_work(struct work_struct *work)
{
struct urb_node *unode = container_of(work, struct urb_node,
release_urb_work.work);
up(&unode->dev->urbs.limit_sem);
}