linux的互斥mutex

来源:互联网 发布:淘宝虚拟物品发货 编辑:程序博客网 时间:2024/06/05 14:29

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""msrcpsr_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_tlock;unsigned intcount;struct list_headwait_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 __schedwait_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 __scheddo_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
原创粉丝点击