引入
当设备被一个程序打开时,存在被另一个程序打开的可能,如果两个或多个程序同时对设备文件进行写操作,这就是说我们的设备资源同时被多个进程使用,对共享资源(硬件资源、和软件上的全局变量、静态变量等)的访问则很容易导致竞态。
显然这不是我们想要的,所以本节引入互斥的概念:实现同一时刻,只能一个应用程序使用驱动程序
互斥其实现很简单,就是采用一些标志,当文件被一个进程打开后,就会设置该标志,使其他进程无法打开设备文件
目的
同一时刻,只允许驱动程序被一个进程打开
1.其中的标志需要使用函数来操作,不能直接通过判断变量来操作标志
eg:
open中记录打开的次数,can_open_cnt
表示运行打开的次数,定义为1表示只允许一个设备打开.
但是在汇编中一个c代码的加减实际上是分为a.读----b.修改----c.写回
的过程,
当A程序执行上述的a语句时,切换到B程序执行此程序,也执行到此语句的a.读,
由于A并未修改can_open_cnt = 0,B也能正常打开,B关闭后,can_open_cnt++ = 1
继续执行A。由此看来,使用判断变量来操作标志存在篡改的过程。
1 int can_open_cnt =1; 2 // open 3 if (--can_open_cnt != 0) //先减后判断 4 { //0-->无进程访问驱动, <0 -->已有进程访问 5 can_open_cnt++; 6 return _EBUSY 7 } 8 //close 9 can_open_cnt++;
1.1所以采用某种函数来实现,保证执行过程不被其他行为打断,有两种类型函数可以实现:
原子操作(像原子一样不可再细分不可被中途打断)
当多个进程同时访问同一个驱动时,只能有一个进程访问成功,其它进程会退出
互斥信号量操作
比如:A、B进程同时访问同一个驱动时,只有A进程访问成功了,B进程进入休眠等待状态,当A进程执行完毕释放后,等待状态的B进程又来访问,保证一个一个进程都能访问
2. 原子操作详解
1 常用原子操作函数举例: 2 atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0 3 atomic_read(atomic_t *v); //返回原子变量的值 4 void atomic_inc(atomic_t *v); //原子变量增加1 5 void atomic_dec(atomic_t *v); //原子变量减少1 6 int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
2.1修改驱动程序
定义原子变量:
1 /*定义原子变量canopen并初始化为1 */ 2 atomic_t canopen = ATOMIC_INIT(1);
在.open成员函数里添加:
1 /*自减操作后测试其是否为0,为0则返回true,否则返回false */ 2 if(!atomic_dec_and_test(&canopen)) 3 { 4 atomic_inc(&canopen); //++,复位 5 return -1; 6 }
在. release成员函数里添加:
atomic_inc(&canopen); //++,复位
驱动程序:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <asm/irq.h> 9 #include <asm/io.h> 10 #include <asm/arch/regs-gpio.h> 11 #include <asm/hardware.h> 12 #include <linux/poll.h> 13 14 15 static struct class *sixthdrv_class; 16 static struct class_device *sixthdrv_class_dev; 17 18 volatile unsigned long *gpfcon; 19 volatile unsigned long *gpfdat; 20 21 volatile unsigned long *gpgcon; 22 volatile unsigned long *gpgdat; 23 24 25 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 26 27 /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */ 28 static volatile int ev_press = 0; 29 30 static struct fasync_struct *button_async; 31 32 33 struct pin_desc{ 34 unsigned int pin; 35 unsigned int key_val; 36 }; 37 38 39 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ 40 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ 41 static unsigned char key_val; 42 43 struct pin_desc pins_desc[4] = { 44 {S3C2410_GPF0, 0x01}, 45 {S3C2410_GPF2, 0x02}, 46 {S3C2410_GPG3, 0x03}, 47 {S3C2410_GPG11, 0x04}, 48 }; 49 50 static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量并初始化为1 51 //static DECLARE_MUTEX(button_lock); //定义互斥锁 52 53 /* 54 * 确定按键值 55 */ 56 static irqreturn_t buttons_irq(int irq, void *dev_id) 57 { 58 struct pin_desc * pindesc = (struct pin_desc *)dev_id; 59 unsigned int pinval; 60 61 pinval = s3c2410_gpio_getpin(pindesc->pin); 62 63 if (pinval) 64 { 65 /* 松开 */ 66 key_val = 0x80 | pindesc->key_val; 67 } 68 else 69 { 70 /* 按下 */ 71 key_val = pindesc->key_val; 72 } 73 74 ev_press = 1; /* 表示中断发生了 */ 75 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ 76 77 kill_fasync (&button_async, SIGIO, POLL_IN); 78 79 return IRQ_RETVAL(IRQ_HANDLED); 80 } 81 82 static int sixth_drv_open(struct inode *inode, struct file *file) 83 { 84 85 if (!atomic_dec_and_test(&canopen)) //自减操作 86 { 87 atomic_inc(&canopen); //原子变量加1 88 return -EBUSY; 89 } 90 91 /* 配置GPF0,2为输入引脚 */ 92 /* 配置GPG3,11为输入引脚 */ 93 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 94 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); 95 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); 96 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); 97 98 return 0; 99 } 100 101 ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 102 { 103 if (size != 1) 104 return -EINVAL; 105 106 if (file->f_flags & O_NONBLOCK) 107 { 108 if (!ev_press) 109 return -EAGAIN; 110 } 111 else 112 { 113 /* 如果没有按键动作, 休眠 */ 114 wait_event_interruptible(button_waitq, ev_press); 115 } 116 117 /* 如果有按键动作, 返回键值 */ 118 copy_to_user(buf, &key_val, 1); 119 ev_press = 0; 120 121 return 1; 122 } 123 124 125 int sixth_drv_close(struct inode *inode, struct file *file) 126 { 127 atomic_inc(&canopen); //原子变量加1 128 free_irq(IRQ_EINT0, &pins_desc[0]); 129 free_irq(IRQ_EINT2, &pins_desc[1]); 130 free_irq(IRQ_EINT11, &pins_desc[2]); 131 free_irq(IRQ_EINT19, &pins_desc[3]); 132 //up(&button_lock); 133 return 0; 134 } 135 136 static unsigned sixth_drv_poll(struct file *file, poll_table *wait) 137 { 138 unsigned int mask = 0; 139 poll_wait(file, &button_waitq, wait); // 不会立即休眠 140 141 if (ev_press) 142 mask |= POLLIN | POLLRDNORM; 143 144 return mask; 145 } 146 147 static int sixth_drv_fasync (int fd, struct file *filp, int on) 148 { 149 printk("driver: sixth_drv_fasync "); 150 return fasync_helper (fd, filp, on, &button_async); 151 } 152 153 154 static struct file_operations sencod_drv_fops = { 155 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 156 .open = sixth_drv_open, 157 .read = sixth_drv_read, 158 .release = sixth_drv_close, 159 .poll = sixth_drv_poll, 160 .fasync = sixth_drv_fasync, 161 }; 162 163 164 int major; 165 static int sixth_drv_init(void) 166 { 167 major = register_chrdev(0, "sixth_drv", &sencod_drv_fops); 168 169 sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); 170 171 sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ 172 173 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); 174 gpfdat = gpfcon + 1; 175 176 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); 177 gpgdat = gpgcon + 1; 178 179 return 0; 180 } 181 182 static void sixth_drv_exit(void) 183 { 184 unregister_chrdev(major, "sixth_drv"); 185 class_device_unregister(sixthdrv_class_dev); 186 class_destroy(sixthdrv_class); 187 iounmap(gpfcon); 188 iounmap(gpgcon); 189 return 0; 190 } 191 192 193 module_init(sixth_drv_init); 194 195 module_exit(sixth_drv_exit); 196 197 MODULE_LICENSE("GPL");
测试程序:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdio.h> 5 #include <poll.h> 6 #include <signal.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 11 12 /* sixthdrvtest 13 */ 14 int fd; 15 16 void my_signal_fun(int signum) 17 { 18 unsigned char key_val; 19 read(fd, &key_val, 1); 20 printf("key_val: 0x%x ", key_val); 21 } 22 23 int main(int argc, char **argv) 24 { 25 unsigned char key_val; 26 int ret; 27 int Oflags; 28 29 //signal(SIGIO, my_signal_fun); 30 31 //fd = open("/dev/buttons", O_RDWR | O_NONBLOCK); 32 fd = open("/dev/buttons", O_RDWR); 33 if (fd < 0) 34 { 35 printf("can't open! "); 36 return -1; 37 } 38 39 //fcntl(fd, F_SETOWN, getpid()); 40 41 //Oflags = fcntl(fd, F_GETFL); 42 43 //fcntl(fd, F_SETFL, Oflags | FASYNC); 44 45 46 while (1) 47 { 48 ret = read(fd, &key_val, 1); 49 printf("key_val: 0x%x, ret = %d ", key_val, ret); 50 sleep(5); 51 } 52 53 return 0; 54 }
如下图,可以看到第一个进程访问驱动成功,后面的就再也不能访问成功了
3.互斥信号量详解
互斥信号量(semaphore)是用于保护临界区的一种常用方法,
应用程序在操作之前,要先申请信号量,申请不到的话,要么返回,要么等休眠
如果能申请到信号量,进程才能继续操作,操作完毕后,要释放信号量。
释放了之后,如果有其他的应用程序等待获取此信号量的话,就要去唤醒那个应用程序。
3.1信号量函数如下:
/*注意: 在2.6.36版本后这个函数DECLARE_MUTEX修改成DEFINE_SEMAPHORE了*/ 1)static DECLARE_MUTEX(button_lock); //定义互斥锁button_lock,被用来后面的down和up用 2)void down(struct semaphore * sem); // 获取不到就进入不被中断的休眠状态(down函数中睡眠) 3)int down_interruptible(struct semaphore * sem); //获取不到就进入可被中断的休眠状态(down函数中睡眠) 4)int down_trylock(struct semaphore * sem); //试图获取信号量,获取不到则立刻返回正数 5)void up(struct semaphore * sem); //释放信号量
3.2修改驱动程序(以down函数获取为例)
(1)定义互斥锁变量:
/*定义互斥锁button_lock,被用来后面的down()和up()使用 */ static DECLARE_MUTEX(button_lock);
(2)在.open成员函数里添加:
/* 获取不到就进入不被中断的休眠状态(down函数中睡眠) */ down(&button_lock);
(3)在. release成员函数里添加:
/* 释放信号量 */ up(&button_lock);
驱动程序如下:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <asm/irq.h> 9 #include <asm/io.h> 10 #include <asm/arch/regs-gpio.h> 11 #include <asm/hardware.h> 12 #include <linux/poll.h> 13 14 15 static struct class *sixthdrv_class; 16 static struct class_device *sixthdrv_class_dev; 17 18 volatile unsigned long *gpfcon; 19 volatile unsigned long *gpfdat; 20 21 volatile unsigned long *gpgcon; 22 volatile unsigned long *gpgdat; 23 24 25 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 26 27 /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */ 28 static volatile int ev_press = 0; 29 30 static struct fasync_struct *button_async; 31 32 33 struct pin_desc{ 34 unsigned int pin; 35 unsigned int key_val; 36 }; 37 38 39 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ 40 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ 41 static unsigned char key_val; 42 43 struct pin_desc pins_desc[4] = { 44 {S3C2410_GPF0, 0x01}, 45 {S3C2410_GPF2, 0x02}, 46 {S3C2410_GPG3, 0x03}, 47 {S3C2410_GPG11, 0x04}, 48 }; 49 50 static DECLARE_MUTEX(button_lock); //定义互斥锁 51 /* 52 * 确定按键值 53 */ 54 static irqreturn_t buttons_irq(int irq, void *dev_id) 55 { 56 struct pin_desc * pindesc = (struct pin_desc *)dev_id; 57 unsigned int pinval; 58 59 pinval = s3c2410_gpio_getpin(pindesc->pin); 60 61 if (pinval) 62 { 63 /* 松开 */ 64 key_val = 0x80 | pindesc->key_val; 65 } 66 else 67 { 68 /* 按下 */ 69 key_val = pindesc->key_val; 70 } 71 72 ev_press = 1; /* 表示中断发生了 */ 73 wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ 74 75 kill_fasync (&button_async, SIGIO, POLL_IN); 76 77 return IRQ_RETVAL(IRQ_HANDLED); 78 } 79 80 static int sixth_drv_open(struct inode *inode, struct file *file) 81 { 82 #if 0 83 if (!atomic_dec_and_test(&canopen)) //自减操作 84 { 85 atomic_inc(&canopen); //原子变量加1 86 return -EBUSY; 87 } 88 #endif 89 /*获取信号量*/ 90 down(&button_lock); 91 //若为第一次执行,则会获得信号量,第二次打开的话,就会进入休眠 92 93 /* 配置GPF0,2为输入引脚 */ 94 /* 配置GPG3,11为输入引脚 */ 95 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); 96 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); 97 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); 98 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); 99 100 return 0; 101 } 102 103 ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 104 { 105 if (size != 1) 106 return -EINVAL; 107 108 if (file->f_flags & O_NONBLOCK) 109 { 110 if (!ev_press) 111 return -EAGAIN; 112 } 113 else 114 { 115 /* 如果没有按键动作, 休眠 */ 116 wait_event_interruptible(button_waitq, ev_press); 117 } 118 119 /* 如果有按键动作, 返回键值 */ 120 copy_to_user(buf, &key_val, 1); 121 ev_press = 0; 122 123 return 1; 124 } 125 126 127 int sixth_drv_close(struct inode *inode, struct file *file) 128 { 129 free_irq(IRQ_EINT0, &pins_desc[0]); 130 free_irq(IRQ_EINT2, &pins_desc[1]); 131 free_irq(IRQ_EINT11, &pins_desc[2]); 132 free_irq(IRQ_EINT19, &pins_desc[3]); 133 up(&button_lock); /*释放信号量*/ 134 return 0; 135 } 136 137 static unsigned sixth_drv_poll(struct file *file, poll_table *wait) 138 { 139 unsigned int mask = 0; 140 poll_wait(file, &button_waitq, wait); // 不会立即休眠 141 142 if (ev_press) 143 mask |= POLLIN | POLLRDNORM; 144 145 return mask; 146 } 147 148 static int sixth_drv_fasync (int fd, struct file *filp, int on) 149 { 150 printk("driver: sixth_drv_fasync "); 151 return fasync_helper (fd, filp, on, &button_async); 152 } 153 154 155 static struct file_operations sencod_drv_fops = { 156 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 157 .open = sixth_drv_open, 158 .read = sixth_drv_read, 159 .release = sixth_drv_close, 160 .poll = sixth_drv_poll, 161 .fasync = sixth_drv_fasync, 162 }; 163 164 165 int major; 166 static int sixth_drv_init(void) 167 { 168 major = register_chrdev(0, "sixth_drv", &sencod_drv_fops); 169 170 sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); 171 172 sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ 173 174 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); 175 gpfdat = gpfcon + 1; 176 177 gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); 178 gpgdat = gpgcon + 1; 179 180 return 0; 181 } 182 183 static void sixth_drv_exit(void) 184 { 185 unregister_chrdev(major, "sixth_drv"); 186 class_device_unregister(sixthdrv_class_dev); 187 class_destroy(sixthdrv_class); 188 iounmap(gpfcon); 189 iounmap(gpgcon); 190 return 0; 191 } 192 193 194 module_init(sixth_drv_init); 195 196 module_exit(sixth_drv_exit); 197 198 MODULE_LICENSE("GPL");
测试程序同上一个。
测试:
下图,只有831进程在处于静止状态(没有中断触发)
832进程处于down休眠状态,直至831释放掉信号
灭掉进程831
多个信号量访问时, 会一个一个进程来排序访问
4.阻塞与非阻塞
4.1概念
- 阻塞,
如果不满足条件会挂起,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
2. 非阻塞,
不满足条件返回,进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止
4.2 怎么来判断阻塞与非阻塞操作?
1.在用户层open时,默认为阻塞操作,如果添加了” O_NONBLOCK”,表示使open()、read()、write()不被阻塞
实例:
fd = open("/dev/buttons",O_RDWR); //使用阻塞操作 fd = open("/dev/buttons ", O_RDWR | O_NONBLOCK); //非阻塞操作
2.然后在驱动设备中,通过file_operations成员函数.open、.read、.write带的参数file->f_flags 来查看用户层访问时带的参数
实例:
1 if( file->f_flags & O_NONBLOCK ) //非阻塞操作,获取不到则退出 2 { 3 ... ... 4 } 5 else //阻塞操作,获取不到则进入休眠 6 { 7 ... ... 8
4.3 修改应用程序,通过判断file->f_flags来使用阻塞操作还是非阻塞操作
(1)定义互斥锁变量:
/*定义互斥锁button_lock,被用来后面的down()和up()使用 */ static DECLARE_MUTEX(button_lock);
(2)在.open成员函数里添加:
if( file->f_flags & O_NONBLOCK ) //非阻塞操作 { if(down_trylock(&button_lock) ) //尝试获取信号量,获取不到则退出 return -1; } else //阻塞操作 { down(&button_lock); //获取信号量,获取不到则进入休眠 }
(3)在read成员函数里添加
ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { if (size != 1) return -EINVAL; if (file->f_flags & O_NONBLOCK) { /*非阻塞*/ if (!ev_press) return -EAGAIN; } else { /*阻塞*/ /* 如果没有按键动作, 休眠 */ wait_event_interruptible(button_waitq, ev_press); } /* 如果有按键动作, 返回键值 */ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; }
(4)在. release成员函数里添加:
/*释放信号量*/ up(&button_lock);
测试阻塞:
1 int main(int argc,char **argv) 2 { 3 int oflag; 4 unsigned int val=0; 5 fd=open("/dev/buttons",O_RDWR); //使用阻塞操作 6 if(fd<0) 7 {printf("can't open, fd=%d ",fd); 8 return -1;} 9 else 10 { 11 printf("can open,PID=%d ",getpid()); //打开成功,打印pid进程号 12 } 13 14 while(1) 15 { 16 val=read( fd, &ret, 1); //读取驱动层数据 17 printf("key_vale=0X%x,retrun=%d ",ret,val); 18 } 19 return 0;
测试非阻塞:
上面的测试程序
第5行:
fd=open("/dev/buttons",O_RDWR | O_NONBLOCK);
参考