嵌入式开发记录-补充02:IO模型
1、阻塞:参考:https://www.sohu.com/a/258717832_781584
当条件不满足的时候,应用进程睡眠;
struct __wait_queue { unsigned int flags; void *private; wait_queue_func_t func; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t;
1、初始化等待队列头,每个等待队列都有一个头;
init_waitqueue_head((wait_queue_head_t *q)
2、初始化等待队列项,对应每个task.
在read()函数中初始化等待队列项,在条件不满足的时候需要应用层睡眠,这里是应用层调用,因此current指到应用层;
void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p ) // current
3、添加等待队列项到等待队列头的链表上;
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
4、设置当前进程的状态
set_current_state(state_value) // 设置应用层的状态 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2
5、进程调度
void schedule(void) // 进程调度:进入睡眠状态,如果条件满足并唤醒,重新返回到这里运行.
//-------条件满足唤醒--------- wake_up(wait_queue_head_t *q) wake_up_interruptible(wait_queue_head_t * x)
6、从等待队列上移除等待队列项
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
7、设置应用层状态--运行态
set_current_state(state_value)
阻塞IO实现方式一:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/err.h> #include <asm/uaccess.h> // 适用于x86平台。 //#include <asm-generic/uaccess.h> // 使用ARM平台。 #include <linux/wait.h> #include <linux/sched.h> #define COUNT 3 #define DEV_NAME "devDemo" static dev_t devNum=0; static struct cdev* mydev=NULL; static struct class * dev_cls=NULL; static struct device* devp=NULL; static char kbuf[128]={'\0'}; static int kbufcount=0; static int ma=240; static wait_queue_head_t wqh; static wait_queue_t wq; static int myopen (struct inode *node, struct file *filp) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; } static ssize_t myread (struct file *filp, char __user *ubuf, size_t size, loff_t *off) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size); if(kbufcount == 0){ init_waitqueue_entry(&wq, current); // 2.2 初始化等待队列项。 add_wait_queue(&wqh, &wq); // 2.3 等待队列项添加到等待队列头后面。 set_current_state(TASK_INTERRUPTIBLE); // 2.4 设置当前进程状态 schedule(); // 上层应用进程将阻塞在这里 // 2.5 进程调度---进入睡眠,条件满足并返回 set_current_state(TASK_RUNNING); // 2.7 设置当前进程状态 remove_wait_queue(&wqh, &wq); // 2.8 将等待队列项移除 } if(size > kbufcount) size = kbufcount; if(copy_to_user(ubuf, kbuf,13)){ printk(KERN_INFO "copy to user failed\n"); return -EAGAIN; } kbufcount =0; return 0; } static ssize_t mywrite (struct file *filp, const char __user * ubuf, size_t size, loff_t *off) { //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size); if(size > 128){ size = 128; } if(copy_from_user(kbuf, ubuf, size)){ printk(KERN_ERR "copy from user failed\n"); return -EAGAIN; // 拷贝失败应用层程序再尝试一次; } kbufcount = size; wake_up(&wqh); // 2.6 唤醒队列头后面所有进程; printk(KERN_INFO "write buf:%s\n", kbuf); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return kbufcount; } static int myrelaese (struct inode *nod, struct file *file) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; } static struct file_operations fileops={ .owner = THIS_MODULE, .open = myopen, .release = myrelaese, .read = myread, .write = mywrite, }; static int __init mdev_init(void) { int ret=0,i; printk("Hello cdev\n"); //ret = alloc_chrdev_region(&devNum, 0, 1, "dev_demo"); devNum = MKDEV(ma,0); printk("dev major:%d.\n", MAJOR(devNum)); printk("dev manor:%d.\n", MINOR(devNum)); ret = register_chrdev_region(devNum, COUNT, DEV_NAME); if(ret != 0){ printk("chr dev region err\n"); goto err0; } // 1. 为cdev分配空间. mydev = cdev_alloc(); if(mydev == NULL){ printk(KERN_ERR "alloc cdev error\n"); goto err1; } // 2. cdev初始化. cdev_init(mydev, &fileops); // 3. 添加cdev ret = cdev_add(mydev, devNum, COUNT); if(ret < 0 ){ printk(KERN_ERR "dev add error\n"); goto err1; } // 4. 在sys/class下创建目录 dev_cls = class_create(THIS_MODULE, DEV_NAME); if(IS_ERR(dev_cls)){ printk(KERN_ERR "class create failed:%ld\n", PTR_ERR(dev_cls)); ret = PTR_ERR(dev_cls); goto err2; } // 5、创建设备结点,在dev目录下,创建名称为DEV_NAME+i的设备结点; for(i=0;i<3;i++){ devp = device_create(dev_cls, NULL, MKDEV(ma,i), NULL, "%s%d", DEV_NAME,i); if(IS_ERR(devp)){ printk(KERN_ERR "device create failed: %ld\n", PTR_ERR(devp)); ret = PTR_ERR(devp); goto err3; } } //2.1 初始化等待队列头。 init_waitqueue_head(&wqh); // 初始化等待队列头。 printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; err3: for(--i;i>=0;i--){ device_destroy(dev_cls,MKDEV(ma,i)); } class_destroy(dev_cls); err2: cdev_del(mydev); err1: unregister_chrdev_region(devNum, COUNT); err0: return 0; } static void __exit mdev_exit(void) { int i=0; printk("Good bye\n"); for(i=0;i<3;i++){ device_destroy(dev_cls,MKDEV(ma,i)); } class_destroy(dev_cls); cdev_del(mydev); unregister_chrdev_region(devNum, COUNT); } module_init(mdev_init); module_exit(mdev_exit); MODULE_LICENSE("GPL");
阻塞IO实现方式二:
wait_event(wait_queue_head_t *wq, condition) wait_event_interruptible(wait_queue_head_t *wq, condition) // 可中断的 wake_up(wait_queue_head_t *q) wake_up_interruptible(wait_queue_head_t * x)
2、非阻塞:
当条件不满足的时候,返回错误给用进程;
应用层的open操作默认为阻塞操作,那么如果需要非阻塞需要应用层传递非阻塞标志;flags中的O_NONBLOCK
在驱动中:
if(filp->f_flags & O_NONBLOCK){ return -EAGAIN; }
3、IO多路复用:
操作多个文件,对应多个文件描述符;每个文件的读写操作是否阻塞需要判断,如果有其中的几个文件读写操作不阻塞了,就是可以操作了,通过返回值返回这样的信息;
其中对应的驱动的操作接口为:应用层调用select、poll、epoll都会调到下面这个接口;
unsigned int (*poll) (struct file *, struct poll_table_struct *);
驱动中需要实现的,主要实现里面那个参数
static unsigned int mypoll (struct file *filp, struct poll_table_struct *pt) { int mask =0; if(kbufcount){ mask |= POLLIN; } // static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) poll_wait(filp, &wqh,pt); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return mask; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/err.h> #include <asm/uaccess.h> // 适用于x86平台。 //#include <asm-generic/uaccess.h> // 使用ARM平台。 #include <linux/wait.h> #include <linux/sched.h> #include <linux/poll.h> #define COUNT 3 #define DEV_NAME "devDemo" static dev_t devNum=0; static struct cdev* mydev=NULL; static struct class * dev_cls=NULL; static struct device* devp=NULL; static char kbuf[128]={'\0'}; static int kbufcount=0; static int ma=240; static wait_queue_head_t wqh; static wait_queue_t wq; static int myopen (struct inode *node, struct file *filp) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; } static ssize_t myread (struct file *filp, char __user *ubuf, size_t size, loff_t *off) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size); if(kbufcount == 0){ if(filp->f_flags & O_NONBLOCK){ return -EAGAIN; } init_waitqueue_entry(&wq, current); // 2.2 初始化等待队列项。 add_wait_queue(&wqh, &wq); // 2.3 等待队列项添加到等待队列头后面。 set_current_state(TASK_INTERRUPTIBLE); // 2.4 设置当前进程状态 schedule(); // 上层应用进程将阻塞在这里 // 2.5 进程调度---进入睡眠,条件满足并返回 set_current_state(TASK_RUNNING); // 2.7 设置当前进程状态 remove_wait_queue(&wqh, &wq); // 2.8 将等待队列项移除 } if(size > kbufcount) size = kbufcount; if(copy_to_user(ubuf, kbuf,13)){ printk(KERN_INFO "copy to user failed\n"); return -EAGAIN; } kbufcount =0; return 0; } static ssize_t mywrite (struct file *filp, const char __user * ubuf, size_t size, loff_t *off) { //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size); if(size > 128){ size = 128; } if(copy_from_user(kbuf, ubuf, size)){ printk(KERN_ERR "copy from user failed\n"); return -EAGAIN; // 拷贝失败应用层程序再尝试一次; } kbufcount = size; wake_up(&wqh); // 2.6 唤醒队列头后面所有进程; printk(KERN_INFO "write buf:%s\n", kbuf); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return kbufcount; } static int myrelaese (struct inode *nod, struct file *file) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; } static unsigned int mypoll (struct file *filp, struct poll_table_struct *pt) { int mask =0; if(kbufcount){ mask |= POLLIN; } // static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) poll_wait(filp, &wqh,pt); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return mask; } static struct file_operations fileops={ .owner = THIS_MODULE, .open = myopen, .release = myrelaese, .read = myread, .write = mywrite, .poll = mypoll, }; static int __init mdev_init(void) { int ret=0,i; printk("Hello cdev\n"); //ret = alloc_chrdev_region(&devNum, 0, 1, "dev_demo"); devNum = MKDEV(ma,0); printk("dev major:%d.\n", MAJOR(devNum)); printk("dev manor:%d.\n", MINOR(devNum)); ret = register_chrdev_region(devNum, COUNT, DEV_NAME); if(ret != 0){ printk("chr dev region err\n"); goto err0; } // 1. 为cdev分配空间. mydev = cdev_alloc(); if(mydev == NULL){ printk(KERN_ERR "alloc cdev error\n"); goto err1; } // 2. cdev初始化. cdev_init(mydev, &fileops); // 3. 添加cdev ret = cdev_add(mydev, devNum, COUNT); if(ret < 0 ){ printk(KERN_ERR "dev add error\n"); goto err1; } // 4. 在sys/class下创建目录 dev_cls = class_create(THIS_MODULE, DEV_NAME); if(IS_ERR(dev_cls)){ printk(KERN_ERR "class create failed:%ld\n", PTR_ERR(dev_cls)); ret = PTR_ERR(dev_cls); goto err2; } // 5、创建设备结点,在dev目录下,创建名称为DEV_NAME+i的设备结点; for(i=0;i<3;i++){ devp = device_create(dev_cls, NULL, MKDEV(ma,i), NULL, "%s%d", DEV_NAME,i); if(IS_ERR(devp)){ printk(KERN_ERR "device create failed: %ld\n", PTR_ERR(devp)); ret = PTR_ERR(devp); goto err3; } } //2.1 初始化等待队列头。 init_waitqueue_head(&wqh); // 初始化等待队列头。 printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; err3: for(--i;i>=0;i--){ device_destroy(dev_cls,MKDEV(ma,i)); } class_destroy(dev_cls); err2: cdev_del(mydev); err1: unregister_chrdev_region(devNum, COUNT); err0: return 0; } static void __exit mdev_exit(void) { int i=0; printk("Good bye\n"); for(i=0;i<3;i++){ device_destroy(dev_cls,MKDEV(ma,i)); } class_destroy(dev_cls); cdev_del(mydev); unregister_chrdev_region(devNum, COUNT); } module_init(mdev_init); module_exit(mdev_exit); MODULE_LICENSE("GPL");
测试应用实例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> char rBuf[64]; int main(int argc, char* argv[]) { int fd =0,ret=0; fd_set rfds; printf("hello world\n"); struct UData dat={100,"liunx"}; fd = open("/dev/devDemo1",O_RDWR); if(fd < 0){ printf("open device failed.\n"); perror("open"); return -1; } while(1){ FD_ZERO(&rfds); FD_SET(fd, &rfds); ret = select(fd+1,&rfds, NULL, NULL, NULL); if(ret < 0){ perror("select\n"); } if(FD_ISSET(fd, &rfds)){ read(fd,rBuf, 64); printf("select read:%s\n", rBuf); } } close(fd); return 0; }
4、异步通知:
基于通知机制---->信号的方式signal
内核(驱动)--通知-->应用层进程
内核发送SIGIO信号,那么应用程序异步接收信号,使用异步接收信号
开启异步接收
fcntl(fd, F_SETFL, fcntl(fd,F_GETFL)|O_ASYNC);
设置异步接收拥有者:
fcntl(fd, F_SETOWN, getpid())
内核实现异步通知:
fcntl系统调用:do_fcntl
do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file * filp)
调用:int (*fasync) (int, struct file *, int);
在该接口调用:fasync_helper
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
初始化异步通知结构体,并插入异步通知队列中;如果条件满足了,发送信号到应用程序,应用程序接收信号,进行读操作;
static unsigned int mypoll (struct file *filp, struct poll_table_struct *pt) { int mask =0; if(kbufcount){ mask |= POLLIN; } // static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) poll_wait(filp, &wqh,pt); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return mask; }
内核给应用程序发送信号:
// void kill_fasync(struct fasync_struct **fp, int sig, int band) kill_fasync(&fapp, SIGIO, POLL_IN); // 发送信号
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/err.h> #include <asm/uaccess.h> // 适用于x86平台。 //#include <asm-generic/uaccess.h> // 使用ARM平台。 #include <linux/wait.h> #include <linux/sched.h> #include <linux/poll.h> #include <asm/signal.h> #define COUNT 3 #define DEV_NAME "devDemo" static dev_t devNum=0; static struct cdev* mydev=NULL; static struct class * dev_cls=NULL; static struct device* devp=NULL; static char kbuf[128]={'\0'}; static int kbufcount=0; static int ma=240; static wait_queue_head_t wqh; static wait_queue_t wq; static struct fasync_struct * fapp=NULL; static int myopen (struct inode *node, struct file *filp) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; } static ssize_t myread (struct file *filp, char __user *ubuf, size_t size, loff_t *off) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size); if(kbufcount == 0){ if(filp->f_flags & O_NONBLOCK){ return -EAGAIN; } init_waitqueue_entry(&wq, current); // 2.2 初始化等待队列项。 add_wait_queue(&wqh, &wq); // 2.3 等待队列项添加到等待队列头后面。 set_current_state(TASK_INTERRUPTIBLE); // 2.4 设置当前进程状态 schedule(); // 上层应用进程将阻塞在这里 // 2.5 进程调度---进入睡眠,条件满足并返回 set_current_state(TASK_RUNNING); // 2.7 设置当前进程状态 remove_wait_queue(&wqh, &wq); // 2.8 将等待队列项移除 } if(size > kbufcount) size = kbufcount; if(copy_to_user(ubuf, kbuf,13)){ printk(KERN_INFO "copy to user failed\n"); return -EAGAIN; } kbufcount =0; return 0; } static ssize_t mywrite (struct file *filp, const char __user * ubuf, size_t size, loff_t *off) { //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size); if(size > 128){ size = 128; } if(copy_from_user(kbuf, ubuf, size)){ printk(KERN_ERR "copy from user failed\n"); return -EAGAIN; // 拷贝失败应用层程序再尝试一次; } kbufcount = size; //wake_up(&wqh); // 2.6 唤醒队列头后面所有进程; kill_fasync(&fapp, SIGIO, POLL_IN); // 发送信号 printk(KERN_INFO "write buf:%s\n", kbuf); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return kbufcount; } static int myrelaese (struct inode *nod, struct file *file) { printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; } static unsigned int mypoll (struct file *filp, struct poll_table_struct *pt) { int mask =0; if(kbufcount){ mask |= POLLIN; } // static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) poll_wait(filp, &wqh,pt); printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return mask; } // 异步IO /* int (*fasync) (int, struct file *, int); int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) 内核中发信号: // POLL_IN/ POLL_OUT kill_fasync(struct fasync_struct ** fp, int sig, int band) */ static int myfasync(int fd, struct file * filp, int isAsync) { // 1、 printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return fasync_helper(fd, filp, isAsync, &fapp);; } static struct file_operations fileops={ .owner = THIS_MODULE, .open = myopen, .release = myrelaese, .read = myread, .write = mywrite, .poll = mypoll, .fasync = myfasync, }; static int __init mdev_init(void) { int ret=0,i; printk("Hello cdev\n"); //ret = alloc_chrdev_region(&devNum, 0, 1, "dev_demo"); devNum = MKDEV(ma,0); printk("dev major:%d.\n", MAJOR(devNum)); printk("dev manor:%d.\n", MINOR(devNum)); ret = register_chrdev_region(devNum, COUNT, DEV_NAME); if(ret != 0){ printk("chr dev region err\n"); goto err0; } // 1. 为cdev分配空间. mydev = cdev_alloc(); if(mydev == NULL){ printk(KERN_ERR "alloc cdev error\n"); goto err1; } // 2. cdev初始化. cdev_init(mydev, &fileops); // 3. 添加cdev ret = cdev_add(mydev, devNum, COUNT); if(ret < 0 ){ printk(KERN_ERR "dev add error\n"); goto err1; } // 4. 在sys/class下创建目录 dev_cls = class_create(THIS_MODULE, DEV_NAME); if(IS_ERR(dev_cls)){ printk(KERN_ERR "class create failed:%ld\n", PTR_ERR(dev_cls)); ret = PTR_ERR(dev_cls); goto err2; } // 5、创建设备结点,在dev目录下,创建名称为DEV_NAME+i的设备结点; for(i=0;i<3;i++){ devp = device_create(dev_cls, NULL, MKDEV(ma,i), NULL, "%s%d", DEV_NAME,i); if(IS_ERR(devp)){ printk(KERN_ERR "device create failed: %ld\n", PTR_ERR(devp)); ret = PTR_ERR(devp); goto err3; } } //2.1 初始化等待队列头。 init_waitqueue_head(&wqh); // 初始化等待队列头。 printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__); return 0; err3: for(--i;i>=0;i--){ device_destroy(dev_cls,MKDEV(ma,i)); } class_destroy(dev_cls); err2: cdev_del(mydev); err1: unregister_chrdev_region(devNum, COUNT); err0: return 0; } static void __exit mdev_exit(void) { int i=0; printk("Good bye\n"); for(i=0;i<3;i++){ device_destroy(dev_cls,MKDEV(ma,i)); } class_destroy(dev_cls); cdev_del(mydev); unregister_chrdev_region(devNum, COUNT); } module_init(mdev_init); module_exit(mdev_exit); MODULE_LICENSE("GPL");