我們想來達(dá)到一個目的:同一時刻,只能有一個應(yīng)用程序打開/dev/buttons
一般的,我們想達(dá)到目的會想到下面這種方法:
static?int?canopen?=?1; static?int?sixth_drv_open(struct?inode?*inode,struct?file?*file) { ????if(--canopen?!=?0) ????{ ????????canopen++; ????????return?-EBUSY; ????} } static?int?sixth_drv_close?(struct?inode?*inode,?struct?file?*file) { ????canopen++; }
假設(shè)程序A來調(diào)用,那么進(jìn)入open函數(shù),canopen = 0,if條件不成立;如果A沒有釋放,程序B來調(diào)用時候,canopen = -1,if條件成立,return -EBUSY;
原則上是可以實(shí)現(xiàn),但是我們進(jìn)行–canopen操作實(shí)際上轉(zhuǎn)化程匯編使用三條指令(讀->改->寫)執(zhí)行的,由于Linux是多任務(wù)編程的。當(dāng)我們程序A進(jìn)行–canopen的讀取后,程序B剛好進(jìn)行那么就有可能發(fā)生程序AB都能調(diào)用驅(qū)動!看下圖分析:
下面來介紹三種解決上面bug的方法 一、原子操作:
原子操作指的是在執(zhí)行過程中不會被別的代碼路徑所中斷的操作。
?常用原子操作函數(shù)舉例: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。
Makefile
KERN_DIR?=?/work/system/linux-2.6.22.6 all: ????make?-C?$(KERN_DIR)?M=`pwd`?modules? clean: ????make?-C?$(KERN_DIR)?M=`pwd`?modules?clean ????rm?-rf?modules.order obj-m?+=?sixth_drv.o
驅(qū)動函數(shù):sixth_drv.c
#include#include#include#include#include#include#include#include#include#include#include#includestatic?struct?fasync_struct?*button_async; static?struct?class?*sixthdrv_class; static?struct?class_device?*sixthdrv_class_dev; static?DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*?中斷事件標(biāo)志,?中斷服務(wù)程序?qū)⑺?,s3c24xx_sixth_read將它清0?*/ static?volatile?int?ev_press?=?0; volatile?unsigned?long?*gpfcon; volatile?unsigned?long?*gpfdat; volatile?unsigned?long?*gpgcon; volatile?unsigned?long?*gpgdat; struct?pin_desc{ ????unsigned?int?pin; ????unsigned?int?key_val; }; /*?鍵值:?按下時,0x01、0x02、0x03?*/ /*?鍵值:?松開時,0x81、0x82、0x83?*/ static?unsigned?char?key_val; struct?pin_desc?pin_desc[3]?=?{ ????{S3C2410_GPF0,0X01}, ????{S3C2410_GPF2,0X02}, ????{S3C2410_GPG3,0X03}, }; atomic_t?canopen?=?ATOMIC_INIT(1);//定義原子變量canopen并初始化為1 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); ????????*gpfdat?|=?((1<<4)?|?(1<<5)?|?(1<key_val; ????????*gpfdat?&=?~((1<<4)?|?(1<<5)?|?(1<<6)); ????} ????ev_press?=?1;????????????????/*?表示中斷發(fā)生了?*/ ????wake_up_interruptible(&button_waitq);???/*?喚醒休眠的進(jìn)程?*/ ????kill_fasync?(&button_async,?SIGIO,?POLL_IN); ????return?IRQ_RETVAL(IRQ_HANDLED); } static?int?sixth_drv_open(struct?inode?*inode,struct?file?*file) { ????if(!atomic_dec_and_test(&canopen)) ????{ ????????atomic_inc(&canopen);? ????????return?-EBUSY; ????} ????/*?配置GPF0,2、GPG3為中斷引腳?*/ ????request_irq(IRQ_EINT0,?buttons_irq,IRQT_BOTHEDGE,"s2",&pin_desc[0]); ????request_irq(IRQ_EINT2,?buttons_irq,IRQT_BOTHEDGE,"s3",&pin_desc[1]); ????request_irq(IRQ_EINT11,buttons_irq,IRQT_BOTHEDGE,"s4",&pin_desc[2]); ????/*?配置GPF4、5、6為輸入引腳?*/ ????*gpfcon?&=?~((0x3<<4*2)?|?(0x3<<5*2)?|?(0x3<<6*2)); ????*gpfcon?|=??((1<<4*2)?|?(1<<5*2)?|?(1<<6*2)); ????return?0; } static?ssize_t?sixth_drv_read?(struct?file?*file,?char?__user?*buf,?size_t?count,?loff_t?*ppos) { ????//看用戶需要讀取的空間,和這里的是否相同 ????if(count?!=?1) ????????return?-EINVAL; ????/*?如果無按鍵動作發(fā)生,則進(jìn)行休眠狀態(tài)?*/ ????/*?如果ev_press等于0,休眠?*/ ????wait_event_interruptible(button_waitq,ev_press); ????/*?如果有按鍵動作發(fā)生,則返回按鍵的值?*/ ????copy_to_user(buf,&key_val,1); ????ev_press?=?0; ????return?1; } static?int?sixth_drv_close?(struct?inode?*inode,?struct?file?*file) { ????atomic_inc(&canopen);? ????free_irq(IRQ_EINT0,??&pin_desc[0]); ????free_irq(IRQ_EINT2,??&pin_desc[1]); ????free_irq(IRQ_EINT11,?&pin_desc[2]); ????return?0; } static?unsigned?int?sixth_drv_poll(struct?file?*file,?struct?poll_table_struct?*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_fasyncn"); ????return?fasync_helper?(fd,?filp,?on,?&button_async); } static?struct?file_operations?sixth_drv_fops?=?{ ????.owner???=?THIS_MODULE,?/*?這是一個宏,推向編譯模塊時自動創(chuàng)建的__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",&sixth_drv_fops); ????????sixthdrv_class?=?class_create(THIS_MODULE,"sixthdrv"); ????????sixthdrv_class_dev?=?class_device_create(sixthdrv_class,NULL,MKDEV(major,0),NULL,"buttons"); ????????gpfcon?=?(volatile?unsigned?long?*)ioremap(0x56000050,16); ????????gpfdat?=?gpfcon?+?1; ????????gpgcon?=?(volatile?unsigned?long?*)ioremap(0x56000060,16); ????????gpgdat?=?gpgcon?+?1; ????????return?0; } static?int?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");
驅(qū)動測試函數(shù):sixthdrvtest.c
#include#include#include#include#include#include#include#include/*? ?*?sixthdrvtest ?*/ int?fd; void?czg_signal_handler(int?signum) { ????unsigned?char?key_val?=?0; ????read(fd,&key_val,1); ????printf("key_val:?0x%xn",key_val); } int?main(int?argc,?char?**argv) { ????int?ret; ????int?oflags; ????signal(SIGIO,czg_signal_handler); ????fd?=?open("/dev/buttons",O_RDWR); ????if(fd?<?0) ????{ ????????printf("can't?open!n"); ????????return?-1; ????} ????fcntl(fd,F_SETOWN,getpid());?//?告訴內(nèi)核,發(fā)給誰 ????oflags?=?fcntl(fd,F_GETFL); ????fcntl(fd,F_SETFL,oflags?|?FASYNC);?//?改變fasync標(biāo)記, ????//最終會調(diào)用到驅(qū)動的faync?>?fasync_helper:初始化/釋放fasync_struct ????//然后當(dāng)按鍵按下時候,在irqreturn_t?buttons_irq中斷處理中調(diào)用kill_fasync ????while(1) ????{ ????????sleep(1000); ????} ????return?0; }
測試:
make arm-linux-gcc?-o?sixthdrvtest?sixthdrvtest.c? cp?sixthdrvtest?sixth_drv.ko?/work/nfs_root/czg insmod?sixth_drv.ko ./sixthdrvtest?&
二、信號量:
信號量(semaphore)是用于保護(hù)臨界區(qū)的一種常用方法,只有得到信號量的進(jìn)程才能執(zhí)行臨界區(qū)代碼。當(dāng)獲取不到信號量時,進(jìn)程進(jìn)入休眠等待狀態(tài)。
//定義信號量 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);
代碼只需要在上面的驅(qū)動程序sixth_drv.c稍做修改
static?DECLARE_MUTEX(button_lock);?????//定義互斥鎖 /*?獲?取?信?號?量?*/ down(&button_lock); /*?釋放信號量?*/ up(&button_lock);
三、阻塞: 阻塞操作:
是指在執(zhí)行設(shè)備操作時若不能獲得資源則掛起進(jìn)程,直到滿足可操作的條件后再進(jìn)行操作。被掛起的進(jìn)程進(jìn)入休眠狀態(tài),被從調(diào)度器的運(yùn)行隊(duì)列移走,直到等待的條件被滿足。
非阻塞操作:
進(jìn)程在不能進(jìn)行設(shè)備操作時并不掛起,它或者放棄,或者不停地查詢,直至可以進(jìn)行操作為止。
代碼在上面的驅(qū)動程序sixth_drv.c稍做修改
static?int?sixth_drv_open(struct?inode?*inode,struct?file?*file) { ????if?(file->f_flags?&??O_NONBLOCK) ????{ ????????if?(down_trylock(&button_lock)) ????????????return?-EBUSY; ????} ????else ????{ ????????/*?獲?取?信?號?量?*/ ????????down(&button_lock); ????} } /*************************************************/ static?ssize_t?sixth_drv_read?(struct?file?*file,?char?__user?*buf,?size_t?count,?loff_t?*ppos) { ????//看用戶需要讀取的空間,和這里的是否相同 ????if(count?!=?1) ????????return?-EINVAL; ????if(file->f_flags?&?O_NONBLOCK) ????{ ????????if(!ev_press) ????????????return?-EAGAIN; ????} ????else ????{ ????/*?如果無按鍵動作發(fā)生,則進(jìn)行休眠狀態(tài)?*/ ????/*?如果ev_press等于0,休眠?*/ ????wait_event_interruptible(button_waitq,ev_press); ????} }
阻塞:
驅(qū)動測試代碼:sixthdrvtest.c
#include#include#include#include#include#include#include#include/*? ?*?sixthdrvtest ?*/ int?fd; void?czg_signal_handler(int?signum) { ????unsigned?char?key_val?=?0; ????read(fd,&key_val,1); ????printf("key_val:?0x%xn",key_val); } int?main(int?argc,?char?**argv) { ????int?ret; ????int?oflags; ????unsigned?char?key_val?=?0; ????//signal(SIGIO,czg_signal_handler); ????fd?=?open("/dev/buttons",O_RDWR); ????if(fd?<?0) ????{ ????????printf("can't?open!n"); ????????return?-1; ????} ????//fcntl(fd,F_SETOWN,getpid());?//?告訴內(nèi)核,發(fā)給誰 ????//oflags?=?fcntl(fd,F_GETFL); ????//fcntl(fd,F_SETFL,oflags?|?FASYNC);?//?改變fasync標(biāo)記, ????//最終會調(diào)用到驅(qū)動的faync?>?fasync_helper:初始化/釋放fasync_struct ????//然后當(dāng)按鍵按下時候,在irqreturn_t?buttons_irq中斷處理中調(diào)用kill_fasync ????while(1) ????{ ????????read(fd,&key_val,1); ????????printf("key_val:?0x%xn",key_val); ????????//sleep(1000); ????} ????return?0; }
非阻塞:
驅(qū)動測試代碼:sixthdrvtest.c
#include#include#include#include#include#include#include#include/*? ?*?sixthdrvtest ?*/ int?fd; void?czg_signal_handler(int?signum) { ????unsigned?char?key_val?=?0; ????read(fd,&key_val,1); ????printf("key_val:?0x%xn",key_val); } int?main(int?argc,?char?**argv) { ????int?ret; ????int?oflags; ????unsigned?char?key_val?=?0; ????//signal(SIGIO,czg_signal_handler); ????fd?=?open("/dev/buttons",O_RDWR?|?O_NONBLOCK); ????if(fd?<?0) ????{ ????????printf("can't?open!n"); ????????return?-1; ????} ????//fcntl(fd,F_SETOWN,getpid());?//?告訴內(nèi)核,發(fā)給誰 ????//oflags?=?fcntl(fd,F_GETFL); ????//fcntl(fd,F_SETFL,oflags?|?FASYNC);?//?改變fasync標(biāo)記, ????//最終會調(diào)用到驅(qū)動的faync?>?fasync_helper:初始化/釋放fasync_struct ????//然后當(dāng)按鍵按下時候,在irqreturn_t?buttons_irq中斷處理中調(diào)用kill_fasync ????while(1) ????{ ????????ret?=?read(fd,&key_val,1); ????????printf("key_val:?0x%x,ret?=?%dn",key_val,ret); ????????sleep(4); ????} ????return?0; }