/**原子变量操作是Linux的一种简单同步机制,是一种在操作过程中不会被打断的操作.***/
/****其API和原子类型定义在include/asm/atomic.h文件中,使用汇编实现
****优点是编写简单;缺点是功能太简单,只能做计数操作,保护的东西太少,
***/
/*原子变量类型为atomic_t,atomic_t实际上是一个int类型的值,但由于处理器限制,
只能表示24位数的范围, 低8位设置成一个锁.*/
typedef struct{
volatile int counter;
}atomic_t;
/*atomic_t类型初始化使用如下宏*/
atomic_t count = ATOMIC_INIT(0);
/*设置atomic_t变量的值使用如下宏*/
#define atomic_tset(v,i) (((v)->counter)=i)
/*读取atomic_t变量的值使用如下宏*/
#define atomic_read(v) ((v)->counter)
/*原子变量加减法,使用如下函数*/
atomic_add(int i, volatile atomic_t *v); //将i值加到v中
atomic_sub(int i, volatile atomic_t *v); //从v中减去i值
static inline void atomic_inc(volatile atomic_t *v); //将v指向的变量加1
static inline void atomic_dec(volatile atomic_t *v); //将v指向的变量减1
/*原子位操作,原子位操作不需要定义一个类似stomic_t的变量,只需要一个普通的变量指针*/
static inline void set_bit(int nr, volatile unsigned long *addr); //将addr变量的第nr位设置为1
static inline void clear_bit(int nr, volatile unsigned long *addr); //将addr变量的第nr位设置为0
static inline void change_bit(int nr, volatile unsigned long *addr); //将addr变量的第nr位设置为相反数
static inline int test_and_set_bit(int nr, volatile unsigned long *addr); //将addr变量的第nr位设置为1,并返回未修改之前的值
static inline int test_and_clear_bit(int nr, volatile unsigned long *addr); //将addr变量的第nr位设置为0,并返回未修改之前的值
static inline int test_and_change_bit(int nr, volatile unsigned long *addr); //将addr变量的第nr位设置为相反数,并返回未修改之前的值
/*单独的原子操作不能满足复杂的内核设计需要,Linux提供一些锁机制来避免竞争条件,最简单的一种就是自旋锁*/
/*自旋锁是一种简单的并发控制机制,其是实现信号量和完成量的基础*/
/*定义和初始化自旋锁*/
spinlocak_t lock = SPIN_LOCK_UNLOCKED; //编译阶段通过宏来实现
void spin_lock_init(spinlock_t lock); //使用函数动态地初始化
/*锁定自旋锁或叫获得自旋锁*/
//如果能够获得自旋锁则宏立刻返回;否则,这个锁会一直自旋在那里,直到该锁被释放
#define spin_lock(lock) _spin_lock(lock);
/*释放自旋锁*/
#define spin_unlock(lock) _spin_unlock(lock) //释放lock自旋锁,调用该宏之后,锁立刻被释放
/****自旋锁等待不是讲进程挂起,而是不断地循环条件是否满足,满足则解锁,
*****所以自旋锁对系统性能有影响。程序不应该长时间持有自旋锁,它适合
*****短时间锁定的轻量级的轻量级加锁机制.*/
/***信号量只有当得到信号量的继承或者线程时才能够进入临界区.与自旋锁不同,
当进程不能获得信号量时,进程将自己加入等待队列睡眠,直到信号量被释放,进程才被唤醒.***/
/*信号量结构体*/
struct semaphore{
spinlock_t lock; //用来对count变量起保护作用,当count要变化时,内部会锁定lock锁,修改完之后再释放锁
unsigned int count; //等于0值,表示信号量被其他进程使用,现在不可用,但是wait_list队列中没有进程在等待信号量
//小于0值,表示至少有一个进程在wait_list队列中等待信号量
//大于0值,表示信号量是空闲的,程序可以使用这个信号量
struct list_head wait_list; //等待队列链表头,存放正在睡眠的进程链表
}
/*定义和初始化一个信号量*/
struct semaphore sema;
static inline void sema_init(struct semaphore *sem,int val);//初始化信号量,并设置sem中count值为val
#define init_MUTEX(sem) sema_init(sem,1) //初始化一个count值为1的互斥信号量,
#define init_MUTEX_LOCKED(sem) sema_init(sem,0) //初始化一个count值为0的信号量
/*锁定信号量*/
void down(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags); //锁定信号量的自旋锁
if (likely(sem->count > 0))
sem->count--; //如果信号量可用,将count减1
else
__down(sem); //如果信号量不可用,挂起进程
spin_unlock_irqrestore(&sem->lock, flags); //释放自旋锁
}
/*释放信号量*/
void up(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags); //锁定信号量自旋锁
if (likely(list_empty(&sem->wait_list)))
sem->count++; //如果等待队列为空,将count加1
else
__up(sem); //如果等待队列不为空,看下述内核代码,
spin_unlock_irqrestore(&sem->lock, flags); //释放自旋锁
}
static noinline void __sched __up(struct semaphore *sem)
{
//获得等待队列下一个元素的首地址,涉及到内核经常用到了container of 宏,在另一篇博客有详细讲解
//[container_of宏](https://blog.csdn.net/owhfg/article/details/81100532)
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); //唤醒相应的进程
}
/***由对内核源码可以得出结论,在进程未调用down()函数时也可以调用up()函数,放出信号量,
****且up()函数每次只能唤醒最先进入等待队列的进程。
***/
/****Linux中提供一种机制,实现一个线程发送一个信号通知另外一个进程开始完成某个任务,
*****这种机制就是完成量.完成量的目的是告诉一个线程某个事件已经发生,和信号量比较类似,
*****但在这种线程通信情况下有更高的效率****/
/*完成量的结构体*/
struct completion{
unsigned int done; //等于0时,等待完成量的进程会进入等待状态
//大于0时, 表示等待完成量的函数可以立刻执行
wait_queue_head_t wait; //等待队列的链表头,所有等待该完成量的进程组成一个链表结构
}
/*定义和初始化完成量*/
struct completion com;
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait); //初始化等待队列头
}
/*还可以使用宏来定义和初始化一个完成量*/
#define DECLARE_COMPLETION(work) \
struct completion work = COMPLETION_INITIALIZER(work)
#define COMPLETION_INITIALIZER(work)
{0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait)}
/*等待完成量*/
//该函数会执行一个不会被信号中断的等待,直到有线程完成这个完成量
void __sched wait_for_completion(struct completion *x);
/*释放完成量*/
void complete(struct completion *x); //唤醒一个等待的进程
void complete_all(struct completetion *x); //唤醒所有的等待进程
Linux驱动中的进程同步
最新推荐文章于 2024-03-24 10:23:17 发布