Linux驱动中的进程同步

/**原子变量操作是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);  //唤醒所有的等待进程












  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值