Linux驱动之同步、互斥、阻塞的应用

发布时间:2024-08-20  

同步、互斥、阻塞的概念:


同步:在并发程序设计中,各进程对公共变量的访问必须加以制约,这种制约称为同步。


互斥机制:访问共享资源的代码区叫做临界区,这里的共享资源可能被多个线程需要,但这些共享资源又不能被同时访问,因此临界区需要以某种互斥机制加以保护,以确保共享资源被互斥访问。


阻塞与非阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程,而是直接返回。



在按键驱动的例子中,如果有多个应用程序调用按键驱动的设备文件,这时候就要利用同步与互斥的概念对这个种情况进行处理:


1、利用原子变量标志来判断设备文件是否被打开,原子变量在操作的时候不能被打断,它是利用关闭中断的方式实现的,一旦关闭了中断,内核将不能对进程进行调度,这就保证了原子性。


直接修改驱动代码,先定义一个原子变量


 static atomic_t open_flag = ATOMIC_INIT(1);     //定义原子变量open_flag 并初始化为1

接着修改打开文件的函数与关闭文件的函数,初始化时open_flag 为1,一旦打开函数被调用则会减1变为0。关闭函数被调用后会加1又变成1。


a、在sixth_drv_open 中利用atomic_dec_and_test函数判断是否已经被调用,如果返回值为0,说明已经被调用。调用atomic_inc函数,并且返回。


b、在sixth_drv_close中第调用atomic_inc。


static int sixth_drv_open (struct inode * inode, struct file * file)

{

    int ret;



    if(atomic_dec_and_test(&open_flag)==0)//自检后是否为0,不为0说明已经被人调用

    {

        atomic_inc(&open_flag);//原子变量+1

        return -EBUSY;

    }

    

    ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, 's1', (void * )&pins_desc[0]);

    if(ret)

    {

        printk('open failed 1n');

        return -1;

    }

    ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, 's2', (void * )& pins_desc[1]);

    if(ret)

    {

        printk('open failed 2n');

        return -1;

    }

    ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, 's3', (void * )&pins_desc[2]);

    if(ret)

    {

        printk('open failed 3n');

        return -1;

    }

    ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, 's4', (void * )&pins_desc[3]);

    if(ret)

    {

        printk('open failed 4n');

        return -1;

    }

    

    return 0;

}



static int sixth_drv_close(struct inode * inode, struct file * file)

{

    atomic_inc(&open_flag);//原子变量+1

    

    free_irq(IRQ_EINT0 ,(void * )&pins_desc[0]);


     free_irq(IRQ_EINT2 ,(void * )& pins_desc[1]);


    free_irq(IRQ_EINT11 ,(void * )&pins_desc[2]);


    free_irq(IRQ_EINT19 ,(void * )&pins_desc[3]);


    return 0;

}


2、利用信号量对打开的文件进行保护:信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程进入休眠等待状态。

 

直接修改驱动代码,先定义一个互斥锁

static DECLARE_MUTEX(button_lock);     //定义互斥锁

接着更改按键驱动中打开文件的函数与关闭文件的函数:


a、在sixth_drv_open函数中如果文件打开方式非阻塞的,那么调用down_trylock函数获取信号量,此函数如果获取不到信号量,直接返回;如果打开文件的方式是阻塞的,那么调用down函数,如果获取不到信号量,则将进程休眠直到获取信号量为止。


b、在sixth_drv_close函数利用up函数直接释放掉信号量。


static int sixth_drv_open (struct inode * inode, struct file * file)

{

    int ret;


    if(file->f_flags & O_NONBLOCK)//非阻塞方式

    {

        if(down_trylock(&button_lock))//获取信号量失败则返回

            return -EBUSY;

    }

    else    

        down(&button_lock);//获得信号量

    

    ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, 's1', (void * )&pins_desc[0]);

    if(ret)

    {

        printk('open failed 1n');

        return -1;

    }

    ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, 's2', (void * )& pins_desc[1]);

    if(ret)

    {

        printk('open failed 2n');

        return -1;

    }

    ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, 's3', (void * )&pins_desc[2]);

    if(ret)

    {

        printk('open failed 3n');

        return -1;

    }

    ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, 's4', (void * )&pins_desc[3]);

    if(ret)

    {

        printk('open failed 4n');

        return -1;

    }

    

    return 0;

}



static int sixth_drv_close(struct inode * inode, struct file * file)

{

    up(&button_lock);//释放信号量

    

    free_irq(IRQ_EINT0 ,(void * )&pins_desc[0]);


     free_irq(IRQ_EINT2 ,(void * )& pins_desc[1]);


    free_irq(IRQ_EINT11 ,(void * )&pins_desc[2]);


    free_irq(IRQ_EINT19 ,(void * )&pins_desc[3]);


    return 0;

}


