7-1、设备ioctl控制
7-2、内核等待队列
7-3、阻塞型字符设备驱动
7-4、poll设备操作
7-5、自动创建设备文件
7-2、内核等待队列
在linux中,可以使用等待队列来实现进程的阻塞睡眠,可以把队列看做是保存容器中,阻塞进程形成一个队列。唤醒是从队列中取出来。
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(wait_queue_t queue, condition)
当condition(一个布尔表达式)为真时,立即放回。否则让进程进入TASK_KILLABLE模式。并挂在queue参数所指令的等待队列上。
5、无条件睡眠
interruptible_sleep_on(wait_queue_head_t *q)
让进程无条件进入可中断的睡眠,并放入q队列中。
6、从等待队列中唤醒进程
wake_up(wait_queue_t *q)
从等待队列中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。
wake_up_interruptible(wait_queue_t *q)
从等待嘟列中唤醒状态为TASK_INTERRUPTIBLE的进程
7-3、阻塞型字符设备驱动(重点)
简介:即通过上面的内核等待队列接口实现用户态调用read时阻塞。用于当内核态没有数据的时候实现进程的休眠功能。用户态可以通过read()函数的O_NONBLOCK参数指定读操作是否发生阻塞。
memdev.c
#ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/proc_fs.h> #include <asm/system.h> #include <asm/uaccess.h> /*使用copy_to_user,copy_from_user必须要包含该文件*/ #include "memdev.h" MODULE_LICENSE("GPL"); /*设备打开*/ int memdev_open(struct inode *inode, struct file *filp) { Mem_Dev *dev; /*获取次设备号*/ int num = MINOR(inode->i_rdev); dev = (Mem_Dev *)filp->private_data; if (!dev) { if (num >= MEMDEV_NR_DEVS) return -ENODEV; dev = &mem_devices[num]; filp->private_data = dev; } /*增加模块引用计数*/ MOD_INC_USE_COUNT; return 0; } /*设备关闭*/ int memdev_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; return 0; } /*设备读操作*/ ssize_t memdev_read(struct file *filp, char *buf, size_t count,loff_t *f_pos) { Mem_Dev *dev = filp->private_data; int pos = *f_pos; ssize_t ret = 0;
//为什么这里要用while??防止不期望的的中断发生唤醒 while (dev->rp == dev->wp) /* 没有数据可读,考虑为什么不用if,而用while */ { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&(dev->inq));
//获取使用有条件睡眠:wait_event_interruptible(dev->inq,have_data); } /* 判断读写数据多少 */ count = min(count, dev->wp - dev->rp); /*读数据到用户空间*/ if (copy_to_user(buf, &(dev->data[pos]), count)) { ret = -EFAULT; goto out; } *f_pos += count; dev->rp += count; ret = count; out: return ret; } /*文件写操作*/ ssize_t memdev_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { Mem_Dev *dev = filp->private_data; int pos = *f_pos; ssize_t ret = -ENOMEM; /*判断写位置是否有效*/ if (dev->wp + count > dev->size) count = dev->size - dev->wp; /*从用户空间写入数据*/ if (copy_from_user(&(dev->data[pos]), buf, count)) { ret = -EFAULT; goto out; } /* 唤醒读进程 */ wake_up(&(dev->inq)); *f_pos += count; dev->wp += count; ret = count; out: return ret; } /*文件定位*/ loff_t memdev_llseek(struct file *filp, loff_t off, int whence) { Mem_Dev *dev = filp->private_data; loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + off; break; case 2: /* SEEK_END */ newpos = dev->size -1 + off; break; default: /* can't happen */ return -EINVAL; } if (newpos<0) return -EINVAL; filp->f_pos = newpos; return newpos; } /* * The following wrappers are meant to make things work with 2.0 kernels */ struct file_operations memdev_fops = { llseek: memdev_llseek, read: memdev_read, write: memdev_write, open: memdev_open, release: memdev_release, }; /*加载函数*/ int memdev_init_module(void) { int result, i; /*设置模块owner*/ SET_MODULE_OWNER(&memdev_fops); /*注册字符设备*/ result = register_chrdev(memdev_major, "memdev", &memdev_fops); if (result < 0) { printk(KERN_WARNING "mem: can't get major %d\n",memdev_major); return result; } if (memdev_major == 0) memdev_major = result; /*为设备描述结构分配内存*/ mem_devices = kmalloc(MEMDEV_NR_DEVS * sizeof(Mem_Dev), GFP_KERNEL); if (!mem_devices) { result = -ENOMEM; goto fail; } memset(mem_devices, 0, MEMDEV_NR_DEVS * sizeof(Mem_Dev)); /*为设备分配内存*/ for (i=0; i < MEMDEV_NR_DEVS; i++) { mem_devices[i].size = MEMDEV_SIZE; mem_devices[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); memset(mem_devices[i].data, 0, MEMDEV_SIZE); /*初始化等待队列*/ init_waitqueue_head(&(mem_devices[i].inq)); } return 0; fail: memdev_cleanup_module(); return result; } /*卸载函数*/ void memdev_cleanup_module(void) { int i; /*注销字符设备*/ unregister_chrdev(memdev_major, "memdev"); /*释放内存*/ if (mem_devices) { for (i=0; i<MEMDEV_NR_DEVS; i++) kfree(mem_devices[i].data); kfree(mem_devices); } } module_init(memdev_init_module); module_exit(memdev_cleanup_module);
memdev.h
#ifndef _MEMDEV_H_ #define _MEMDEV_H_ #include <linux/ioctl.h> #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 0 #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 3 #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif typedef struct Mem_Dev { char *data; struct Mem_Dev *next; /* next listitem */ unsigned long size; unsigned long wp, rp; /* 读写数据的位置 */ struct semaphore sem; /* 定义信号量 */ wait_queue_head_t inq; /* 不要使用指针方式,否则要用kmalloc先为其分配空间 */ } Mem_Dev; /* 定义幻数 */ #define MEMDEV_IOC_MAGIC 'k' /* 定义命令 */ #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_H_ */
Makefile
# The path of kernel source code INCLUDEDIR = /home/study/part2/lesson3/linux-2.4.18/include/ # Compiler CC = arm-linux-gcc # Options CFLAGS = -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -Wno-trigraphs -Os \ -mapcs -fno-strict-aliasing -fno-common -fno-common -pipe \ -mapcs-32 -march=armv4 -mtune=arm9tdmi -mshort-load-bytes \ -msoft-float -I$(INCLUDEDIR) # Target OBJS = memdev.o all: $(OBJS) $(OBJS): memdev.c memdev.h $(CC) $(CFLAGS) -c $< $(CC) app-read.c -static -o app-read $(CC) app-write.c -static -o app-write install: insmod $(OBJS) uninstall: rmmod memdev .PHONY: clean clean: rm -f *.o rm -f app-*
Read.c write.c
#include <stdio.h> int main() { FILE *fp = NULL; char Buf[128]; /*初始化Buf*/ strcpy(Buf,"memdev is char dev!"); printf("BUF: %s\n",Buf); /*打开设备文件*/ fp = fopen("/dev/memdev","r+"); if (fp == NULL) { printf("Open memdev Error!\n"); return -1; } /*清除Buf*/ strcpy(Buf,"Buf is NULL!"); printf("Read BUF1: %s\n",Buf); /*读出数据*/ fread(Buf, sizeof(Buf), 1, fp); /*检测结果*/ printf("Read BUF2: %s\n",Buf); fclose(fp); return 0; } write.c #include <stdio.h> int main() { FILE *fp = NULL; char Buf[128]; /*打开设备文件*/ fp = fopen("/dev/memdev","r+"); if (fp == NULL) { printf("Open Dev memdev Error!\n"); return -1; } /*写入设备*/ strcpy(Buf,"memdev is char dev!"); printf("Write BUF: %s\n",Buf); fwrite(Buf, sizeof(Buf), 1, fp); sleep(5); fclose(fp); return 0; }