总览
本文使用 linux-2.6.22.6 内核, 使用jz2440开发板.
硬件具备唯一性, 因此某一时刻应该只有一个应用程序能对驱动进行操作.
原子操作
原子操作指的是在执行过程中不会被别的进程或中断所打断的操作
底层实现就是 raw_local_irq_save
raw_local_irq_restore
, 保存中断并禁止, 操作完成后, 恢复.
atomic_t v = ATOMIC_INIT(0); atomic_read(atomic_t *v); atomic_inc(atomic_t *v); atomic_dec(atomic_t *v); atomic_dec_and_test(atomic_t *v);
|
drv_sem.c
源码基于 驱动之基于中断设计按键驱动, 部分修改而来
只显示新增和修改的部分. 这样更直观易懂.
#include"drv_sem.h" #define DRV_KEY_INT_NODE_NAME "drv_sem"
atomic_t canopen = ATOMIC_INIT(1);
static int drv_key_int_open(struct inode *inode,struct file *filp) { int ret;
if (!atomic_dec_and_test(&canopen)) { atomic_inc(&canopen); return -EBUSY; }
......
return 0; }
static int drv_key_int_release(struct inode *inode,struct file *filp) { ......
atomic_inc(&canopen); return 0; }
|
drv_sem.h
#define DRIVER_NAME "drv_sem"
|
Makefile
测试文件 test_drv_sem.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h>
int main(int argc, char **argv) { int fd; unsigned char keys_val;
fd = open("/dev/drv_sem0", O_RDWR); if (fd < 0) { printf("can't open!\n"); return -1; }
while (1) { read(fd, &keys_val, 1); printf("keys_val=0x%x, pid=%d\n", keys_val, getpid()); }
return 0; }
|
编译并测试
$ make clean $ make modules $ arm-linux-gcc test_drv_sem.c -o test_drv_sem
$ insmod drv_sem.ko drv_sem:INIT
$ ./test_drv_sem & $ top 789 779 0 S 1312 2% 0% ./test_drv_sem
$ ./test_drv_sem & can't open! # 打开文件失败 $ top 789 779 0 S 1312 2% 0% ./test_drv_sem # 依旧只有一个处于睡眠状态的测试程序.
|
信号量
信号量是用于保护临界区的一种常用方法, 只有得到信号量进程才能执行临界区代码.
与原子操作不同的是, 当获取不到信号量是, 进程进入休眠等待状态!
一但其他进程释放信号量, 该进程获得信号量后, 会恢复运行.
struct semaphore sem; static DECLARE_MUTEX(lock);
void sema_init (struct semaphore *sem, int val); void init_MUTEX(struct semaphore *sem);
void down(struct semaphore * sem); int down_interruptible(struct semaphore * sem); int down_trylock(struct semaphore * sem);
void up(struct semaphore * sem);
|
drv_sem.c
源码基于 驱动之基于中断设计按键驱动, 部分修改而来
只显示新增和修改的部分. 这样更直观易懂.
#include"drv_sem.h" #define DRV_KEY_INT_NODE_NAME "drv_sem"
atomic_t canopen = ATOMIC_INIT(1); static DECLARE_MUTEX(key_lock);
static int drv_key_int_open(struct inode *inode,struct file *filp) { int ret;
#if 0 if (!atomic_dec_and_test(&canopen)) { atomic_inc(&canopen); return -EBUSY; } #else down(&key_lock); #endif
......
return 0; }
static int drv_key_int_release(struct inode *inode,struct file *filp) { ......
#if 0 atomic_inc(&canopen); #else up(&key_lock); #endif return 0; }
|
其它文件 drv_sem.h
Makefile
test_drv_sem.c
和原子操作的部分一样. 不用修改
编译并测试
$ make clean $ make modules $ arm-linux-gcc test_drv_sem.c -o test_drv_sem
$ insmod drv_sem.ko drv_sem:INIT
$ ./test_drv_sem & $ top 789 779 0 S 1312 2% 0% ./test_drv_sem
$ ./test_drv_sem &
$ top 789 779 0 S 1312 2% 0% ./test_drv_sem 791 779 0 D 1308 2% 0% ./test_drv_sem
$ kill 789 $ top 791 779 0 S 1308 2% 0% ./test_drv_sem
|
阻塞和非阻塞
- 阻塞操作
- 系统默认就是阻塞操作
- 是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
- 被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
- 非阻塞操作
- 使用宏定义
O_NONBLOCK
- 进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
drv_sem.c
源码基于 驱动之基于中断设计按键驱动, 部分修改而来
只显示新增和修改的部分. 这样更直观易懂.
#include"drv_sem.h" #define DRV_KEY_INT_NODE_NAME "drv_sem"
atomic_t canopen = ATOMIC_INIT(1); static DECLARE_MUTEX(key_lock);
static int drv_key_int_open(struct inode *inode,struct file *filp) { int ret;
#if 0 if (!atomic_dec_and_test(&canopen)) { atomic_inc(&canopen); return -EBUSY; } #else if (filp->f_flags & O_NONBLOCK) { if (down_trylock(&key_lock)) return -EBUSY; } else { down(&key_lock); } #endif
......
return 0; }
static ssize_t drv_key_int_read(struct file *filp, char __user *ubuff,size_t count,loff_t *offp) { ...... if (count != 1) return -EINVAL;
if (filp->f_flags & O_NONBLOCK) { if (!ev_press) return -EAGAIN; } else { wait_event_interruptible(key_waitq, ev_press); }
ev_press = 0; if (copy_to_user(ubuff, &keys_val, 1)) { return -EFAULT; } return 1; }
static int drv_key_int_release(struct inode *inode,struct file *filp) { ......
#if 0 atomic_inc(&canopen); #else up(&key_lock); #endif return 0; }
|
文件 drv_sem.h
Makefile
和信号量的部分一样. 不用修改
阻塞是open函数的默认值, 其测试就不写了, 因为之前写的按键驱动测试都是阻塞的.
测试文件 unblock_drv_sem.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h>
int main(int argc, char **argv) { int ret, fd; unsigned char keys_val;
fd = open("/dev/drv_sem0", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("can't open!\n"); return -1; }
while (1) { ret = read(fd, &keys_val, 1); printf("keys_val=0x%x, ret=%d\n",keys_val,ret); sleep(3); }
return 0; }
|
编译并测试
$ make clean $ make modules $ arm-linux-gcc unblock_drv_sem.c -o unblock_drv_sem
$ insmod drv_sem.ko drv_sem:INIT
$ ./unblock_drv_sem & key_val=0x0, ret=-1 key_val=0x0, ret=-1 key_val=0x1, ret=1 $ top 789 779 0 S 1312 2% 0% ./unblock_drv_sem
$ ./unblock_drv_sem &
$ top 789 779 0 S 1312 2% 0% ./unblock_drv_sem
|