《深入理解Linux内核(第三版)》笔记(七),第五章内核同步(2)

信号量

// include/asm-i386/semaphore.h/line: 44
struct semaphore {
	atomic_t count;
	int sleepers;
	wait_queue_head_t wait;
};

// wait_queue_head_t 的介绍,在第三章进程的解析文档里介绍过。
// include/linux/wait.h/line: 51
struct __wait_queue_head {
	spinlock_t lock;
	struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
// 现在再看到自旋锁,就没有那么陌生和不安了。
// 用一个双向链表来组织
// Linux 中的双向链表可以组织各种数据结构

信号量的获取

可以想象,信号量往往是和资源绑定的,理论上应该是内核的全局变量。
而 down() 是在某个进程里被调用的,并且这个进程应该知道这个信号量。

// include/asm-i386/semaphore.h/line: 105
static inline void down(struct semaphore * sem)
{
	...
	"call __down_failed\n\t"
	...
}

// arch/i386/kernel/semaphore.c/line: 190
asm(
...
".globl __down_failed\n"
"__down_failed:\n\t"
	...
	"call __down\n\t"
	...
);
// arch/i386/kernel/semaphore.c/line: 57
fastcall void __sched __down(struct semaphore * sem)
{
	struct task_struct *tsk = current;	// 获取当前进程
	DECLARE_WAITQUEUE(wait, tsk);	// 创建一个名为 wait 的 wait_queue_t,并和当前进程绑定
									// 这一定是在内核态吗?使用的是进程的内核态的栈吗?
	unsigned long flags;

	tsk->state = TASK_UNINTERRUPTIBLE;
	spin_lock_irqsave(&sem->wait.lock, flags);
	// 《第三版》P102,互斥睡眠进程;把 wait_queue 放到 wait_queue_head 里
	add_wait_queue_exclusive_locked(&sem->wait, &wait);

	sem->sleepers++;	// 先预定自己睡眠在这个信号量上
	for (;;) {
		int sleepers = sem->sleepers;
		// atomic_add_negative() 是把第一个参数加到第二个参数上,如果是负数则返回1
		if (!atomic_add_negative(sleepers - 1, &sem->count)) {
			sem->sleepers = 0;	// 猜测这个是自旋锁的主要保护对象
			break;
		}
		sem->sleepers = 1;	/* us - see -1 above */
		spin_unlock_irqrestore(&sem->wait.lock, flags);

		schedule();

		spin_lock_irqsave(&sem->wait.lock, flags);
		tsk->state = TASK_UNINTERRUPTIBLE;
	}
	// 把 wati_queue 从 wait_queue_head 中移除
	remove_wait_queue_locked(&sem->wait, &wait);
	wake_up_locked(&sem->wait);
	spin_unlock_irqrestore(&sem->wait.lock, flags);
	tsk->state = TASK_RUNNING;
}
// include/linux/wait.h/line: 156
#define	wake_up_locked(x)	\
	__wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)

// kernel/sched.c/line: 2975
void fastcall __wake_up_locked(wait_queue_head_t *q, unsigned int mode)
{
	__wake_up_common(q, mode, 1, 0, NULL);
}

// kernel/sched.c/line: 2937
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
			     int nr_exclusive, int sync, void *key)
{
	struct list_head *tmp, *next;

	// for (tmp = (&q->task_list)->next, next = tmp->next;
	//	    tmp != (&q->task_list);
	//	    tmp = next, next = tmp->next)
	list_for_each_safe(tmp, next, &q->task_list) {
		wait_queue_t *curr;
		unsigned flags;
		curr = list_entry(tmp, wait_queue_t, task_list);
		flags = curr->flags;
		if (curr->func(curr, mode, sync, key) &&
		    (flags & WQ_FLAG_EXCLUSIVE) &&
		    !--nr_exclusive)
			break;
	}
}
// 此处的参数传入
__wake_up_common(&sem->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, 0, NULL)
// 现在没有追踪到 curr->func() 的指向,所以暂时不细究这个语句了。

信号量的释放

通过调用 up() 实现信号量的释放。

// include/asm-i386/semaphore.h/line: 180
static inline void up(struct semaphore * sem)
{
	...
	"call __up_wakeup\n\t"
	...
}

// arch/i386/kernel/semaphore.c/line: 253
asm(
".section .sched.text\n"
".align 4\n"
".globl __up_wakeup\n"
"__up_wakeup:\n\t"
	...
	"call __up\n\t"
	...
);

// arch/i386/kernel/semaphore.c/line: 52
fastcall void __up(struct semaphore *sem)
{
	wake_up(&sem->wait);
}
// include/linux/wait.h/line: 150
#define wake_up(x)	\
	__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)

// kernel/sched.c/line: 2960
void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,
				int nr_exclusive, void *key)
{
	unsigned long flags;

	spin_lock_irqsave(&q->lock, flags);
	__wake_up_common(q, mode, nr_exclusive, 0, key);
	spin_unlock_irqrestore(&q->lock, flags);
}
// spin_lock_irqsave() 和 spin_lock 类似,但是会中断;暂时存疑吧

读写信号量

// include/asm-i386/rwsem.h/line: 54
struct rw_semaphore {
	signed long		count;
#define RWSEM_UNLOCKED_VALUE		0x00000000
#define RWSEM_ACTIVE_BIAS		0x00000001
#define RWSEM_ACTIVE_MASK		0x0000ffff
#define RWSEM_WAITING_BIAS		(-0x00010000)
#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
	spinlock_t		wait_lock;
	struct list_head	wait_list;
#if RWSEM_DEBUG
	int			debug;
#endif
};

// lib/rwsem.c/line: 11
struct rwsem_waiter {		// 读写信号量的实体,挂到上面的那个 list_head 上
	struct list_head list;
	struct task_struct *task;
	unsigned int flags;
#define RWSEM_WAITING_FOR_READ	0x00000001
#define RWSEM_WAITING_FOR_WRITE	0x00000002
};
// include/asm-i386/rwsem.h/line: 85
static inline void init_rwsem(struct rw_semaphore *sem)
// 以下这几个函数都在这个文件中
// include/linux/rwsem.h
static inline void down_read(struct rw_semaphore *sem)
static inline int down_read_trylock(struct rw_semaphore *sem)
static inline void down_write(struct rw_semaphore *sem)
static inline int down_write_trylock(struct rw_semaphore *sem)
static inline void up_read(struct rw_semaphore *sem)
static inline void up_write(struct rw_semaphore *sem)

// downgrade write lock to read lock
static inline void downgrade_write(struct rw_semaphore *sem)
// include/linux/rwsem.h/line: 64
static inline void down_write(struct rw_semaphore *sem)
{
	might_sleep();
	rwsemtrace(sem,"Entering down_write");
	__down_write(sem);
	rwsemtrace(sem,"Leaving down_write");
}

// include/asm-i386/rwsem.h/line: 146
static inline void __down_write(struct rw_semaphore *sem)
{
	...
	"  call      rwsem_down_write_failed\n\t"
	...
}

// lib/rwsem.c/line: 202
struct rw_semaphore fastcall __sched *
rwsem_down_write_failed(struct rw_semaphore *sem)
{
	struct rwsem_waiter waiter;
	rwsemtrace(sem, "Entering rwsem_down_write_failed");
	waiter.flags = RWSEM_WAITING_FOR_WRITE;
	rwsem_down_failed_common(sem, &waiter, -RWSEM_ACTIVE_BIAS);
	rwsemtrace(sem, "Leaving rwsem_down_write_failed");
	return sem;
}

// lib/rwsem.c/line: 143
static inline struct rw_semaphore *
rwsem_down_failed_common(struct rw_semaphore *sem,
			struct rwsem_waiter *waiter, signed long adjustment)
// 没看太明白,赶进度,先不细看了

版权声明:本文为weixin_42346852原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。