/***********************************************************************************
*
* spinlock,semaphore,atomic,mutex,completion,interrupt
*
* 声明:
* 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
* 不对齐,从而影响阅读.
* 2. 本文中有些源代码没有全部帖出来,主要是因为篇幅太大的原因;
* 3. 基于2中的原因,本文借鉴了python中的缩进代码风格进行代码的体现:
* 1. 有些代码中的"..."代表省略了不影响阅读的代码;
* 2. 如下代码缩进代表在一个函数内部的代码,至于在什么函数里,不影响阅读:
* ... //省略代码
* struct test_s {
* };
* ... //省略代码
*
* //进入临界区之前加锁 }
* spin_lock(&p->lock); |
* | |
* /* 有效代码 */ |-->|采用缩进,代表在一个函数内
* | |的代码
* //出临界区之后解锁 |
* spin_unlock(&p->lock); }
*
* ... //省略代码
* int __init test_init(void)
* {
* ... //省略代码
* }
* ... //省略代码
*
*
* -- 阴 深圳 尚观 Var 曾剑锋
**********************************************************************************/ \\\\\\\\--*目录*--////////
| 一. spinlock接口;
| 二. semaphore接口;
| 三. atomic接口;
| 四. mutex接口;
| 五. completion接口;
| 六. interrupt接口;
| 七. 按键驱动大致写法;
| 八. 测试按键驱动;
\\\\\\\\\\\\////////////// 一. spinlock接口:
. 声明: spinlock_t lock;
. 初始化: spin_lock_init(&test.lock);
. 获取自旋锁: spin_lock(&p->lock);
. 释放自旋锁: spin_unlock(&p->lock);
. spin_lock接口使用Demo:
...
struct test_s {
struct file_operations fops;
spinlock_t lock;
int major;
};
...
//进入临界区之前加锁
spin_lock(&p->lock);
for(i = ; i < ; i++)
{
printk("count = %d, %s", cnt++, kbuf);
/*msleep(10);*/
mdelay();
}
//出临界区之后解锁
spin_unlock(&p->lock);
...
int __init test_init(void)
{
int ret;
// 初始化spin_lock
spin_lock_init(&test.lock); ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
}
... 二. semaphore接口:
. 定义: struct semaphore sem;
. 定义一个信号量,并初始化: DEFINE_SEMAPHORE(name);
. 初始化: sema_init(&test.sem, );
. 3种获取信号量:
. down(&p->sem);
. down_interruptible(&p->sem);
. down_trylock(&p->sem);
. 释放信号量: up(&p->sem);
. semaphore接口使用Demo:
...
struct test_s {
struct file_operations fops;
/**
* spinlock_t lock;
* volatile int count;
*/
struct semaphore sem;
int major;
};
...
/**
* spin_lock(&p->lock);
* if(p->count <= 0)
* {
* spin_unlock(&p->lock);
* return -EAGAIN;
* }
* p->count--;
* spin_unlock(&p->lock);
*/ //加不了锁,睡眠等待
/*down(&p->sem);*/ if(down_trylock(&p->sem))
return -EAGAIN; for(i = ; i < ; i++)
{
printk("count = %d, %s", cnt++, kbuf);
msleep();
} up(&p->sem); /**
* spin_lock(&p->lock);
* p->count++;
* spin_unlock(&p->lock);
*/
...
int __init test_init(void)
{
int ret; /**
* spin_lock_init(&test.lock);
* test.count = 1;
*/ sema_init(&test.sem, ); ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
}
... 三. atomic接口:
. 头文件: linux-3.5/include/linux/atomic.h
. 声明定义: atomic_t val; atomic_t *v = &val;
. 读取原子变量的值: atomic_read(v);
. 修改原子变量的值: atomic_set(v, i);
. 原子变量自加1: atomic_inc(v); --> v += ;
. 原子变量自减1: atomic_dec(v); --> v -= ;
. 原子变量自加1并检测是否为0: atomic_inc_and_test(v); v += ,判断结果是否为0
. 原子变量自减1并检测是否为0: atomic_dec_and_test(v); v -= ,判断结果是否为0
. 原子变量自加1并返回原子变量的值: atomic_inc_return(v)
. 原子变量自减1并返回原子变量的值: atomic_dec_return(v)
. 比较变量i和原子变量的值是否相等: atomic_sub_and_test(i, v)
. atomic接口使用Demo:
...
struct test_s {
struct file_operations fops;
atomic_t v;
int major;
};
typedef struct test_s test_t; static int test_open(struct inode *inode, struct file *file)
{
test_t *p;
p = container_of(file->f_op, test_t, fops); file->private_data = p; if(!atomic_dec_and_test(&p->v))
{
atomic_inc(&p->v);
return -EAGAIN;
} printk("Open.\n"); return ;
} static int test_close(struct inode *inode, struct file *file)
{
test_t *p = file->private_data; printk("Close.\n");
atomic_inc(&p->v); return ;
}
...
int __init test_init(void)
{
int ret; atomic_set(&test.v, ); ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
}
... 四. mutex接口:
. 定义: struct mutex lock;
. 定义一个互斥锁,并初始化: DEFINE_MUTEX(mutexname);
. 初始化: mutex_init(&lock);
. 3种加锁方式:
. mutex_lock(&lock);
. mutex_lock_interruptible(&lock);
. mutex_trylock(&lock);
. 解锁: mutex_unlock(&lock);
. mutex接口使用Demo:
...
struct test_s {
struct file_operations fops;
/*struct semaphore sem;*/
struct mutex lock;
int major;
};
typedef struct test_s test_t;
...
/*mutex_lock(&p->lock);*/ /*
*if(mutex_lock_interruptible(&p->lock))
* return -EINTR;
*/ if(!mutex_trylock(&p->lock))
return -EAGAIN; for(i = ; i < ; i++)
{
printk("count = %d, %s", cnt++, kbuf);
msleep();
} mutex_unlock(&p->lock);
...
int __init test_init(void)
{
int ret; mutex_init(&test.lock); ret = register_chrdev(test.major, DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
} 五. completion接口:
. 定义: struct completion com;
. 定义一个完成量,并初始化: DECLARE_COMPLETION(work)
. 初始化: init_completion(&com);
. 2种等待完成:
. wait_for_completion(&com);
. wait_for_completion_interruptible(&com);
. 2种通知完成量:
. complete(&com);
. complete_all(&com);
. mutex接口使用Demo:
...
struct test_s {
struct file_operations fops;
struct completion com;
int major;
};
typedef struct test_s test_t;
...
static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
test_t *p = file->private_data; //在完成量com上阻塞
/*wait_for_completion(&p->com);*/ if(wait_for_completion_interruptible(&p->com))
return -ERESTARTSYS; printk("Read data.\n"); return count;
} static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
test_t *p = file->private_data; printk("Write data.\n"); /*complete(&p->com);*/ //通知所有阻塞的进程
complete_all(&p->com); return count;
}
...
int __init test_init(void)
{
int ret; init_completion(&test.com);
ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} return ret;
}
...
六. interrupt接口:
. 查看系统中断处理信息: cat /proc/interrupts
. 申请并注册中断处理函数:
static inline int __must_check request_irq( unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_data);
. 释放中断,并取消中断处理函数:
void free_irq(unsigned int irq, void *dev_data);
. 代码执行环境:
. 中断上下文: in_interrupt(); ---> 判断执行环境是否是中断上下文
. 软中断上下文: in_softirq(); ---> 判断执行环境是否是soft irq
. 外部中断上下文: in_irq(); ---> 判断执行环境是否是硬件中断处理环境
. 进程上下文.
. 共享中断方法:
. request_irq()指定共享标志 IRQF_SHARED;
. request_irq()最后一个参数不能传递NULL,传递当前驱动全局变量地址;
. 将系统gpio编号转换成对应的外部中断: gpio_to_irq();
. spinlock中断中使用Demo:
...
struct test_s {
struct file_operations fops;
spinlock_t lock;
int major;
};
typedef struct test_s test_t;
...
int critical(const char *s, spinlock_t *lock)
{
int i;
unsigned long flag;
static int cnt = ; /*spin_lock(lock);*/
/*local_irq_disable();*/
/*local_irq_save(flag);*/
/*spin_lock_irq(lock);*/
spin_lock_irqsave(lock, flag); for(i = ; i < ; i++)
{
printk("count = %d, %s", cnt++, s);
mdelay();
} spin_unlock_irqrestore(lock, flag);
/*spin_unlock_irq(lock);*/
/*local_irq_restore(flag);*/
/*local_irq_enable();*/
/*spin_unlock(lock);*/ return ;
} static irqreturn_t irq_handler(int irq, void *arg)
{
test_t *p = arg; critical("irq\n", &p->lock); return IRQ_HANDLED;
} static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
int ret;
char kbuf[count + ];
test_t *p = file->private_data; ret = copy_from_user(kbuf, buf, count);
if(ret)
return -EFAULT;
kbuf[count] = '\0'; if(critical(kbuf, &p->lock))
return -EAGAIN; return count;
}
...
int __init test_init(void)
{
int ret; spin_lock_init(&test.lock); ret = register_chrdev(test.major,
DEV_NAME, &test.fops);
if(ret > )
{
test.major = ret;
printk("major = %d\n", test.major);
ret = ;
} ret = request_irq(IRQ_EINT(), irq_handler,
IRQF_TRIGGER_FALLING,
"key1", &test);
if(ret)
unregister_chrdev(test.major, DEV_NAME); return ret;
}
... 七. 按键驱动大致写法:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/sched.h> #define DEV_NAME "test" struct timer_list timer;
struct btn_desc {
int gpio;
int num;
char *name;
}; int ev_key = ;
char key_state[] = {, , , };
DECLARE_WAIT_QUEUE_HEAD(wq); struct btn_desc btn[] = {
{ EXYNOS4_GPX3(), , "key1" },
{ EXYNOS4_GPX3(), , "key2" },
{ EXYNOS4_GPX3(), , "key3" },
{ EXYNOS4_GPX3(), , "key4" }
}; void timer_main(unsigned long data)
{
ev_key = ;
wake_up_interruptible(&wq);
printk("timer_main.\n");
} static irqreturn_t irq_handler(int irq, void *arg)
{
struct btn_desc *key = arg; key_state[key->num] = ; mod_timer(&timer, jiffies + ); return IRQ_HANDLED;
} static int test_open(struct inode *inode, struct file *file)
{
int i, irq, ret; for(i = ; i < ARRAY_SIZE(btn); i++)
{
//irq = IRQ_EINT(26+i); //irq可以通过这种方法获得
irq = gpio_to_irq(btn[i].gpio);
ret = request_irq(irq, irq_handler, IRQF_TRIGGER_FALLING | IRQF_SHARED,
btn[i].name, &btn[i]);
if(ret)
break;
} if(ret)
{
for(--i; i >= ; i--)
{
irq = gpio_to_irq(btn[i].gpio);
free_irq(irq, &btn[i]);
}
return ret;
} return ;
} static int test_close(struct inode *inode, struct file *file)
{
int i, irq; for(i = ; i < ARRAY_SIZE(btn); i++)
{
irq = gpio_to_irq(btn[i].gpio);
free_irq(irq, &btn[i]);
} return ;
} static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
int ret; if(count > ARRAY_SIZE(key_state))
count = ARRAY_SIZE(key_state); while(!ev_key)
{
if(file->f_flags & O_NONBLOCK)
return -EAGAIN; if(wait_event_interruptible(wq, ev_key))
return -ERESTARTSYS;
} ret = copy_to_user(buf, key_state, count);
if(ret)
return -EFAULT; memset(key_state, , sizeof(key_state));
ev_key = ; return count;
} static struct file_operations fops = {
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.read = test_read,
}; int major;
int __init test_init(void)
{
int ret; setup_timer(&timer, timer_main, );
ret = register_chrdev(major,
DEV_NAME, &fops);
if(ret > )
{
major = ret;
printk("major = %d\n", major);
ret = ;
} return ret;
} void __exit test_exit(void)
{
del_timer_sync(&timer);
unregister_chrdev(major, DEV_NAME);
} module_init(test_init);
module_exit(test_exit); MODULE_LICENSE("GPL"); 八. 测试按键驱动:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h> int main(int argc, char **argv)
{
int fd, i, ret;
char buf[]; fd = open(argv[], O_RDWR);
if(- == fd)
{
perror("open");
exit();
} while()
{
ret = read(fd, buf, sizeof(buf));
if( != ret)
continue; for(i = ; i < sizeof(buf); i++)
{
if(buf[i] == )
printf("key%d down.\n", i + );
}
} close(fd);
return ;
}