将完整的按键驱动的源代码贴出


#include

#include

#include

#include

#include         //含有iomap函数iounmap函数

#include //含有copy_from_user函数

#include //含有类相关的处理函数

#include //含有S3C2410_GPF0等相关的

#include     //含有IRQ_HANDLEDIRQ_TYPE_EDGE_RISING

#include    //含有IRQT_BOTHEDGE触发类型

#include //含有request_irq、free_irq函数

#include

#include   //含有各种错误返回值

//#include




static struct class *sixth_drv_class;//类

static struct class_device *sixth_drv_class_dev;//类下面的设备

static int sixthmajor;


static unsigned long *gpfcon = NULL;

static unsigned long *gpfdat = NULL;

static unsigned long *gpgcon = NULL;

static unsigned long *gpgdat = NULL;


struct fasync_struct *sixth_fasync;

    

static unsigned int key_val;


struct pin_desc 

{

    unsigned int pin;

    unsigned int key_val;

};


static struct pin_desc  pins_desc[4] = 

{

    {S3C2410_GPF0,0x01},

    {S3C2410_GPF2,0x02},

    {S3C2410_GPG3,0x03},

    {S3C2410_GPG11,0x04}

};



static unsigned int ev_press;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//注册一个等待队列button_waitq


 static atomic_t open_flag = ATOMIC_INIT(1);     //定义原子变量open_flag 并初始化为1


static DECLARE_MUTEX(button_lock);     //定义互斥锁


 

/*

  *0x01、0x02、0x03、0x04表示按键被按下

  */

  

/*

  *0x81、0x82、0x83、0x84表示按键被松开

  */


/*

  *利用dev_id的值为pins_desc来判断是哪一个按键被按下或松开

  */

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

    unsigned int pin_val;

    struct pin_desc * pin_desc = (struct pin_desc *)dev_id;//取得哪个按键被按下的状态

    

    pin_val = s3c2410_gpio_getpin(pin_desc->pin);

    

    if(pin_val) //按键松开

        key_val = 0x80 | pin_desc->key_val;

    else

        key_val = pin_desc->key_val;



    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

    ev_press = 1;    

    

    kill_fasync(&sixth_fasync, SIGIO, POLL_IN);//发生信号给进程

    

    return IRQ_HANDLED;

}




static int sixth_drv_open (struct inode * inode, struct file * file)

{

    int ret;



//    if(atomic_dec_and_test(&open_flag)==0)//自检后是否为0,不为0说明已经被人调用

//    {

//        atomic_inc(&open_flag);//原子变量+1

//        return -EBUSY;

//    }

    if(file->f_flags & O_NONBLOCK)//非阻塞方式

    {

        if(down_trylock(&button_lock))//获取信号量失败则返回

            return -EBUSY;

    }

    else    

        down(&button_lock);//获得信号量

    

    ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, 's1', (void * )&pins_desc[0]);

    if(ret)

    {

        printk('open failed 1n');

        return -1;

    }

    ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, 's2', (void * )& pins_desc[1]);

    if(ret)

    {

        printk('open failed 2n');

        return -1;

    }

    ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, 's3', (void * )&pins_desc[2]);

    if(ret)

    {

        printk('open failed 3n');

        return -1;

    }

    ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, 's4', (void * )&pins_desc[3]);

    if(ret)

    {

        printk('open failed 4n');

        return -1;

    }

    

    return 0;

}



static int sixth_drv_close(struct inode * inode, struct file * file)

{

//    atomic_inc(&open_flag);//原子变量+1

    up(&button_lock);//释放信号量

    

    free_irq(IRQ_EINT0 ,(void * )&pins_desc[0]);


     free_irq(IRQ_EINT2 ,(void * )& pins_desc[1]);


    free_irq(IRQ_EINT11 ,(void * )&pins_desc[2]);


    free_irq(IRQ_EINT19 ,(void * )&pins_desc[3]);


    return 0;

}


static ssize_t sixth_drv_read(struct file * file, char __user * userbuf, size_t count, loff_t * off)

{

    int ret;


    if(count != 1)

    {

        printk('read errorn');

        return -1;

    }


    if(file->f_flags & O_NONBLOCK)//非阻塞方式

    {

        if(!ev_press)//判断是否有按键按下,如果没有直接返回

        {

                key_val = 0;

                copy_to_user(userbuf, &key_val, 1);

                return -EBUSY;

        }

    }

    else//如果没有按键动作,直接进入休眠

        wait_event_interruptible(button_waitq, ev_press);//将当前进程放入等待队列button_waitq中

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>