主页 > IT业界  > 

非阻塞IO(NIO)

非阻塞IO(NIO)

文章目录 非阻塞 IO(NIO)模型驱动程序应用程序模块使用

非阻塞 IO(NIO)

上一节中 blog.csdn.net/tyustli/article/details/135140523,使用等待队列头实现了阻塞 IO

程序使用时,阻塞 IO 和非阻塞 IO 的区别在于文件打开的时候是否使用了 O_NONBLOCK 标志位。

O_RDWR 默认以阻塞的方式打开O_NONBLOCK 以非阻塞的方式打开 模型

驱动程序

文件打卡时,文件打开标志存放在文件结构体 struct file f_flags 字段 文件结构体原型在 linux/fs.h 文件中

struct file { union { struct llist_node f_llist; struct rcu_head f_rcuhead; unsigned int f_iocb_flags; }; /* * Protects f_ep, f_flags. * Must not be taken from IRQ context. */ spinlock_t f_lock; fmode_t f_mode; atomic_long_t f_count; struct mutex f_pos_lock; loff_t f_pos; unsigned int f_flags; struct fown_struct f_owner; const struct cred *f_cred; struct file_ra_state f_ra; struct path f_path; struct inode *f_inode; /* cached value */ const struct file_operations *f_op; u64 f_version; #ifdef CONFIG_SECURITY void *f_security; #endif /* needed for tty driver, and maybe others */ void *private_data; #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct hlist_head *f_ep; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; errseq_t f_wb_err; errseq_t f_sb_err; /* for syncfs */ } __randomize_layout __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */

所以驱动中需要判断文件打开标志是否支持非阻塞方式

new_chrdev_t *dev = (new_chrdev_t *)file->private_data; if (file->f_flags & O_NONBLOCK) { if (!dev->flag) { return -EAGAIN; } }

驱动程序源码

#include "asm-generic/errno-base.h" #include "linux/device/class.h" #include "linux/export.h" #include "linux/uaccess.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/wait.h> #define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */ #define CHRDEVBASE_NUM 1 /* 设备数目 */ static char *string_test = "kernel data this tyustli test"; typedef struct { dev_t dev_id; /* 设备号 */ struct cdev c_dev; /* cdev */ struct class *class; /* 类 */ struct device *device; /* 设备 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ int flag; /* read flag */ char write_buf[100]; char read_buf[100]; wait_queue_head_t read_wait; /* 读等待队列头 */ } new_chrdev_t; static new_chrdev_t new_chrdev1; static int chrdevbase_open(struct inode *inode, struct file *file) { file->private_data = container_of(inode->i_cdev, new_chrdev_t, c_dev); /* 设置私有数据 */ printk("k: chrdevbase open\r\n"); return 0; } static ssize_t chrdevbase_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned long ret = 0; new_chrdev_t *dev = (new_chrdev_t *)file->private_data; if (file->f_flags & O_NONBLOCK) { if (!dev->flag) { return -EAGAIN; } } wait_event_interruptible(dev->read_wait, dev->flag); dev->flag = 0; memcpy(dev->read_buf, string_test, strlen(string_test)); ret = copy_to_user(buf, dev->read_buf, count); if (ret == 0) { printk("k: read data success\r\n"); } else { printk("k: read data failed ret = %ld\r\n", ret); } return ret; } static ssize_t chrdevbase_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned long ret = 0; new_chrdev_t *dev = (new_chrdev_t *)file->private_data; ret = copy_from_user(dev->write_buf, buf, count); if (ret == 0) { printk("k: write data success write data is: %s\r\n", dev->write_buf); } else { printk("k: write data failed ret = %ld\r\n", ret); } dev->flag = 1; wake_up_interruptible(&dev->read_wait); return count; } static int chrdevbase_release(struct inode *inode, struct file *file) { printk("k: chrdevbase release\r\n"); return 0; } static struct file_operations chrdevbase_fops = { .owner = THIS_MODULE, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release, }; static int __init chrdevbase_init(void) { int err = 0; err = alloc_chrdev_region(&new_chrdev1.dev_id, 0, CHRDEVBASE_NUM, CHRDEVBASE_NAME); if (err < 0) { printk("k: alloc chrdev region failed err = %d\r\n", err); goto err_chrdev; } /* get major 1 and minor 1 */ new_chrdev1.major = MAJOR(new_chrdev1.dev_id); new_chrdev1.minor = MINOR(new_chrdev1.dev_id); printk("k: newcheled major=%d,minor=%d\r\n", new_chrdev1.major, new_chrdev1.minor); new_chrdev1.c_dev.owner = THIS_MODULE; cdev_init(&new_chrdev1.c_dev, &chrdevbase_fops); err = cdev_add(&new_chrdev1.c_dev, new_chrdev1.dev_id, 1); if (err < 0) { printk("k: cdev add failed err = %d\r\n", err); goto err_cdev_add; } new_chrdev1.class = class_create("chr_test1"); if (IS_ERR(new_chrdev1.class)) { err = PTR_ERR(new_chrdev1.class); goto err_class_create; } new_chrdev1.device = device_create(new_chrdev1.class, NULL, new_chrdev1.dev_id, NULL, "chr_test1"); if (IS_ERR(new_chrdev1.device)) { err = PTR_ERR(new_chrdev1.device); goto err_device_create; } /* 初始化等待队列头 */ init_waitqueue_head(&new_chrdev1.read_wait); new_chrdev1.flag = 0; printk("k: base module init\r\n"); return 0; err_device_create: class_destroy(new_chrdev1.class); err_class_create: cdev_del(&new_chrdev1.c_dev); err_cdev_add: unregister_chrdev_region(new_chrdev1.dev_id, CHRDEVBASE_NUM); err_chrdev: return err; } static void __exit chrdevbase_exit(void) { device_destroy(new_chrdev1.class, new_chrdev1.dev_id); class_destroy(new_chrdev1.class); cdev_del(&new_chrdev1.c_dev); unregister_chrdev_region(new_chrdev1.dev_id, CHRDEVBASE_NUM); printk("k: base module exit!\r\n"); } module_init(chrdevbase_init); module_exit(chrdevbase_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("tyustli"); MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */ 应用程序

打开文件时使用 O_NONBLOCK 非阻塞方式打开文件

fd = open(filename, O_RDWR | O_NONBLOCK);

如果文件读取失败,循环读取

retvalue = -1; while (retvalue < 0) { retvalue = read(fd, readbuf, 50); if (retvalue < 0) { printf("u: read file %s failed!\r\n", filename); sleep(3); } else { /* 读取成功,打印出读取成功的数据 */ printf("u: read data:%s\r\n", readbuf); } }

完整应用程序代码

#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" static char usrdata[] = { "user data!" }; int main(int argc, char *argv[]) { int fd, retvalue; char *filename; char readbuf[100], writebuf[100]; if (argc != 3) { printf("u: error Usage!\r\n"); return -1; } filename = argv[1]; /* 打开驱动文件 */ fd = open(filename, O_RDWR | O_NONBLOCK); if (fd < 0) { printf("u: can't open file %s\r\n", filename); return -1; } /* 从驱动 文件读取数据 */ if (atoi(argv[2]) == 1) { retvalue = -1; while (retvalue < 0) { retvalue = read(fd, readbuf, 50); if (retvalue < 0) { printf("u: read file %s failed!\r\n", filename); sleep(3); } else { /* 读取成功,打印出读取成功的数据 */ printf("u: read data:%s\r\n", readbuf); } } } /* 向设备驱动写数据 */ if (atoi(argv[2]) == 2) { memcpy(writebuf, usrdata, sizeof(usrdata)); retvalue = write(fd, writebuf, 50); if (retvalue < 0) { printf("u: write file %s failed!\r\n", filename); } } /* 关闭设备 */ retvalue = close(fd); if (retvalue < 0) { printf("u: can't close file %s\r\n", filename); return -1; } return 0; } 模块使用

模块安装

modprobe my_module

查看设备节点

ls /dev ~ # ls /dev/ chr_test1 ptypc tty32 tty7 console ptypd tty33 tty8 cpu_dma_latency ptype tty34 tty9 full ptypf tty35 ttyAMA0 gpiochip0 random tty36 ttyAMA1 gpiochip1 root tty37 ttyAMA2 gpiochip2 rtc0 tty38 ttyAMA3 gpiochip3 snd tty39 ttyp0 hwrng tty tty4 ttyp1

模块使用

~ # /lib/modules/6.5.7+/my_app /dev/chr_test1 1 & ~ # k: chrdevbase open u: read file /dev/chr_test1 failed! u: read file /dev/chr_test1 failed! ~ # /lib/modules/6.5.7+/my_app /dev/chr_test1 2 k: chrdevbase open k: write data success write data is: user data! k: chrdevbase release ~ # k: read data success k: chrdevbase release u: read data:kernel data this tyustli test [1]+ Done /lib/modules/6.5.7+/my_app /dev/chr_test1 1 ~ #
标签:

非阻塞IO(NIO)由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“非阻塞IO(NIO)