概要:
**********当一个驱动无法立刻满足用户空间的应用程序的请求时,驱动程序该如何处理?例如在用户空间应用程序调用read函数读取数据,但是数据还没有到来,没有数据可读,或者调用write函数写数据时,但是缓冲区已经满了,没空间可写,出现这种情况该怎么办呢?
**********解决的办法是:驱动先让用户空间的进程进入睡眠状态,然后等条件满足了再将该进程唤醒,例如有数据来时唤醒读进程,缓冲区空闲时,唤醒写进程
有关进程睡眠的介绍:
**********当一个进程被置为睡眠的时候,这个进程就会被标识为处于一个特殊的状态并且从调度器的运行队列中去除掉.
####################################################################################################################################
实现让用户空间的进程睡眠的方法:
**********通过一个等待队列的数据结构来实现,这个等待队列其实是一个进程列表,在该列表里的进程都等待一个特定的事件
**********一个等待队列由一个"等待队列头"来管理,一个wait_queue_head_t类型的结构
**********等待队列头的数据结构为:----------------------->(在头文件<linux/wait.h>中)
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
**********定义和初始化一个等待队列头的方法
静态的定义和初始化
DECLARE_WAIT_QUEUE_HEAD(name);
动态的定义和初始化
wait_queue_head_t my_queue; init_waitqueue_head(&my_queue);
**********使进程睡眠的宏函数(这些使进程睡眠的函数在进程A中调用)
wait_event(queue, condition) //调用该函数使进程睡眠,在该进程睡眠期间不可被中断 wait_event_interruptible(queue, condition) //睡眠可以被信号中断,若被信号中断,该函数返回一个非0值,驱动应该返回-ERESTARTSYS wait_event_timeout(queue, condition, timeout) //设置进程睡眠的时间,时间一到该函数就返回,返回0,在睡眠期间不可被中断 wait_event_interruptible_timeout(queue, condition, timeout) //在睡眠期间可以被中断参数说明:
**********queue是等待队列头
**********condition是一个布尔值,条件为假时,使进程睡眠,条件为真的时候,唤醒进程,因此在让进程睡眠前将condition设置为假(即0),若是将进程唤醒,将condition设置为真(即非0)
**********唤醒进程的函数(这些唤醒函数在进程B中调用)
void wake_up(wait_queue_head_t *queue); //唤醒所有在给定的等待队列上等待的进程 void wake_up_interruptible(wait_queue_head_t *queue); //唤醒可中断的等待进程####################################################################################################################################
驱动模块实例(test.c文件)
# include <linux/module.h> # include <linux/init.h> # include <linux/kernel.h> # include <linux/fs.h> # include <linux/cdev.h> # include <asm/current.h> # include <linux/sched.h> /** * The descriptions for the module **/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bruce.Wang"); MODULE_DESCRIPTION("Test the char device"); MODULE_VERSION("1.0"); MODULE_ALIAS("CHRDEV"); //MODULE_DEVICE_TABLE("for device of char "); /** *the major device number **/ static int major = 66; static int minor = 88; static int count = 2; static dev_t cdevno; static char chrdev_name[] = "Dediprog"; /** *char device **/ static struct cdev mycdev; int my_open(struct inode *inodep, struct file *filep) { return 0; } #ifdef STATIC_WQ static DECLARE_WAIT_QUEUE_HEAD(wq); #else static wait_queue_head_t wq; #endif static int flag = 0; size_t sleepy_read(struct file *filep, char __user *buf, size_t count, loff_t *offset) { printk(KERN_DEBUG "process %i (%s) going to sleep\n",current->pid, current->comm); wait_event_interruptible(wq, flag != 0); flag = 0; sprintf(buf,"from kernel space,Happy newer");//buf写给用户空间的数据 printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); return 0; } ssize_t sleepy_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) { /* printk("%s %d\n", __func__, __LINE__); printk("from user space--------->%s, count: %d\n", buf, count); return 20; */ printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",current->pid, current->comm); flag = 1; wake_up_interruptible(&wq); return count; } int my_release(struct inode *inodp, struct file *filp) { return 40; } /** *Operations for the character device **/ static struct file_operations fops = { .owner = THIS_MODULE, .open = my_open, .read = sleepy_read, .write = sleepy_write, .release = my_release, }; /** *The init function of the module **/ static int __init chrdev_init(void) { int ret; /** *Combined major and minor device numbers **/ cdevno = MKDEV(major, minor); /** *static register the number of the char device into the kernel **/ ret = register_chrdev_region(cdevno, count, chrdev_name); if (ret < 0){ goto ERROR1; } /** *Allocate space for character devices and init it **/ cdev_init(&mycdev, &fops); /** *Add the char device into the kernel(that is,relate with device number) **/ ret = cdev_add(&mycdev, cdevno, count); if (ret < 0){ goto ERROR2; } #ifndef STATIC_WQ init_waitqueue_head(&wq); #endif return 0; ERROR1: printk("register char device number failed!\n"); return ret; ERROR2: printk("relate char device with number failed!"); unregister_chrdev_region(cdevno, count); return ret; } /** *Function executed when the module exits **/ static void __exit chrdev_exit(void) { /** *Cancellation the char device number from the kernel **/ unregister_chrdev_region(cdevno, count); /** *Remove the char device from the kernel **/ cdev_del(&mycdev); printk("See you later,guys!!\n"); } module_init(chrdev_init); module_exit(chrdev_exit); #########################################################################################################################################################
Makefile的内容obj-m := test.o #KERNEL内核路径 KERNEL := /lib/modules/$(shell uname -r)/build all: make -C $(KERNEL) M=$(shell pwd) modules clean: @rm -rf test.ko modules* *mod* test.o Module*
在命令行执行[linux root ~]# make后得到test.ko模块,然后将模块插入内核执行命令 [linux root ~]#insmod test.ko###################################################################################################################################
在用户空间编写应用程序
读取设备的程序
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd; int ret; char buf[128]; if(argc != 2){ fprintf(stderr, "Usage: %s dev_file\n", argv[0]); return -1; } printf("(1) call open argv[1]=%s\n",argv[1]); fd = open(argv[1], O_RDWR | O_NDELAY); if(fd < 0){ perror("open"); return -2; } printf("(2) call read\n"); ret = read(fd, buf, 11); printf("buf-------%s\nret = %d\n", buf,ret); return 0; }写数据到设备的程序#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd; int ret; char buf[128]; if(argc != 2){ fprintf(stderr, "Usage: %s dev_file\n", argv[0]); return -1; } printf("(1) call open argv[1]=%s\n",argv[1]); fd = open(argv[1], O_RDWR | O_NDELAY); if(fd < 0){ perror("open"); return -2; } sprintf(buf, "Bruce said Hello"); printf("(3) call write\n"); ret = write(fd, buf, 22); printf("ret = %d\n", ret);return 0;
}####################################################################################################################################
在命令行先执行读程序此时,进程阻塞,在执行写程序,读进程由阻塞变为运行状态
先[linux root ~]# ./read Dediprog
再[linux root ~]# ./write Dediprog
其中Dediprog是在与read,write同目录下的字符设备,在驱动模块test.ko已经指定了该字符设备,在应用程需自己创建它
创建字符设备的命令是mknod
[linux root ~]# mknod Dediprog -c 66 88(主次设备号的顺序忘了,不对的话自己改变一下顺序)