/*********************************************************************************** * * 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) * { * ... //省略代码 * } * ... //省略代码 * * * 2015-3-13 阴 深圳 尚观 Var 曾剑锋 **********************************************************************************/ \\\\\\\\--*目录*--//////// | 一. spinlock接口; | 二. semaphore接口; | 三. atomic接口; | 四. mutex接口; | 五. completion接口; | 六. interrupt接口; | 七. 按键驱动大致写法; | 八. 测试按键驱动; \\\\\\\\\\\\////////////// 一. spinlock接口: 1. 声明: spinlock_t lock; 2. 初始化: spin_lock_init(&test.lock); 3. 获取自旋锁: spin_lock(&p->lock); 4. 释放自旋锁: spin_unlock(&p->lock); 5. spin_lock接口使用Demo: ... struct test_s { struct file_operations fops; spinlock_t lock; int major; }; ... //进入临界区之前加锁 spin_lock(&p->lock); for(i = 0; i < 3; i++) { printk("count = %d, %s", cnt++, kbuf); /*msleep(10);*/ mdelay(10); } //出临界区之后解锁 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 > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } return ret; } ... 二. semaphore接口: 1. 定义: struct semaphore sem; 2. 定义一个信号量,并初始化: DEFINE_SEMAPHORE(name); 3. 初始化: sema_init(&test.sem, 1); 4. 3种获取信号量: 1. down(&p->sem); 2. down_interruptible(&p->sem); 3. down_trylock(&p->sem); 5. 释放信号量: up(&p->sem); 6. 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 = 0; i < 3; i++) { printk("count = %d, %s", cnt++, kbuf); msleep(10); } 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, 1); ret = register_chrdev(test.major, DEV_NAME, &test.fops); if(ret > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } return ret; } ... 三. atomic接口: 1. 头文件: linux-3.5/include/linux/atomic.h 2. 声明定义: atomic_t val; atomic_t *v = &val; 3. 读取原子变量的值: atomic_read(v); 4. 修改原子变量的值: atomic_set(v, i); 5. 原子变量自加1: atomic_inc(v); --> v += 1; 6. 原子变量自减1: atomic_dec(v); --> v -= 1; 7. 原子变量自加1并检测是否为0: atomic_inc_and_test(v); v += 1,判断结果是否为0 8. 原子变量自减1并检测是否为0: atomic_dec_and_test(v); v -= 1,判断结果是否为0 9. 原子变量自加1并返回原子变量的值: atomic_inc_return(v) 10. 原子变量自减1并返回原子变量的值: atomic_dec_return(v) 11. 比较变量i和原子变量的值是否相等: atomic_sub_and_test(i, v) 12. 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 0; } static int test_close(struct inode *inode, struct file *file) { test_t *p = file->private_data; printk("Close.\n"); atomic_inc(&p->v); return 0; } ... int __init test_init(void) { int ret; atomic_set(&test.v, 1); ret = register_chrdev(test.major, DEV_NAME, &test.fops); if(ret > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } return ret; } ... 四. mutex接口: 1. 定义: struct mutex lock; 2. 定义一个互斥锁,并初始化: DEFINE_MUTEX(mutexname); 3. 初始化: mutex_init(&lock); 4. 3种加锁方式: 1. mutex_lock(&lock); 2. mutex_lock_interruptible(&lock); 3. mutex_trylock(&lock); 5. 解锁: mutex_unlock(&lock); 6. 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 = 0; i < 3; i++) { printk("count = %d, %s", cnt++, kbuf); msleep(10); } 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 > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } return ret; } 五. completion接口: 1. 定义: struct completion com; 2. 定义一个完成量,并初始化: DECLARE_COMPLETION(work) 3. 初始化: init_completion(&com); 4. 2种等待完成: 1. wait_for_completion(&com); 2. wait_for_completion_interruptible(&com); 5. 2种通知完成量: 1. complete(&com); 2. complete_all(&com); 6. 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 > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } return ret; } ... 六. interrupt接口: 1. 查看系统中断处理信息: cat /proc/interrupts 2. 申请并注册中断处理函数: static inline int __must_check request_irq( unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_data); 3. 释放中断,并取消中断处理函数: void free_irq(unsigned int irq, void *dev_data); 4. 代码执行环境: 1. 中断上下文: in_interrupt(); ---> 判断执行环境是否是中断上下文 1. 软中断上下文: in_softirq(); ---> 判断执行环境是否是soft irq 2. 外部中断上下文: in_irq(); ---> 判断执行环境是否是硬件中断处理环境 2. 进程上下文. 5. 共享中断方法: 1. request_irq()指定共享标志 IRQF_SHARED; 2. request_irq()最后一个参数不能传递NULL,传递当前驱动全局变量地址; 6. 将系统gpio编号转换成对应的外部中断: gpio_to_irq(); 7. 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 = 0; /*spin_lock(lock);*/ /*local_irq_disable();*/ /*local_irq_save(flag);*/ /*spin_lock_irq(lock);*/ spin_lock_irqsave(lock, flag); for(i = 0; i < 3; i++) { printk("count = %d, %s", cnt++, s); mdelay(1000); } spin_unlock_irqrestore(lock, flag); /*spin_unlock_irq(lock);*/ /*local_irq_restore(flag);*/ /*local_irq_enable();*/ /*spin_unlock(lock);*/ return 0; } 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 + 1]; 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 > 0) { test.major = ret; printk("major = %d\n", test.major); ret = 0; } ret = request_irq(IRQ_EINT(26), 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 = 0; char key_state[] = {0, 0, 0, 0}; DECLARE_WAIT_QUEUE_HEAD(wq); struct btn_desc btn[] = { { EXYNOS4_GPX3(2), 0, "key1" }, { EXYNOS4_GPX3(3), 1, "key2" }, { EXYNOS4_GPX3(4), 2, "key3" }, { EXYNOS4_GPX3(5), 3, "key4" } }; void timer_main(unsigned long data) { ev_key = 1; 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] = 1; mod_timer(&timer, jiffies + 20); return IRQ_HANDLED; } static int test_open(struct inode *inode, struct file *file) { int i, irq, ret; for(i = 0; 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 >= 0; i--) { irq = gpio_to_irq(btn[i].gpio); free_irq(irq, &btn[i]); } return ret; } return 0; } static int test_close(struct inode *inode, struct file *file) { int i, irq; for(i = 0; i < ARRAY_SIZE(btn); i++) { irq = gpio_to_irq(btn[i].gpio); free_irq(irq, &btn[i]); } return 0; } 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, 0, sizeof(key_state)); ev_key = 0; 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, 11223344); ret = register_chrdev(major, DEV_NAME, &fops); if(ret > 0) { major = ret; printk("major = %d\n", major); ret = 0; } 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[4]; fd = open(argv[1], O_RDWR); if(-1 == fd) { perror("open"); exit(1); } while(1) { ret = read(fd, buf, sizeof(buf)); if(4 != ret) continue; for(i = 0; i < sizeof(buf); i++) { if(buf[i] == 1) printf("key%d down.\n", i + 1); } } close(fd); return 0; }