非阻塞 IO(NIO)

文章目录

非阻塞 IO(NIO)

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

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

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

模型

驱动程序

文件打卡时,文件打开标志存放在文件结构体 struct file f_flags 字段

文件结构体原型在 linux/fs.h 文件中

c 复制代码
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 */

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

c 复制代码
    new_chrdev_t *dev = (new_chrdev_t *)file->private_data;

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

驱动程序源码

c 复制代码
#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 非阻塞方式打开文件

c 复制代码
fd = open(filename, O_RDWR | O_NONBLOCK);

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

c 复制代码
        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);
            }
        }

完整应用程序代码

c 复制代码
#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;
}

模块使用

模块安装

shell 复制代码
modprobe my_module

查看设备节点

shell 复制代码
ls /dev
shell 复制代码
~ # 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

模块使用

shell 复制代码
~ # /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
~ # 
相关推荐
ZachOn1y8 天前
Java 入门指南:JVM(Java虚拟机)—— Java 内存运行时的数据区域
java·jvm·java-ee·团队开发·个人开发·nio
Flying_Fish_roe9 天前
java当中什么是NIO
java·开发语言·nio
CocoaAndYy9 天前
NIO、Reactor模式与直接内存
nio
FIN技术铺10 天前
问:聊一下NIO模型在Netty框架中的用法?
java·nio
FIN技术铺11 天前
问:你知道IO和NIO有哪些区别不?
java·nio
代码哲学12 天前
【BIO、NIO、AIO适用场景分析】
java·nio
漆黑的莫莫12 天前
经验笔记:RPC与高性能NIO框架
笔记·rpc·nio
G丶AEOM12 天前
还不懂BIO,NIO,AIO吗
java·nio
齐 飞15 天前
NIO笔记04-网络编程
java·网络·笔记·后端·nio
ZachOn1y16 天前
Java 入门指南:Java NIO —— Buffer(缓冲区)
java·后端·java-ee·团队开发·个人开发·nio