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 等设备,这些额外功能是为了节约硬件成本,减少产品功耗,缩小产品形状,而集成到处理器内部的。