上次调试好了一个基本的字符驱动,支持并发控制。在实际的使用中,read 和 write 系统调用要支持阻塞访问的。这次使用等待队列实现阻塞。在实现阻塞的代码中涉及进程调度的内容。下面是read和write 函数:
- static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
- 62 {
- 63 int ret = 0;
- 64 struct cfifo_t *dev = filp->private_data;
- 65 DECLARE_WAITQUEUE(wait, current);
- 66
- 67 down(&dev->sem);
- 68 add_wait_queue(&dev->r_wait, &wait);
- 69
- 70 while(dev->current_len == 0){
- 71 if(filp->f_flags & O_NONBLOCK){
- 72 ret = -EAGAIN;
- 73 goto out;
- 74 }
- 75
- 76 __set_current_state(TASK_INTERRUPTIBLE);
- 77 up(&dev->sem);
- 78
- 79 schedule(); /*调度其他进程运行*/
- 80 if(signal_pending(current)){
- 81 ret = -ERESTARTSYS;
- 82 goto out2;
- 83 }
- 84
- 85 down(&dev->sem);
- 86 }
- 87
- 88 if(count > dev->current_len)
- 89 count = dev->current_len;
- 90
- 91 if(copy_to_user(buf, (void*)(dev->mem), count)){
- 92 ret = -EFAULT;
- 93 goto out;
- 94 } else{
- 95 memcpy(dev->mem, dev->mem + count, dev->current_len - count);
- 96 dev->current_len -= count;
- 97 *fpos =dev->current_len;
- 98
- 99 wake_up_interruptible(&dev->w_wait);
- 100 ret =count;
- 101
- 102 printk(KERN_INFO "read %s %d bites \n", buf, count);
- 103 }
- 104
- 105 out:
- 106 up(&dev->sem);
- 107 out2:
- 108 remove_wait_queue(&dev->r_wait, &wait);
- 109 set_current_state(TASK_RUNNING);
- 110
- 111 return ret;
- 112 }
- 113
- static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
- 115 {
- 116 int ret = 0;
- 117 struct cfifo_t *dev = filp->private_data;
- 118 DECLARE_WAITQUEUE(wait, current);
- 119
- 120 down(&dev->sem);
- 121 add_wait_queue(&dev->w_wait, &wait);
- 122
- 123 while(dev->current_len >= GLOBALMEM_SIZE){
- 124 if(filp->f_flags & O_NONBLOCK){
- 125 ret = -EAGAIN;
- 126 goto out;
- 127 }
- 128
- 129 __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
- 130 up(&dev->sem); /* 释放信号量,避免死锁 */
- 131
- 132 schedule(); /* 调度其他进程运行*/
- 133 if(signal_pending(current)){
- 134 ret = -ERESTARTSYS;
- 135 goto out2;
- 136 }
- 137
- 138 down(&dev->sem);
- 139 }
- 140
- 141 if(count > GLOBALMEM_SIZE - dev->current_len)
- 142 count = GLOBALMEM_SIZE - dev->current_len;
- 143
- 144
- 145 if(copy_from_user(dev->mem+dev->current_len, buf, count)){
- 146 ret = - EFAULT;
- 147 goto out;
- 148 }
- 149 else{
- 150 dev->current_len += count;
- 151 *fpos = dev->current_len;
- 152 ret = count;
- 153
- 154 wake_up_interruptible(&dev->r_wait);
- 155 printk(KERN_INFO "write %s %d bites \n", buf, count);
- 156 }
- 157
- 158 out:
- 159 up(&dev->sem);
- 160 out2:
- 161 remove_wait_queue(&dev->w_wait, &wait);
- 162 set_current_state(TASK_RUNNING);
- 163
- 164 return ret;
- 165 }
开始读,这时设备中没有内容,read函数会阻塞在79行。然后向设备中写入内容,write函数运行到154行时,会唤醒写的进程,和65行代码有关
- DECLARE_WAITQUEUE(wait, current)
下面是验证:
- root@wang:/work/wanghuan/drives# ls
- cdev.c cfifo.c cfifo.ko Makefile modules.order
- root@wang:/work/wanghuan/drives# insmod cfifo.ko
- root@wang:/work/wanghuan/drives# ls /dev/cfifo*
- /dev/cfifo0 /dev/cfifo1
- root@wang:/work/wanghuan/drives# cat /dev/cfifo0 &
- [1] 2889
- root@wang:/work/wanghuan/drives# ps
- PID TTY TIME CMD
- 2046 pts/0 00:00:00 bash
- 2889 pts/0 00:00:00 cat
- 2890 pts/0 00:00:00 ps
- root@wang:/work/wanghuan/drives# ls > /dev/cfifo0
- cdev.c
- cfifo.c
- cfifo.ko
- Makefile
- modules.order
- root@wang:/work/wanghuan/drives# ls > /dev/cfifo1
- cdev.c
- cfifo.c
- cfifo.ko
- Makefile
- modules.order
- root@wang:/work/wanghuan/drives
下面是完整的源码:
- /**
- * =====================================================================================
- * Filename: cfifo.c
- * Description: 字符设备驱动模型 阻塞I/O
- * Version: 1.0
- * Created: 2011年06月12日 17时19分50秒
- * Revision: none
- * Compiler: gcc
- *
- * Author: wanghuan,
- * Company:
- * =====================================================================================
- */
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/fs.h> /* file_operation */
- #include <linux/errno.h> /* Error number */
- #include <linux/mm.h>
- #include <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/init.h> /* __init __exit */
- #include <linux/device.h>
- #include <linux/cdev.h>
- #include <asm/io.h>
- #include <asm/system.h>
- #include <asm/uaccess.h> /* copy_to_user, copy_from_user */
- #include <linux/kernel.h> /* printk() */
- #define GLOBALMEM_SIZE 0x1000
- #define MEM_CLEAN 0x1
- #define DEV_NAME "cfifo"
- #define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)
- static int MAJOR_NR = 255; /* Driver Major Number */
- //static int MINOR_NR = 0; /* Driver Major Number */
- struct cfifo_t{
- struct cdev cdev;
- unsigned char mem[GLOBALMEM_SIZE];
- struct semaphore sem;
- unsigned int current_len;
- wait_queue_head_t r_wait;
- wait_queue_head_t w_wait;
- };
- static struct cfifo_t *cfifo_p;
- static int cfifo_open(struct inode *inode, struct file *filp)
- {
- struct cfifo_t *dev;
- dev = container_of(inode->i_cdev, struct cfifo_t, cdev);
- filp->private_data = dev;
- return 0;
- }
- static int cfifo_release(struct inode *inode, struct file *filp)
- {
- filp->private_data = NULL;
- return 0;
- }
- static ssize_t cfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
- {
- int ret = 0;
- struct cfifo_t *dev = filp->private_data;
- DECLARE_WAITQUEUE(wait, current);
-
- down(&dev->sem);
- add_wait_queue(&dev->r_wait, &wait);
- while(dev->current_len == 0){
- if(filp->f_flags & O_NONBLOCK){
- ret = -EAGAIN;
- goto out;
- }
- __set_current_state(TASK_INTERRUPTIBLE);
- up(&dev->sem);
- schedule(); /*调度其他进程运行*/
- if(signal_pending(current)){
- ret = -ERESTARTSYS;
- goto out2;
- }
- down(&dev->sem);
- }
- if(count > dev->current_len)
- count = dev->current_len;
- if(copy_to_user(buf, (void*)(dev->mem), count)){
- ret = -EFAULT;
- goto out;
- } else{
- memcpy(dev->mem, dev->mem + count, dev->current_len - count);
- dev->current_len -= count;
- *fpos =dev->current_len;
- wake_up_interruptible(&dev->w_wait);
- ret =count;
- printk(KERN_INFO "read %s %d bites \n", buf, count);
- }
- out:
- up(&dev->sem);
- out2:
- remove_wait_queue(&dev->r_wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
- }
- static ssize_t cfifo_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
- {
- int ret = 0;
- struct cfifo_t *dev = filp->private_data;
- DECLARE_WAITQUEUE(wait, current);
- down(&dev->sem);
- add_wait_queue(&dev->w_wait, &wait);
- while(dev->current_len >= GLOBALMEM_SIZE){
- if(filp->f_flags & O_NONBLOCK){
- ret = -EAGAIN;
- goto out;
- }
- __set_current_state(TASK_INTERRUPTIBLE); /* 进程状态,定义在 linux/sched.h */
- up(&dev->sem); /* 释放信号量,避免死锁 */
- schedule(); /* 调度其他进程运行*/
- if(signal_pending(current)){
- ret = -ERESTARTSYS;
- goto out2;
- }
- down(&dev->sem);
- }
- if(count > GLOBALMEM_SIZE - dev->current_len)
- count = GLOBALMEM_SIZE - dev->current_len;
- if(copy_from_user(dev->mem+dev->current_len, buf, count)){
- ret = - EFAULT;
- goto out;
- }
- else{
- dev->current_len += count;
- *fpos = dev->current_len;
- ret = count;
- wake_up_interruptible(&dev->r_wait);
- printk(KERN_INFO "write %s %d bites \n", buf, count);
- }
- out:
- up(&dev->sem);
- out2:
- remove_wait_queue(&dev->w_wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
- }
- static loff_t cfifo_llseek(struct file *filp, loff_t offset, int orig)
- {
- loff_t ret;
- switch(orig){
- case 0:
- if(offset < 0){
- ret = -EFAULT;
- break;
- }
- if((unsigned int)offset > GLOBALMEM_SIZE){
- ret = -EFAULT;
- break;
- }
- filp->f_pos = (unsigned int)offset;
- ret = filp->f_pos;
- break;
- case 1:
- if(filp->f_pos + offset > GLOBALMEM_SIZE){
- ret = -EFAULT;
- break;
- }
- if(filp->f_pos + offset < 0){
- ret = -EFAULT;
- break;
- }
- filp->f_pos += offset;
- ret = filp->f_pos;
- break;
- default:
- ret = -EFAULT;
- }
- return ret;
- }
- static int cfifo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
- {
- struct cfifo_t *dev = filp->private_data;
- switch(cmd){
- case MEM_CLEAR:
- if(down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- memset(dev->mem, 0, GLOBALMEM_SIZE);
- up(&dev->sem);
- printk(KERN_INFO "globalmem is set to zero \n");
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static const struct file_operations cfifo_fops = {
- .owner = THIS_MODULE,
- .open = cfifo_open,
- .release = cfifo_release,
- .read = cfifo_read,
- .write = cfifo_write,
- .ioctl = cfifo_ioctl,
- .llseek = cfifo_llseek,
- };
- static void cfifo_setup_cdev(struct cfifo_t *cfifo_cdev, int minor)
- {
- int err;
- dev_t devno = MKDEV(MAJOR_NR, minor);
-
- cdev_init(&cfifo_cdev->cdev, &cfifo_fops);
- cfifo_p->cdev.owner = THIS_MODULE;
- err = cdev_add(&cfifo_cdev->cdev, devno, 1);
- if(err != 0)
- printk(KERN_NOTICE "Error %d adding gmen", err);
- cfifo_cdev->current_len = 0;
- init_MUTEX(&cfifo_cdev->sem);
- init_waitqueue_head(&cfifo_cdev->r_wait);
- init_waitqueue_head(&cfifo_cdev->w_wait);
- }
- static struct class *cdev_class;
- static int __init cfifo_cdev_init(void)
- {
- int result;
- dev_t devno = MKDEV(MAJOR_NR, 0);
- if(0 != MAJOR_NR){
- result = register_chrdev_region(devno, 1, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
- } else {
- result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
- MAJOR_NR = MAJOR(devno);
- }
- printk(KERN_CRIT"hello cfifo\n");
- if(result < 0)
- return result;
- cfifo_p = kmalloc(2*sizeof(struct cfifo_t), GFP_KERNEL);
- if(NULL == cfifo_p){
- unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1);
- result = -ENOMEM;
- }
- memset(cfifo_p, 0, 1*sizeof(struct cfifo_t));
- cfifo_setup_cdev(&cfifo_p[0], 0);
- cfifo_setup_cdev(&cfifo_p[0], 1);
- cdev_class = class_create(THIS_MODULE, DEV_NAME);
- if(IS_ERR(cdev_class))
- return PTR_ERR(cdev_class);
- device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0), NULL, DEV_NAME"%d", 0);
- device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1), NULL, DEV_NAME"%d", 1);
- return 0;
- }
- void cfifo_cdev_exit(void)
- {
- cdev_del(&cfifo_p[0].cdev);
- cdev_del(&cfifo_p[0].cdev);
- device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
- device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
- class_destroy(cdev_class); //delete class created by us
- kfree(cfifo_p);
- unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 1); //由于注册了两个设备,最后一个参数为 2
- }
- module_init(cfifo_cdev_init);
- module_exit(cfifo_cdev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("wanghuan");
Makefile:
- #===============================================================================
- # Filename: Makefile
- # Description:
- #
- # Author: wanghuan
- # Company:
- #
- #==============================================================================
- obj-m :=cfifo.o #目标文件
- KDIR :=/lib/modules/2.6.35-22-generic/build #内核路径
- PWD := $(shell pwd) #模块源文件路径
- all:
- $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
- @rm -rf *.mod.*
- @rm -rf .*.cmd
- @rm -rf *.o
- @rm -rf Module.*
- chmod a+x cfifo.ko
- clean: