注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
虚拟机为:Ubuntu 12.10
开发板上系统内核版本:linux-3.5.7.2
等待队列
在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可
看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,
从等待等列中取出进程。
Linux 3.5.7.2内核提供了如下关于等待队列的操作:
1、定义等待队列
wait_queue_head_t my_queue
2、初始化等待队列
init_waitqueue_head(&my_queue)
3、定义并初始化等待队列
DECLARE_WAIT_QUEUE_HEAD(my_queue)
4、有条件睡眠
wait_event(queue,condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在 queue参数所指定的等待队列上。
wait_event_interruptible(queue,condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。
int wait_event_killable(wait_queue_t queue, condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。
5、无条件睡眠(老版本,建议不再使用)
sleep_on(wait_queue_head_t *q)
让进程进入不可中断的睡眠,并把它放入等待队列。
interruptible_sleep_on(wait_queue_head_t *q)
让进程进入可中断的睡眠,并把它放入等待队列。
6、从等待队列中唤醒进程
wake_up(wait_queue_t *q)
从等待队列中唤醒状态为TASK_UNINTERRUPTIBLE,
TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。
wake_up_interruptible(wait_queue_t *q)
从等待队列中唤醒状态为TASK_INTERRUPTIBLE的进程
阻塞的实现
在阻塞型驱动程序中,Read实现方法如下:如果进程调用read,但设备没有数据或数据不足,进程阻塞。当新数据到达后,唤醒被阻塞进程。
在阻塞型驱动程序中,Write实现方法如下:如果进程调用了write,但设备没有足够的空间供其写入数据,进程阻塞。当设备中的数据被读走后,缓冲区中空出部分空间,则唤醒进程。
阻塞方法是文件读写操作的默认方法,但应用程序员可通过使用O_NONBLOCK标志来人为的设置读写操作为非阻塞方法(该标志定义在<linux/fcntl.h>中,在打开文件时指定)。如果设置了O_NONBLOCK标志,read和write的行为是不同的。如果进程在没有数据就绪时调用了read,或者在缓冲区没有空间时调用了write,系统只是简
单地返回-EAGAIN,而不会阻塞进程。
2.程序清单
本次实验程序为国嵌培训代码,本人作了改动和较为详细的注释,如有错误请指出。
memdev.h
//头文件
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
#include <linux/ioctl.h>
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0 /*预设的mem的主设备号*/
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2 /*设备数*/
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
/*mem设备描述的结构体*/
struct mem_dev
{
char *data;
unsigned long size;
wait_queue_head_t inq;
};
/*定义幻数*/
#define MEMDEV_IOC_MAGIC 'k'
/* 定义命令 ,这里的命令都是unsigned int类型*/
#define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1)
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)
#define MEMDEV_IOC_MAXNR 3 //定义命令的最大序列号
#endif
memdev.c
/*********************************************
*memdev.c 阻塞版本
*********************************************/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "memdev.h"
static int mem_major = MEMDEV_MAJOR;
bool have_data = false; /*表明设备有足够空间可供读*/
module_param(mem_major,int,S_IRUGO);
struct mem_dev *mem_devp;/*设备结构体指针*/
struct cdev cdev;
/*文件打开函数*/
int mem_open(struct inode *inode,struct file *filp)
{
struct mem_dev *dev;
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
/*大于最大的次设备号就错误了 因为注册的时候要指定次设备的数目*/
if(num >=MEMDEV_NR_DEVS)
return -ENODEV;
dev = &mem_devp[num];
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode,struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针由于read没有struct inode *inode结构 而且函数的
参数不能改 只能改函数名所以只能在open函数里面设置*/
struct mem_dev *dev = filp->private_data;
/*判断读位置是否有效*/
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
/*没有数据可读 考虑为什么不用if 而用while !!!!??*/
while(!have_data)
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
wait_event_interruptible(dev->inq, have_data);
}
/*读数据到用户空间*/
if(copy_to_user(buf,(void*)(dev->data+p),count))
{
ret = -EFAULT;
}
else
{
*poss +=count;
ret = count;
printk(KERN_INFO "read %d bytes from %lu\n",count,p);
}
have_data = false; /*表明不再有数据可读 */
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret=0;
struct mem_dev *dev = filp->private_data;
if(p>=MEMDEV_SIZE)
return 0;
if(count>MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
if(copy_from_user(dev->data+p,buf,count))
{
ret = -EFAULT;
}
else
{
*poss += count;
ret = count;
printk(KERN_INFO "write %d bytes from %lu\n",count,p);
}
have_data = true;/*有新数据可读*/
/*唤醒读进程*/
wake_up(&(dev->inq));
return ret;
}
int memdev_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err = 0;
int ret = 0;
int ioarg = 0;
/* 检测命令的有效性 */
if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC)//命令是否操作这一类设备
return -EINVAL;
if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) // 命令序列号是否超出定义
return -EINVAL;
/* 根据命令类型,检测参数空间是否可以访问 */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
/* 根据命令,执行相应的操作 */
switch(cmd) {
/* 打印当前设备信息 */
case MEMDEV_IOCPRINT:
printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
break;
/* 获取参数 */
case MEMDEV_IOCGETDATA:
ioarg = 1101;
ret = __put_user(ioarg, (int *)arg);
break;
/* 设置参数 */
case MEMDEV_IOCSETDATA:
ret = __get_user(ioarg, (int *)arg);
printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
break;
default:
return -EINVAL;
}
return ret;
}
static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos + offset;
break;
case 2:
newpos = MEMDEV_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos<0) || (newpos>MEMDEV_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
static const struct file_operations mem_fops =
{
.owner = THIS_MODULE,
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
.unlocked_ioctl = memdev_ioctl,/*为什么这里有‘,’??*/
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major,0);
/*静态申请设备号*/
if(mem_major)
result = register_chrdev_region(devno,2,"memdev");
else/*动态申请设备号*/
{
result = alloc_chrdev_region(&devno,0,2,"memdev");
mem_major = MAJOR(devno);
}
if(result < 0)
return result;
/*初始化cdev结构*/
cdev_init(&cdev,&mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/*注册字符设备*/
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
/*为设备描述结构分配内存*/
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp)/*申请失败*/
{
result = -ENOMEM;
goto fail_malloc;
}
memset(mem_devp,0,MEMDEV_NR_DEVS * sizeof(struct mem_dev));
/*为设备分配内存*/
for(i=0;i<MEMDEV_NR_DEVS;i++)
{
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
memset(mem_devp[i].data,0,MEMDEV_SIZE);
/*初始化等待队列*/
init_waitqueue_head(&(mem_devp[i].inq));
}
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
static void memdev_exit(void)
{
cdev_del(&cdev);
kfree(mem_devp);
unregister_chrdev_region(MKDEV(mem_major,0),2);
}
MODULE_AUTHOR("fulinux");
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
memdevapp_read.c
/*********************************************
*memdevapp.c
*********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is read");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/memdev3",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
lseek(fd,0,SEEK_SET);
/*清除buf*/
strcpy(buf,"nothing");
/*读出设备*/
read(fd,buf,sizeof(buf));
/*检测结果*/
printf("buf:%s\n",buf);
return 0;
}
memdevapp_write.c
/*********************************************
*memdevapp.c
*********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd;
char buf[4096];
//FILE *fp0 = NULL;
/*初始化buf*/
strcpy(buf,"This is write");
printf("buf:%s\n",buf);
/*打开设备文件*/
fd=open("/dev/memdev3",O_RDWR);
if(fd == -1)
{
printf("open memdev failed!\n");
return -1;
}
/*写入设备*/
write(fd,buf,sizeof(buf));
return 0;
}
/**************************************************************************
Makefile
**************************************************************************/
1 ifneq ($(KERNELRELEASE),)
2
3 obj-m:=memdev.o
4
5 else
6
7 KERNELDIR:=/lib/modules/3.5.7.2/build
8
9 PWD:=$(shell pwd)
10
11 default:
12
13 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
14
15 install:
16 insmod ./memdev.ko
17
18 uninstall:
19 rmmod memdev
20
21 clean:
22 rm -rf *.o *.mod.* *.ko
23
24 endif
fulinux@ubuntu:~$ clear
fulinux@ubuntu:~$ cd memdev2/
fulinux@ubuntu:~/memdev2$ ls -l
总用量 24
-rw-rw-r-- 1 fulinux fulinux 257 3月 6 14:41 Makefile
-rw-rw-r-- 1 fulinux fulinux 759 3月 6 14:37 memdevapp_read.c
-rw-rw-r-- 1 fulinux fulinux 515 3月 6 14:36 memdevapp_write.c
-rw-rw-r-- 1 fulinux fulinux 6920 3月 6 14:49 memdev.c
-rw-rw-r-- 1 fulinux fulinux 772 3月 6 13:32 memdev.h
fulinux@ubuntu:~/memdev2$ sudo make
[sudo] password for fulinux:
make -C /lib/modules/3.5.7.2/build M=/home/fulinux/memdev2 modules
make[1]: 正在进入目录 `/usr/src/linux-source-3.5.0/linux-source-3.5.0'
CC [M] /home/fulinux/memdev2/memdev.o
/home/fulinux/memdev2/memdev.c:397:5: 警告: 从不兼容的指针类型初始化 [默认启用]
/home/fulinux/memdev2/memdev.c:397:5: 警告: (在‘mem_fops.unlocked_ioctl’的初始化附近) [默认启用]
Building modules, stage 2.
MODPOST 1 modules
CC /home/fulinux/memdev2/memdev.mod.o
LD [M] /home/fulinux/memdev2/memdev.ko
make[1]:正在离开目录 `/usr/src/linux-source-3.5.0/linux-source-3.5.0'
fulinux@ubuntu:~/memdev2$ ls
Makefile memdev.c memdev.mod.c modules.order
memdevapp_read.c memdev.h memdev.mod.o Module.symvers
memdevapp_write.c memdev.ko memdev.o
fulinux@ubuntu:~/memdev2$ cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
252 bsg
253 watchdog
254 rtc
Block devices:
1 ramdisk
259 blkext
7 loop
8 sd
9 md
11 sr
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
252 device-mapper
253 virtblk
254 mdp
fulinux@ubuntu:~/memdev2$ sudo make install
insmod ./memdev.ko
fulinux@ubuntu:~/memdev2$ cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
251 memdev
252 bsg
253 watchdog
254 rtc
Block devices:
1 ramdisk
259 blkext
7 loop
8 sd
9 md
11 sr
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
252 device-mapper
253 virtblk
254 mdp
fulinux@ubuntu:~/memdev2$ ls -al memdev3
ls: 无法访问memdev3: 没有那个文件或目录
fulinux@ubuntu:~/memdev2$ mknod /dev/memdev3 c 251 0
mknod: "/dev/memdev3": 权限不够
fulinux@ubuntu:~/memdev2$ sudo mknod /dev/memdev3 c 251 0
fulinux@ubuntu:~/memdev2$ ls -al memdev3
ls: 无法访问memdev3: 没有那个文件或目录
fulinux@ubuntu:~/memdev2$ ls -al /dev/memdev3
crw-r--r-- 1 root root 251, 0 3月 6 15:31 /dev/memdev3
fulinux@ubuntu:~/memdev2$ gcc -o memdevapp_read memdevapp_read.c
memdevapp_read.c: 在函数‘main’中:
memdevapp_read.c:24:5: 警告: 隐式声明与内建函数‘strcpy’不兼容 [默认启用]
fulinux@ubuntu:~/memdev2$ gcc -o memdevapp_write memdevapp_write.c
memdevapp_write.c: 在函数‘main’中:
memdevapp_write.c:20:5: 警告: 隐式声明与内建函数‘strcpy’不兼容 [默认启用]
fulinux@ubuntu:~/memdev2$ ./memdevapp_write
buf:This is write
open memdev failed!
fulinux@ubuntu:~/memdev2$ sudo ./memdevapp_write
buf:This is write
fulinux@ubuntu:~/memdev2$ sudo ./memdevapp_read
buf:This is read
buf:This is write
fulinux@ubuntu:~/memdev2$