linux的互斥mutex

1.原子操作
整型原子操作
void atomic_set(atomic_t * v , int i); 
atomic_t v= ATOMIC_INIT(0); 
atomic_read(atomic_t *v); 
 atomic_add(int i, atomic_t *v); 
  atomic_sub(int i , atomic_t *v); 
  atomic_inc(atomic_t *v); 
  atomic_dec(atomic_t *v); 
    int atomic_inc_and_test(atomic *v); 
  int atomic_dec_and_test(atomic *v); 
  int atomic_sub_and_test(int i ,atomic *v); 
    int atomic_add_return(int i ,atomic *v); 
  int atomic_sub_return(int i,atomic *v); 
  int atomic_inc_return(atomic *v); 
  int atomic_dec_return(atomic *v); 
    原子锁的定义如下:
typedef struct {<span style="white-space:pre"></span>int counter; 
} atomic_t; 
  说白了就是一个整型的变量。    
  个人觉得原子锁其实就是自己给自己加了标志罢了。也没有做什么特殊的处理。  在互斥上的时候,处于忙等待,我想也就是调用了后面3个function的原因罢了。
  后面3个函数在不同平台的定义也不同。 
  如在arm上:



#define atomic_add(i, v)	(void) atomic_add_return(i, v)
#define atomic_inc_and_test(v)	(atomic_add_return(1, v) == 0)
static inline int atomic_add_return(int i, atomic_t *v)
{
	unsigned long flags;
	int val;


	<span style="color:#ff0000;">raw_local_irq_save(flags);</span>
	val = v->counter;
	v->counter = val += i;
	<span style="color:#ff0000;">raw_local_irq_restore(flags);</span>


	return val;
}

#define raw_local_irq_save(flags)			\
	do {						\
		typecheck(unsigned long, flags);	\
		flags = arch_local_irq_save();		\
	} while (0)

/*
 * Save the current interrupt enable state & disable IRQs
 */
static inline unsigned long arch_local_irq_save(void)
{
	unsigned long flags, temp;


	asm volatile(
		"	mrs	%0, cpsr	@ arch_local_irq_save\n"
		"	orr	%1, %0, #128\n"
		"	msr	cpsr_c, %1"
		: "=r" (flags), "=r" (temp)
		:
		: "memory", "cc");
	return flags;
}

位原子操作


void set_bit(nr,addr);

void clear_bit(nr,addr);

void change_bit(nr,addr);

int test_bit(nr ,addr);

以下几个函数的先返回后修改:

去addr地址上的内容 , 从小端开始,第nr位置1,或者清零。

int test_and_set_bit(nr,addr);

int test_and_clear_bit(nr, addr);

int test_and_change_bit (nr, addr );


2. 信号量


1. 直接创建信号量  -----------》进入睡眠

vod sema_init(struct semaphore * sem , int vol);


DECLARE_MUTEX(name); 信号量为1

DECLARE_MUTEX_UNLOCK(name ); 信号量为0

down(sturct semaphore *sem);

up(sturct semaphore *sem);



首先 信号量的定义如下:

/* Please don't access any members of this structure directly */
struct semaphore {
	raw_spinlock_t		lock;
	unsigned int		count;
	struct list_head	wait_list;
};
以上可以看出,信号量的成员是不可以直接访问的,访问需要用别的函数
如:

extern void down(struct semaphore *sem);
extern int __must_check down_interruptible(struct semaphore *sem);
extern int __must_check down_killable(struct semaphore *sem);
extern int __must_check down_trylock(struct semaphore *sem);
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);
extern void up(struct semaphore *sem);
我们以常用的获取信号量down和释放信号量Up为例子:

/**
 * 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;

	raw_spin_lock_irqsave(&sem->lock, flags);
	if (likely(sem->count > 0))
		sem->count--;
	else
		__down(sem);
	raw_spin_unlock_irqrestore(&sem->lock, flags);
}

主要就是对semaphore 结构体里的count计数, 一般count为几,就代表可以有多少线程持有该信号量。

再来细看_down 里的操作是如何让线程睡眠的:



static noinline void __sched __down(struct semaphore *sem)
{
	__down_common(sem, TASK_UNINTERRUPTIBLE, 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 task_struct *task = current;
	struct semaphore_waiter waiter;

	list_add_tail(&waiter.list, &sem->wait_list);
	waiter.task = task;
	waiter.up = 0;

	for (;;) {
		if (signal_pending_state(state, task))
			goto interrupted;
		if (timeout <= 0)
			goto timed_out;
		__set_task_state(task, 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);<pre name="code" class="cpp">
 从以上基本就可以看出了,如果信号量count=0,线程会设置状态为不可中断的睡眠状态,然后被调度出去。 

具体就是 :

先把信号量 及 需要获得信号量的线程加到信号等待的列表及task里,然后对线程设置好不可中断的状态,

然后再来调度出去。

再来看看up是怎么做的呢?

* 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;

	<pre name="code" class="cpp">
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);}
 

static noinline void __sched __up(struct semaphore *sem)
{
	struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
						struct semaphore_waiter, list);
	list_del(&waiter->list);
	waiter->up = 1;
	wake_up_process(waiter->task);
}
up比较简单,一旦某个信号量释放了,就去立刻到信号量等待的链表里去检索这个信号量,

当检测到信号量后,就会将其从链表中删除,然后唤醒等待的task,去唤醒之前沉睡的线程。

以上思想还是比较简单的。不过也是Linux 内核的精髓。很巧妙。


以上我们已经多次遇到了这个语句了:

raw_spin_lock_irqsave(&sem->lock, flags);
<pre name="code" class="cpp">raw_spin_unlock_irqrestore(&sem->lock, flags);
 
这2句看名字就能知道什么意思,但是最终最终下去,还是要换成ARM或者其他平台的指令的。
追踪这个代码下去,发现了如下的代码:
static inline void arch_local_irq_enable(void)
{
	asm volatile(
		"	cpsie i			@ arch_local_irq_enable"
		:
		:
		: "memory", "cc");
}

static inline void arch_local_irq_disable(void)
{
	asm volatile(
		"	cpsid i			@ arch_local_irq_disable"
		:
		:
		: "memory", "cc");
}

这个已经够明白的了吧。CPSID I 关中断。CPSIE 开中断。(这两个汇编指令,还是请自觉去看看ARM指令之类的datasheet吧。)


2.读写信号量 rw_semaphore

struct rw_semaphore

void init_rwsem(struct rw_semaphore *sem);


当某个给定写入者试图进入临界区时,在所有写入者完成其工作前,不会允许读取者获得访问。

这个目前,我是还有怎么遇到,等日后遇到了我们再说吧。



3. 自旋锁

任何拥有自旋锁的代码都必须是原子的,他不能因为任何原因放弃处理器。

spinlock_t mylock;

void spin_lock_init(spinlock_t *lock);

void spin_lock(spinlock_t *lock);

void spin_unlock(spinlock_t *lock);



自选锁的定义如下:

typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;
typedef struct raw_spinlock {
<span style="white-space:pre">	</span>arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
<span style="white-space:pre">	</span>unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
<span style="white-space:pre">	</span>unsigned int magic, owner_cpu;
<span style="white-space:pre">	</span>void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
<span style="white-space:pre">	</span>struct lockdep_map dep_map;
#endif
} raw_spinlock_t;
typedef struct {
<span style="white-space:pre">	</span>volatile unsigned char lock;
} arch_spinlock_t;
说到底还是一个无符号字符变量。


那么看看spin_lock()是怎么工作的吧:

static inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}
#define raw_spin_lock(lock)<span style="white-space:pre">	</span>_raw_spin_lock(lock)
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
<span style="white-space:pre">	</span>preempt_disable();
<span style="white-space:pre">	</span>spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
<span style="white-space:pre">	</span>LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
到这里就可以了,不往下看了,看多了就会伤身体。会涉及到一堆调度的问题,这里我们不过多研究。

这里有一个禁止抢占的函数,也就是说,自旋锁锁上以后,CPU就只能被这个线程占有了,无法调度出去。

如果同一个线程在同一个CPU上获得2次相同的锁,那么就会让CPU直接挂掉,导致kernel panic。

在CPU 锁好后,会给自旋锁的计数值做减1操作。

那么spin_unlock呢?

他与spin_lock正好相反:

static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
	spin_release(&lock->dep_map, 1, _RET_IP_);
	do_raw_spin_unlock(lock);
	preempt_enable();
}

先给自旋锁的计数值加上1 , 然后再释放CPU。

知道自旋锁怎么用就好了,本人肤浅,跟到这里就不想往下了。

sededxcsszsaszaaz  

关于死锁:


a. 自旋锁可能导致系统死锁。递归一个自旋锁,如果一个已经 aa de




PS:最近买了块TQ2440的板子,回头真正做到汇编指令级别的,再往下研究吧。


4.完成量 completion 


DECLARE_COMPLETION(my_completion);


void wait_for_completion (struct  completion *c);

void complete(struct  completion *c)

void complete_all(struct  completion *c)




首先来看看completion的定义吧:

struct completion {
	unsigned int done;
	wait_queue_head_t wait;
};
struct __wait_queue_head {
<span style="white-space:pre">	</span>spinlock_t lock;
<span style="white-space:pre">	</span>struct list_head task_list;
};

然后看看完成量的使用:

 * This waits to be signaled for completion of a specific task. It is NOT
 * interruptible and there is no timeout.
 *
 * See also similar routines (i.e. wait_for_completion_timeout()) with timeout
 * and interrupt capability. Also see complete().
 */
