OK6410之信号量和阻塞机制

上一篇文章讲了利用原子变量防止设备竞态的发生,这一节将如何利用信号量达到同样的效果。

 

学过ucosIII操作系统的朋友会对信号量这个概念比较熟悉。程序中请求信号量,如果得到信号量则信号量的值-1,并且程序继续运行。若请求不到信号量则会阻塞或者进行其他操作。

当程序结束前要释放信号量,则信号量的值+1,这样别的程序才能得到信号量。

 

信号量的相关内核代码在<asm/semaphore.h>中。

 

(1)初始化信号量

信号量的相关结构体为struct semaphore。为了初始化信号量有不同的办法,其中之一就是直接创建:

int sema_init(struct semaphore *sem, int val); 

其中val是信号量的初值。

 

但是因为信号量经常被用于互斥模式,即它的值通常是取1的,所以内核提供了一种更加方便的定义方式

DECLARE_MUTEX(name); //定义信号量name,初始值为1
DECLARE_MUTEX_LOCKED(name); //定义信号量name,初始值为0

其中DECLARE_MUTEX的定义为

#define DECLARE_MUTEX(name)	\
	struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)


#define __SEMAPHORE_INITIALIZER(name, n)				\
{									\
	.lock		= __SPIN_LOCK_UNLOCKED((name).lock),		\
	.count		= n,						\
	.wait_list	= LIST_HEAD_INIT((name).wait_list),		\
}

 

(2)请求信号量

请求信号量的相关函数为

void down(struct semaphore * sem);            // 获取不到就进入不被中断的休眠状态(down函数中睡眠)

int down_interruptible(struct semaphore * sem);  //获取不到就进入可被中断的休眠状态(down函数中睡眠)

int down_trylock(struct semaphore * sem);       //试图获取信号量,获取不到则立刻返回正数

其中前两个函数如果请求不到信号量会阻塞,因此考虑是否使用阻塞机制中分情况调用不同的函数。

一般来说应该在平时编程时应该使用down_interruptible,但是使用时要格外小心,如果操作被中断,则会返回非零值,且调用者不会获得信号量。因此对down_interruptible的正确使用需要始终检查返回值,从而做出不同的响应。

一旦进程成功down了信号量之后就受到了信号量赋予的临界段的保护。

 

(3)释放信号量

void up(struct semaphore *sem);

在进程结束前要释放信号量。

 

阻塞机制

当进程无法得到调用的信号量的时候,分为阻塞和非阻塞两种情况。如果阻塞,则进程一直阻塞,此时调用的是down()函数;如果非阻塞,则此时进程结束,调用的是down_trylock()函数。

 

至于是否采用阻塞,是在open打开文件时通过标志量O_NOBLOCK设定的

fd = open("/dev/buttons ", O_RDWR | O_NONBLOCK); 

综上所诉,开始编写程序

首先在开头定义信号量

DECLARE_MUTEX(sig);

之后在驱动的open()函数内,通过flags判断是否阻塞,然后调用不同的信号函数

if(file -> f_flags &  O_NONBLOCK)) // 非阻塞
{
        if(down_trylock(&sig))
               return -1;
}
else
        down();                  //阻塞

同样在read函数内也需要进行判断

if(file -> f_flags & O_NOBLOCK)    //非阻塞
{
    if(!ev_press)
     return -1;
}

else                            //阻塞
{
   wait_event_interruptible(button_waitq, ev_press); 
}

 

完整的驱动程序:

#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/irq.h>
#include <linux/poll.h>  
#include <asm/irq.h>  
#include <asm/io.h>  
#include <linux/interrupt.h>  
#include <linux/cdev.h>  
#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <linux/wait.h>
#include <linux/sched.h>  
#include <mach/hardware.h>  
#include <linux/platform_device.h>  
#include <linux/semaphore.h>
//#include <mach/regs-gpio.h>  
//#include <mach/gpio-bank-n.h>  
#include <plat/gpio-cfg.h>  

DECLARE_MUTEX(sig);    //初始化信号量

struct class *key_class;
struct class_device *key_class_device;
volatile unsigned long *gpmcon = NULL;
volatile unsigned long *gpmdat = NULL; 

struct cdev cdev;

dev_t dev;   //设备号


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;

unsigned int key_val;

struct pin_desc
{
	unsigned pin;
	unsigned key_val;
};

struct pin_desc pins_desc[4] = {
	{S3C64XX_GPN(0), 0x01},
	{S3C64XX_GPN(1), 0x02},
	{S3C64XX_GPN(2), 0x03},
	{S3C64XX_GPN(3), 0x04},	
};


static irqreturn_t button_irq(int irq, void *dev)
{
	unsigned int pval;
	struct pin_desc *pdesc = (struct pin_desc*)dev;

	pval = gpio_get_value(pdesc -> pin);
	if(pval)
	{
		key_val = 0x80 | pdesc -> key_val;
	}
	else
		key_val = pdesc -> key_val;
	
	ev_press = 1;
	wake_up_interruptible(&button_waitq);

	printk("press check\n");

	return IRQ_RETVAL(IRQ_HANDLED);
}

unsigned int key_poll(struct file *file, poll_table *wait)
{
	unsigned int mask = 0;
 
	poll_wait(file, &button_waitq, wait);//将button_waitq放入队列中。到这里还没有进入休眠
 
	if (ev_press)
		mask |= POLLIN | POLLWRNORM;//mask是返回应用程序poll的返回值,mask一定要和应用程序中的events相对应,不然还会进行超时
 
	return mask;
}

int key_open(struct inode *inode,struct file *file)
{
	if(file->f_flags & O_NONBLOCK)
	{
		if(down_trylock(&sig))     //如果获取不到信号量则返回正值
			return -EBUSY;
	}
	else
		down(&sig);

	request_irq(IRQ_EINT(0), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s0",&pins_desc[0]);  /* 这里的irq指的不是中断源 */
	request_irq(IRQ_EINT(1), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s1",&pins_desc[1]);
	request_irq(IRQ_EINT(2), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s2",&pins_desc[2]);
	request_irq(IRQ_EINT(3), button_irq, IRQF_SAMPLE_RANDOM | IRQ_TYPE_EDGE_BOTH |IRQF_SHARED, "s3",&pins_desc[3]);
	/*gpmcon = (volatile unsigned long *)ioremap(0x7f008820,4);
	writel(0x1111,gpmcon);
	gpmdat = (volatile unsigned long *)ioremap(0x7f008824,4); /* 指针加1是以数据类型相加的*/
	
	printk("open!\n");
	return 0;
}

ssize_t key_release(struct inode *inode,struct file *file)
{
	up(&sig);
	free_irq(IRQ_EINT(0),&pins_desc[0]);
	free_irq(IRQ_EINT(1),&pins_desc[1]);
	free_irq(IRQ_EINT(2),&pins_desc[2]);
	free_irq(IRQ_EINT(3),&pins_desc[3]);
	return 0;
}


ssize_t key_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{
    
	if(count != 1)
	return -EINVAL;

	printk("$$$$$$$$$$key_read$$$$$$$$$\n");
	if(file->f_flags & O_NONBLOCK)
	{
		if(ev_press != 1)
	{
		return -1;
	}
	}
	
	else
	{
		wait_event_interruptible(button_waitq, ev_press);
	}
	
    
	copy_to_user(buf,&key_val,1);
	ev_press = 0;

	return count;
 
}

ssize_t key_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	char mbuf[10];
	printk("write!\n");
	copy_from_user(mbuf,buf,count);
	    switch(mbuf[0])
    {
        case 1:
            writel(0x01, gpmdat);
            break;
 
        case 0:
            writel(0x0F,gpmdat);
            break;
		default:
			break;

    }
		return count;
}



struct file_operations key = {
	.owner   = THIS_MODULE,
	.open    = key_open,
	.read    = key_read,
	.write   = key_write,
	.release = key_release,
	.poll    = key_poll,
};


int mykey_init(void)
{
    cdev_init(&cdev,&key);

	alloc_chrdev_region(&dev,0,1,"int_key_chrdev");

	cdev_add(&cdev,dev,1);

	key_class = class_create(THIS_MODULE,"int_key");
	/*if(IS_ERR(led_class))
	return PTR_ERR(led_class);*/

	key_class_device = device_create(key_class,NULL,dev,NULL,"int_key1");
	/* if(unlikely(IS_ERR(led_class)))
	return PTR_ERR(led_class); */
   
	return 0;
}

void key_exit(void)
{
	cdev_del(&cdev);
	unregister_chrdev_region(dev,1);
	device_unregister(key_class_device);
	class_destroy(key_class);
}

MODULE_LICENSE("GPL");
module_init(mykey_init);
module_exit(key_exit);

 

测试程序

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;       
  fd=open("/dev/int_key1",O_RDWR );           //阻塞操作,若阻塞则加上O_NOBLOCK
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);      
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打开成功,打印pid进程号
       } 

   while(1)
   { 
    val=read( fd, &ret, 1);              //读取驱动层数据
     printf("key_vale=0X%x,retrun=%d\r\n",ret,val);  
   }
   return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值