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 等設備,這些額外功能是爲了節約硬件成本,減少產品功耗,縮小產品形狀,而集成到處理器內部的。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章