内核信号量--来自深入理解Linux内核

信号量

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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值