1. 信号量

信号量(semaphone)是保护临界区的一种常用方法。当一个数据可能被多个进程访问,但是同时只能被一个进程访问,这时会用到信号量对该数据作一个保护。

一般会将该信号量和被保护的数据组合在一起形成一个结构体,如:

Struct globalmem_dev

{

Struct cdev cdev;

Unsigned char mem[SIZE];

Struct semaphore sem;

}

信号量的初始化:

Init_MUTEX(&globalmem_devp->sem);将一个互斥的信号量初始化为1,当然也可以设定为大于1的值,使用sema_init(sem, val)。注意参数是一个指针,指向信号量变量的地址。因为要对该信号量进行修改访问。

信号量的关键参数为count。因为一直只允许一个进程访问,所以有一个锁lock要对count操作进行保护。__SPIN_LOCK_UNLOCKED((name).lock),在初始化时候为unlock状态。

struct semaphore {

spinlock_tlock;

unsigned intcount;

struct list_headwait_list;

};

#define init_MUTEX(sem)sema_init(sem, 1)

#define __SEMAPHORE_INITIALIZER(name, n)\

{\

.lock= __SPIN_LOCK_UNLOCKED((name).lock),\

.count= n,\

.wait_list= LIST_HEAD_INIT((name).wait_list),\

}

LIST_HEAD_INIT((name).wait_list),为初始化一个等待队列头。等待队列头为一个双向链表的头,prevnext都为name的地址

#define LIST_HEAD_INIT(name) { &(name), &(name) }

获得信号量

/**

* 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);//会导致该进程睡眠,直到释放该信号量。而且不能被信号唤醒。不建议使用。

int __must_check down_interruptible(struct semaphore *sem);

int down_interruptible(struct semaphore *sem)

{

unsigned long flags;

int result = 0;

spin_lock_irqsave(&sem->lock, flags);

if (likely(sem->count > 0))

sem->count--;

else

result = __down_interruptible(sem);

spin_unlock_irqrestore(&sem->lock, flags);

return result;

}

down_interruptible如果获得信号量即count 值大于零,将count--,且返回值为零。

释放信号量

/**

* up - release the semaphore

* @sem: the semaphore to release

*

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

spin_lock_irqsave(&sem->lock, flags);

if (likely(list_empty(&sem->wait_list)))

sem->count++;

else

__up(sem);

spin_unlock_irqrestore(&sem->lock, flags);

}

实际上对信号量中间的count操作前也要做获取自旋锁的操作。操作完count之后解锁。

2. 自旋锁

也是对临界资源进行互斥访问的典型手段。为了获取一个自旋锁,在CPU上运行的代码先执行一个原子操作,测试并设置某个内存变量。如果锁已经空闲,则获得这个自旋锁并继续执行。如果被占用,则在一个小的循环内重复这个测试并设置的操作,即所谓的自旋。直到该自旋锁被其它持有者释放。

定义自旋锁

Spinlock_t lock

初始化自旋锁

Spin_lock_init(&lock);

void __spin_lock_init(spinlock_t *lock, const char *name,

struct lock_class_key *key)

{

#ifdef CONFIG_DEBUG_LOCK_ALLOC

/*

* Make sure we are not reinitializing a held lock:

*/

debug_check_no_locks_freed((void *)lock, sizeof(*lock));

lockdep_init_map(&lock->dep_map, name, key, 0);

#endif

lock->raw_lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;

lock->magic = SPINLOCK_MAGIC;

lock->owner = SPINLOCK_OWNER_INIT;

lock->owner_cpu = -1;

}

初始化其关键字为0lock->raw_lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;

获得自旋锁

Spin_lock(lock)

#define __LOCK(lock) \

do { preempt_disable(); __acquire(lock); (void)(lock); } while (0) 

# define __acquire(x)__context__(x,1)

Preempt_disable()应该是禁止中断,保持对cpu的独占

__context__(x,1)不断地对x进行测试,看值是否为0,如果为0,则获得锁,并加1。如果不为0则不断地进行测试等待操作。

Spin_trylock(&lock) 如果不能获得,立刻返回

释放自旋锁

Spin_unlock()

#define __UNLOCK(lock) \

do { preempt_enable(); __release(lock); (void)(lock); } while (0)

# define __release(x)__context__(x,-1)

本质上是进行一个-1操作。

3. 自旋锁信号量

信号量:进程级,用于多个进程对资源访问的互斥。如果没能获得资源,会发生进程上下文切换。当前进程会睡眠挂起,直到资源能够访问。当然这需要结合等待队列来进行。进程上下文切换开销比较大,只有当进程占用资源比较长时间时,用信号量才是较好的选择

自旋锁:当需要保护的临界区访问时间比较短时,用自旋锁很方便,可以节省进程上下文切换的时间。但是锁不能再临界区长时间停留,否则会降低系统效率。

可能引起阻塞的代码不能用自旋锁。否则会引起死锁。

在中断的情况下保护资源,自能使用自旋锁,避免阻塞。