linux字符驱动之同步互斥按键驱动
2016-04-07 16:54
411 查看
上一节里,我们将在上一节的基础上修改驱动,将其修改为有异步通知功能的按键驱动,目标是,按下按键时,驱动主动去通知应用程序。是不是感觉驱动已经比较完善了,好像已经是完美无缺了?是不是这样呢?好像不是呢,有没有这么一种情况,多个进程想同时使用驱动的设备节点?在多线的环境下,分分钟可能会发生这种情况。
在这一节里,我们在上一节的基础上,实现同一时刻只能有一个进程使用同一个设备,例如:只能有一个进程,在同一时刻里使用/dev/buttons这个设备。
问:如何实现同一时刻只能有一个进程使用某个设备?
答:使用linux互斥机制
问:linux互斥机制有哪些?
答:有很多种,如:原子变量、互斥锁、信号量、自旋锁、读写锁等等
问:在这一节里,我们使用什么互斥机制?
答:原子变量/信号量,举二个例子来示范linux的互斥机制
问:如何使用原子变量的互斥机制?
答:先定义一个原子变量,然后再初始化它。具体如下:
问:与原子变量相关的函数有哪些?
答:原子操作指的是在执行过程中不会被别的代码路径所中断的操作,常用原子操作函数举例:
问:原子变量相关的函数在此驱动里,在哪来会被调用?
答:既然是防止多个进程打开同一个设备,那自然是在open函数就调用,在close函数也会被调用,示例如下:
驱动程序代码:
应用测试程序:
信号量互斥方式测试步骤:
由上面的测试结果可知:当多次执行./sixth_test &时,可发现进程602的状态为S即睡眠状态,而进程603的状态为D即僵死状态
,只有当我们杀死602进程时,603的状态才能变为S正常状态,这也就达到了互斥的目的。
在这一节里,我们在上一节的基础上,实现同一时刻只能有一个进程使用同一个设备,例如:只能有一个进程,在同一时刻里使用/dev/buttons这个设备。
问:如何实现同一时刻只能有一个进程使用某个设备?
答:使用linux互斥机制
问:linux互斥机制有哪些?
答:有很多种,如:原子变量、互斥锁、信号量、自旋锁、读写锁等等
问:在这一节里,我们使用什么互斥机制?
答:原子变量/信号量,举二个例子来示范linux的互斥机制
问:如何使用原子变量的互斥机制?
答:先定义一个原子变量,然后再初始化它。具体如下:
问:与原子变量相关的函数有哪些?
答:原子操作指的是在执行过程中不会被别的代码路径所中断的操作,常用原子操作函数举例:
问:原子变量相关的函数在此驱动里,在哪来会被调用?
答:既然是防止多个进程打开同一个设备,那自然是在open函数就调用,在close函数也会被调用,示例如下:
驱动程序代码:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <linux/poll.h> static struct class *sixthdrv_class; static struct class_device *sixthdrv_class_dev; //volatile unsigned long *gpfcon; //volatile unsigned long *gpfdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */ static volatile int ev_press = 0; static struct fasync_struct *button_async; struct pin_desc{ unsigned int pin; unsigned int key_val; }; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; /* * K1,K2,K3,K4对应GPG0,GPG3,GPG5,GPG6 */ struct pin_desc pins_desc[4] = { {S3C2410_GPG0, 0x01}, {S3C2410_GPG3, 0x02}, {S3C2410_GPG5, 0x03}, {S3C2410_GPG6, 0x04}, }; //static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量并初始化为1 static DECLARE_MUTEX(button_lock); //定义互斥锁 /* * 确定按键值 */ 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; } else { /* 按下 */ key_val = pindesc->key_val; } ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED); } static int sixth_drv_open(struct inode *inode, struct file *file) { #if 0 if (!atomic_dec_and_test(&canopen)) { atomic_inc(&canopen); return -EBUSY; } #endif if (file->f_flags & O_NONBLOCK) { if (down_trylock(&button_lock)) return -EBUSY; } else { /* 获取信号量 */ down(&button_lock); } /* GPG0,GPG3,GPG5,GPG6为中断引脚: EINT8,EINT11,EINT13,EINT14 */ request_irq(IRQ_EINT8, buttons_irq, IRQT_BOTHEDGE, "K1", &pins_desc[0]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "K2", &pins_desc[1]); request_irq(IRQ_EINT13, buttons_irq, IRQT_BOTHEDGE, "K3", &pins_desc[2]); request_irq(IRQ_EINT14, buttons_irq, IRQT_BOTHEDGE, "K4", &pins_desc[3]); return 0; } 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; } int sixth_drv_close(struct inode *inode, struct file *file) { //atomic_inc(&canopen); free_irq(IRQ_EINT8, &pins_desc[0]); free_irq(IRQ_EINT11, &pins_desc[1]); free_irq(IRQ_EINT13, &pins_desc[2]); free_irq(IRQ_EINT14, &pins_desc[3]); up(&button_lock); return 0; } static unsigned sixth_drv_poll(struct file *file, poll_table *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_fasync\n"); return fasync_helper (fd, filp, on, &button_async); } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__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", &sencod_drv_fops); sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ // gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); // gpfdat = gpfcon + 1; return 0; } static void sixth_drv_exit(void) { unregister_chrdev(major, "sixth_drv"); class_device_unregister(sixthdrv_class_dev); class_destroy(sixthdrv_class); // iounmap(gpfcon); return 0; } module_init(sixth_drv_init); module_exit(sixth_drv_exit); MODULE_LICENSE("GPL");
应用测试程序:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> /* sixthdrvtest */ int fd; void my_signal_fun(int signum) { unsigned char key_val; read(fd, &key_val, 1); printf("key_val: 0x%x\n", key_val); } int main(int argc, char **argv) { unsigned char key_val; int ret; int Oflags; //signal(SIGIO, my_signal_fun); fd = open("/dev/buttons", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("can't open!\n"); return -1; } //fcntl(fd, F_SETOWN, getpid()); //Oflags = fcntl(fd, F_GETFL); //fcntl(fd, F_SETFL, Oflags | FASYNC); while (1) { ret = read(fd, &key_val, 1); printf("key_val: 0x%x, ret = %d\n", key_val, ret); sleep(5); } return 0; }
信号量互斥方式测试步骤:
[WJ2440]# ls Qt fifth_drv.ko lib sddisk third_test TQLedtest fifth_test linuxrc second_drv.ko tmp app_test first_drv.ko mnt second_test udisk bin first_test opt sixth_drv.ko usr dev fourth_drv.ko proc sixth_test var driver_test fourth_test root sys web etc home sbin third_drv.ko [WJ2440]# insmod sixth_drv.ko [WJ2440]# lsmod sixth_drv 3472 0 - Live 0xbf000000 [WJ2440]# ls /dev/buttons -l crw-rw---- 1 root root 252, 0 Jan 2 04:47 /dev/buttons [WJ2440]# ./sixth_test & [WJ2440]# ./sixth_test & [WJ2440]# ps PID USER VSZ STAT COMMAND 1 root 2088 S init 2 root 0 SW< [kthreadd] 3 root 0 SW< [ksoftirqd/0] 4 root 0 SW< [events/0] 5 root 0 SW< [khelper] 11 root 0 SW< [async/mgr] 237 root 0 SW< [kblockd/0] 247 root 0 SW< [khubd] 254 root 0 SW< [kmmcd] 278 root 0 SW [pdflush] 279 root 0 SW [pdflush] 280 root 0 SW< [kswapd0] 325 root 0 SW< [aio/0] 329 root 0 SW< [nfsiod] 333 root 0 SW< [crypto/0] 443 root 0 SW< [mtdblockd] 557 root 0 SW< [usbhid_resumer] 573 root 0 SW< [rpciod/0] 587 root 1508 S EmbedSky_wdg 589 root 2092 S -/bin/sh 590 root 2088 S /usr/sbin/telnetd -l /bin/login 602 root 1428 S ./sixth_test 603 root 1428 D ./sixth_test 604 root 2092 R ps
由上面的测试结果可知:当多次执行./sixth_test &时,可发现进程602的状态为S即睡眠状态,而进程603的状态为D即僵死状态
,只有当我们杀死602进程时,603的状态才能变为S正常状态,这也就达到了互斥的目的。
相关文章推荐
- 进程间互斥
- linux定时任务的设置 crontab 配置指南
- linux解压缩及源码安装
- Kali Linux ***测试之拒绝服务***及防御
- linux下ACE工具包的编译安装
- Linux alias --设置命令的别名
- Linux useradd --添加用户账号
- linux字符驱动之poll机制按键驱动
- linux ppp上网的文章
- Linux零基础入学之1-2可用快照创建和服务器的组装
- linux 主要配置文件
- CentOS Rsync服务端与Windows cwRsync客户端实现数据同步
- Centos7下yum安装mysql
- 第四阶段 :安全与监控 Linux监控和安全运维
- Linux ac --在线时间统计
- Linux显示全部执行中的进程
- Linux服务器管理员Journald初级指南
- linux下配置JDK JAVA环境
- Linux下线程的调度策略与优先级
- Linux 下I/O多路复用总结