在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源) ,可能会引发“竞态” ,因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用) 。
自旋锁与信号量“类似而不类” ,类似说的是它们功能上的相似性, “不类”指代它们在本质和实现机理上完全不一样,不属于一类。
自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁, “自旋”就是“在原地打转” 。而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的“不类” 。
但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。这就是它们的“类似” 。
鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,会只能在进程上下文使用。如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断) ,就必须使用自旋锁。
先贴代码
#include <linux/module.h>
#include #include #include #include #include #include /* * xiaoyang yi @HIT 2011-9-22 * char device driver with sync(semaphore and spinlock) */ MODULE_LICENSE('GPL'); static int MAJOR_NUM=0; static struct semaphore sem; static int global_var = 0; static int global_var_count = 0; static spinlock_t spin; static ssize_t globalvar_read(struct file*,char*,size_t, loff_t*); static ssize_t globalvar_write(struct file*,const char*,size_t, loff_t*); static int globalvar_open(struct inode*node, struct file* fp); static int globalvar_release(struct inode*node, struct file* fp); /*init the file_operation structure*/ static struct file_operations globalvar_fpos={ .read = globalvar_read, .write = globalvar_write, .open = globalvar_open, .release = globalvar_release, }; static int __init globalvar_init(void) { int ret; /*register device drivre*/ ret = register_chrdev(MAJOR_NUM,'globalvar',&globalvar_fpos); if(ret < 0){ printk('globalvar reg failed!n'); }else{ printk('globalvar reg okn'); spin_lock_init(&spin); } if(MAJOR_NUM == 0){ MAJOR_NUM = ret; } sema_init(&sem,1); return ret; } static void __exit globalvar_exit() { unregister_chrdev(MAJOR_NUM,'globalvar'); } static ssize_t globalvar_read(struct file* fp, char* buf, size_t len, loff_t* off) { printk('[debug]:globalvar read get in!n'); /*get semaphore*/ if(down_interruptible(&sem)){ return -ERESTARTSYS; } /*copy from kernel to user space*/ if(copy_to_user(buf,&global_var,sizeof(int)) != 0){ /*release semaphore*/ up(&sem); return -EFAULT; } /*release semaphore*/ up(&sem); printk('[debug]:globalvar read ok!n'); return sizeof(int); } static ssize_t globalvar_write(struct file* fs,const char* buf, size_t len, loff_t* off) { /*get semaphore*/ if(down_interruptible(&sem)){ return -ERESTARTSYS; } printk('down_interruptible ok!n'); if(copy_from_user(&global_var,buf,sizeof(int) != 0)){ /*release semaphore*/ up(&sem); return -EFAULT; } /*release semaphore*/ up(&sem); return sizeof(int); } /* * open device with checking busy. * if busy,count++;else return 0 */ static int globalvar_open(struct inode*node, struct file* fp) { /*get spinlock*/ spin_lock(&spin); /*reach criticle section*/ if(global_var_count){ spin_unlock(&spin); printk('[debug]:globalvar open fialed!n'); return -EBUSY; } /*release spinlock*/ global_var_count++; spin_unlock(&spin); printk('[debug]:globalvar open ok!n'); return 0; } static int globalvar_release(struct inode*node, struct file* fp) { spin_lock(&spin); global_var_count--; spin_unlock(&spin); return 0; } /*module setting*/ module_init(globalvar_init); module_exit(globalvar_exit); /*this is end of file*/ makefile如下: CC=gcc PWD:=$(shell pwd) KERNELDIR=/usr/src/kernels/2.6.40.4-5.fc15.i686.PAE INSTALLDIR=./ INC=$(KERNELDIR)/include MOD_NAME=globalvar obj-m:=globalvar.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules -I $(INC) @echo 'compiled ok!' run: sudo insmod *.ko install: @echo 'install ok!' unstall: sudo rmmod $(MOD_NAME) sudo rmmod /dev/$(MOD_NAME) @echo 'unregister module ok!' debug: cat /var/log/messages | tail result: cat /proc/devices help: @echo 'install with 'mknod /dev/devname c dev_num 0' cmd' clean: rm -rf *.o *.ko *.mod.c *.markers *.order *.symvers 程序很简单,就不多解释了。 在测试时出现过Oops NULL Pointer内核错误。 Oops信息如下: UG: sleeping function called from invalid context at arch/x86/mm/fault.c:1103 in_atomic(): 0, irqs_disabled(): 1, pid: 11416, name: a.out Pid: 11416, comm: a.out Not tainted 2.6.40.4-5.fc15.i686.PAE #1 Call Trace: [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ BUG: unable to handle kernel NULL pointer dereference at (null) IP: [ *pdpt = 000000002fed0001 *pde = 0000000000000000 Oops: 0000 [#1] SMP 是空指针错误,由其堆栈及调用信息可大概猜出是semaphore或者spin的错误。后来排查发现semaphore没有init,如果信号量用户互斥(mutex:mutual exclusion),将信号量的值初始化的1,这样只允许一个进程或者线程执行。这种情况下,信号量也成为互斥锁。从实践现象看来;没有初始化信号量直接down和up会造成访问错误,具体原因还没查到。炯! 应用程序: #include #include #include #include #include /* * xiaoyang yi @HIT 2011.9.24 * this is a test for char device 'globalvar' */ int main() { int fd,num; /*opemn device*/ fd = open('/dev/globalvar',O_RDWR,S_IRUSR|S_IWUSR); if(fd != -1){ /*read globalvar the first time*/ read(fd,&num,sizeof(int)); printf('read first time,globalvar=%dn',num); /*writing test*/ printf('print number to write: '); scanf('%d',&num); write(fd,&num,sizeof(int)); /*read globalvar*/ read(fd,&num,sizeof(int)); printf('read again globalvar=%dn',num); close(fd); }else{ printf('device open failed!n'); } return 0; }
上一篇:linux驱动学习(4)--阻塞和非阻塞
下一篇:linux驱动学习(2)-beep驱动
- 热门资源推荐
- 热门放大器推荐
- 羽毛球训练监测器
- 一种 16 位、300 kSPS 低功耗逐次逼近 ADC 系统,具有用于高达 4 kHz 的亚奈奎斯特输入信号的最佳低功耗驱动放大器
- SY898547 EV,用于 SY89547、2.5V、3.2 Gbps LVDS 多路复用器的评估板,带内部终端
- OP484ESZ-REEL 2.5V 基准的典型应用在单 3V 电源上运行
- NCV3063 1.5A 电压反相稳压器的典型电压反相应用原理图
- 具有欠压锁定功能的 LT3973IDD-3.3 5V 降压转换器的典型应用
- NSI45030ZT1G 单串LED恒流LED驱动器的典型应用
- NCP1587A 低压同步降压控制器的典型应用
- 福禄克早期型号 8020A 万用表原理图
- 使用 Richtek Technology Corporation 的 RT8237C 的参考设计

现代雷达系统的信号设计
TS339CJ

BFR340T






京公网安备 11010802033920号