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