void __sched wait_for_completion(struct completion *x)
{
	wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE, 0);
}
static long __sched
wait_for_common(struct completion *x, long timeout, int state, int iowait)
{
<span style="white-space:pre">	</span>might_sleep();


<span style="white-space:pre">	</span>spin_lock_irq(&x->wait.lock);
<span style="white-space:pre">	</span>timeout = do_wait_for_common(x, timeout, state, iowait);
<span style="white-space:pre">	</span>spin_unlock_irq(&x->wait.lock);
<span style="white-space:pre">	</span>return timeout;
}
static inline long __sched
do_wait_for_common(struct completion *x, long timeout, int state, int iowait)
{
<span style="white-space:pre">	</span>if (!x->done) {
<span style="white-space:pre">		</span>DECLARE_WAITQUEUE(wait, current);


<span style="white-space:pre">		</span>__add_wait_queue_tail_exclusive(&x->wait, &wait);
<span style="white-space:pre">		</span>do {
<span style="white-space:pre">			</span>if (signal_pending_state(state, current)) {
<span style="white-space:pre">				</span>timeout = -ERESTARTSYS;
<span style="white-space:pre">				</span>break;
<span style="white-space:pre">			</span>}
<span style="white-space:pre">			</span>__set_current_state(state);
<span style="white-space:pre">			</span>spin_unlock_irq(&x->wait.lock);
<span style="white-space:pre">			</span>if (iowait)
<span style="white-space:pre">				</span>timeout = io_schedule_timeout(timeout);
<span style="white-space:pre">			</span>else
<span style="white-space:pre">				</span>timeout = schedule_timeout(timeout);
<span style="white-space:pre">			</span>spin_lock_irq(&x->wait.lock);
<span style="white-space:pre">		</span>} while (!x->done && timeout);
<span style="white-space:pre">		</span>__remove_wait_queue(&x->wait, &wait);
<span style="white-space:pre">		</span>if (!x->done)
<span style="white-space:pre">			</span>return timeout;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>x->done--;
<span style="white-space:pre">	</span>return timeout ?: 1;
}
也是通过调度来完成的,不过觉得实时性可能要差点。






平台设备是指处理器上集成的额外功能的附加设备,如watchdog ,i2c,i2s,rtc,adc 等设备,这些额外功能是为了节约硬件成本,减少产品功耗,缩小产品形状,而集成到处理器内部的。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值