上次调试好了一个基本的字符驱动,支持并发控制。在实际的使用中,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: