linux 原子操作能用在中断中吗

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 原子操作能用在中断中吗相关的知识,希望对你有一定的参考价值。

参考技术A 中断里只要不睡眠即可,因为中断上下文不能调度,而原子操作不会等待 肯定会返回一个结果 要么执行要么不执行 所以不会睡眠 参考技术B 不能。不能中断。要么执行,要么不执行。 参考技术C 不能中断的,亲 ,要么继续 要么就关机...

Linux之同步互斥阻塞20160703

主要介绍一下Linux下的互斥与阻塞方面的知识:

1. 原子操作

原子操作指的是在执行过程中不会被别的代码路径所中断的操作。

常用原子操作函数举例:

atomic_t v = ATOMIC_INIT(0);     //定义原子变量v并初始化为0

atomic_read(atomic_t *v);        //返回原子变量的值

void atomic_inc(atomic_t *v);    //原子变量增加1

void atomic_dec(atomic_t *v);    //原子变量减少1

int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false

 

2. 信号量

信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。

当获取不到信号量时,进程进入休眠等待状态。

 

定义信号量

struct semaphore sem;

初始化信号量

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

void init_MUTEX(struct semaphore *sem);//初始化为0

 

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

 

获得信号量

void down(struct semaphore * sem);

int down_interruptible(struct semaphore * sem);

int down_trylock(struct semaphore * sem);

释放信号量

void up(struct semaphore * sem);

 

3. 阻塞

阻塞操作    

是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。

被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

 

非阻塞操作  

进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。

 

fd = open("...", O_RDWR | O_NONBLOCK);

 

 

示例代码:

1.应用程序:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <poll.h>

#include <signal.h>

#include <sys/types.h>

#include <unistd.h>

#include <fcntl.h>

 

 

/* sixthdrvtest

  */

int fd;

 

void my_signal_fun(int signum)

{

unsigned char key_val;

read(fd, &key_val, 1);

printf("key_val: 0x%x\\n", key_val);

}

 

int main(int argc, char **argv)

{

unsigned char key_val;

int ret;

int Oflags;

 

//signal(SIGIO, my_signal_fun);

 

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

if (fd < 0)

{

printf("can\'t open!\\n");

return -1;

}

 

//fcntl(fd, F_SETOWN, getpid());

 

//Oflags = fcntl(fd, F_GETFL);

 

//fcntl(fd, F_SETFL, Oflags | FASYNC);

 

 

while (1)

{

ret = read(fd, &key_val, 1);

printf("key_val: 0x%x, ret = %d\\n", key_val, ret);

sleep(5);

}

 

return 0;

}

 

2.驱动程序:

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <linux/irq.h>

#include <asm/uaccess.h>

#include <asm/irq.h>

#include <asm/io.h>

#include <asm/arch/regs-gpio.h>

#include <asm/hardware.h>

#include <linux/poll.h>

 

 

static struct class *sixthdrv_class;

static struct class_device *sixthdrv_class_dev;

 

volatile unsigned long *gpfcon;

volatile unsigned long *gpfdat;

 

volatile unsigned long *gpgcon;

volatile unsigned long *gpgdat;

 

 

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

 

/* 中断事件标志, 中断服务程序将它置1sixth_drv_read将它清0 */

static volatile int ev_press = 0;

 

static struct fasync_struct *button_async;

 

 

struct pin_desc{

unsigned int pin;

unsigned int key_val;

};

 

 

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */

/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */

static unsigned char key_val;

 

struct pin_desc pins_desc[4] = {

{S3C2410_GPF0, 0x01},

{S3C2410_GPF2, 0x02},

{S3C2410_GPG3, 0x03},

{S3C2410_GPG11, 0x04},

};

 

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

 

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

 

/*

  * 确定按键值

  */

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

struct pin_desc * pindesc = (struct pin_desc *)dev_id;

unsigned int pinval;

 

pinval = s3c2410_gpio_getpin(pindesc->pin);

 

if (pinval)

{

/* 松开 */

key_val = 0x80 | pindesc->key_val;

}

else

{

/* 按下 */

key_val = pindesc->key_val;

}

 

    ev_press = 1;                  /* 表示中断发生了 */

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

 

kill_fasync (&button_async, SIGIO, POLL_IN);

 

return IRQ_RETVAL(IRQ_HANDLED);

}

 

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

{

#if 0

if (!atomic_dec_and_test(&canopen))

{

atomic_inc(&canopen);

return -EBUSY;

}

#endif

 

if (file->f_flags & O_NONBLOCK)

{

if (down_trylock(&button_lock))

return -EBUSY;

}

else

{

/* 获取信号量 */

down(&button_lock);

}

 

/* 配置GPF0,2为输入引脚 */

/* 配置GPG3,11为输入引脚 */

request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);

request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);

request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);

request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);

 

return 0;

}

 

ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

{

if (size != 1)

return -EINVAL;

 

if (file->f_flags & O_NONBLOCK)

{

if (!ev_press)

return -EAGAIN;

}

else

{

/* 如果没有按键动作, 休眠 */

wait_event_interruptible(button_waitq, ev_press);

}

 

/* 如果有按键动作, 返回键值 */

copy_to_user(buf, &key_val, 1);

ev_press = 0;

 

return 1;

}

 

 

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

{

//atomic_inc(&canopen);

free_irq(IRQ_EINT0, &pins_desc[0]);

free_irq(IRQ_EINT2, &pins_desc[1]);

free_irq(IRQ_EINT11, &pins_desc[2]);

free_irq(IRQ_EINT19, &pins_desc[3]);

up(&button_lock);

return 0;

}

 

static unsigned sixth_drv_poll(struct file *file, poll_table *wait)

{

unsigned int mask = 0;

poll_wait(file, &button_waitq, wait); // 不会立即休眠

 

if (ev_press)

mask |= POLLIN | POLLRDNORM;

 

return mask;

}

 

static int sixth_drv_fasync (int fd, struct file *filp, int on)

{

printk("driver: sixth_drv_fasync\\n");

return fasync_helper (fd, filp, on, &button_async);

}

 

 

static struct file_operations sencod_drv_fops = {

    .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open    =  sixth_drv_open,     

.read  = sixth_drv_read,    

.release =  sixth_drv_close,

.poll    =  sixth_drv_poll,

.fasync  =  sixth_drv_fasync,

};

 

 

int major;

static int sixth_drv_init(void)

{

major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);

 

sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");

 

sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

 

gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);

gpfdat = gpfcon + 1;

 

gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);

gpgdat = gpgcon + 1;

 

return 0;

}

 

static void sixth_drv_exit(void)

{

unregister_chrdev(major, "sixth_drv");

class_device_unregister(sixthdrv_class_dev);

class_destroy(sixthdrv_class);

iounmap(gpfcon);

iounmap(gpgcon);

return 0;

}

 

 

module_init(sixth_drv_init);

 

module_exit(sixth_drv_exit);

 

MODULE_LICENSE("GPL");

最后附笔者学习笔记:

以上是关于linux 原子操作能用在中断中吗的主要内容,如果未能解决你的问题,请参考以下文章

016_linux驱动之_原子操作

Linux内核同步原语之原子操作

Linux 并发与竞争(原子操作自旋锁信号量互斥体)

Linux驱动开发原子操作自旋锁信号量互斥体

Linux驱动开发原子操作自旋锁信号量互斥体

操作系统中啥是“原子操作”