非阻塞 IO(NIO)

news2025/6/9 11:03:32

文章目录

  • 非阻塞 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 文件中

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
~ # 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1330782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

直播电商“去网红化”势在必行,AI数字人打造品牌专属IP

近年来&#xff0c;网红直播带货“翻车”事件频发&#xff0c;给品牌商带来了信任危机和负面口碑的困扰&#xff0c;严重损害了企业的声誉。这证明强大的个人IP,对于吸引粉丝和流量确实能起到巨大的好处,堪称“金牌销售”,但太过强势的个人IP属性也会给企业带来一定风险&#x…

搜索二叉树(超详解)

文章目录 前言查找搜索二叉树的结构insertfinderase递归版本Findinserterase 二叉树的拷贝问题搜索二叉树的应用Key模型Key/Value的模型 前言 普通二叉树其实意义不大&#xff0c; 如果用二叉树存储数据的话&#xff0c;还不如顺序表&#xff0c;链表这些。 搜索二叉树它的意义…

Python 全栈体系【四阶】(九)

第四章 机器学习 十二、逻辑回归 1. 概述 1.1 什么是逻辑回归 逻辑回归&#xff08;Logistic Regression&#xff09; 虽然被称为回归&#xff0c;但其实际上是分类模型&#xff0c;常用于二分类。逻辑回归因其简单、可并行化、可解释强而受到广泛应用。二分类&#xff08;…

通过使用Gromacs和MM-PBSA计算结合能

关键词&#xff1a;Gromacs、MM-PBSA、结合能、受体、配体 当前&#xff0c;Gromacs已经被广泛应用在生物&#xff0c;材料等领域的模拟计算&#xff0c;其便捷、灵活等特点受到广泛研究人员的青睐。在模拟领域&#xff0c;至关重要的一类问题是计算体系内的自由能。例如两个蛋…

【智慧办公】如何让智能会议室的电子标签实现远程、批量更新信息?东胜物联网硬件网关让解决方案更具竞争力

近年来&#xff0c;为了减少办公耗能、节能环保、降本增效&#xff0c;越来越多的企业开始从传统的办公模式转向智慧办公。 以智能会议室为例&#xff0c;会议是企业业务中不可或缺的一部分&#xff0c;但在传统办公模式下&#xff0c;一来会议前行政人员需要提前准备会议材料…

S7-1200/1500(T) 通过功能块FB38051实现 SINAMICS S200 的 EPOS 基本定位控制

SINAMICS S200 PN 是西门子推出的新一代伺服驱动系统。SINAMICS S200 将与 SIMOTICS S-1FL2伺服电机、Motion Connect 350/380 电缆相结合&#xff0c;作为新型单轴 AC/AC 伺服系统&#xff0c;增强了西门子伺服驱动产品的竞争力并扩大了西门子在标准伺服市场的产品组合覆盖范围…

HTML---浮动

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.常见的网页布局 二.标准文档流 标准文档流常见标签 标准文档流的组成 块级元素<div…

Plantuml之对象图语法介绍(十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

什么是多域名证书?

多域名证书是一种SSL/TLS证书&#xff0c;允许在同一证书中添加多个域名。这些域名可以是不同的主机名或完全不同的域。传统的SSL证书通常只能用于一个域名&#xff0c;而多域名证书的引入使得一个证书可以涵盖多个域&#xff0c;为多站点的管理提供了便利。 多域名证书的优势 …

开启虚拟与现实的融合时代

数字人直播&#xff0c;作为一项新兴技术&#xff0c;正逐渐改变着我们的生活方式和沟通方式。它通过利用最先进的人工智能技术&#xff0c;使得虚拟形象得以与现实世界实时互动&#xff0c;为用户带来了全新的体验。本文将探讨数字人直播的意义、应用场景以及可能带来的影响。…

如何通过UMC配置外围组件

随着云计算技术的不断发展&#xff0c;在信息化建设模式上云是大势所趋。对于企业而言&#xff0c;已建立的内部集成并不能支撑其快速搭建开发环境、快速部署集群服务&#xff0c;并且动态水平扩展对多组织情况许可费用高昂、没有敏捷快速迭代机制&#xff0c;导致开发完毕就落…

MySQL部署之yum安装

MySQL https://www.mysql.com //mysql官网 yum安装步骤 yum安装 清理环境 [rootmysql ~]# yum erase mariadb mariadb-server mariadb-libs mariadb-devel -yuserdel -r mysql[rootmysql ~]# rm -rf /etc/my* && rm -rf /var/lib/mysql && rm -rf /use/bin/m…

【LeetCode刷题笔记】前缀树

208. 实现 Trie (前缀树) 解题思路: 1. 前缀树 Map实现 ,使用一个 Map<Character, Trie> 来存储 每个字符 对应的 若干子节点 ,在构造函数中初始化 根节点 root 为 当前对象实例 , 在 插入

一文掌握分布式锁:Mysql/Redis/Zookeeper实现

目录 一、项目准备spring项目数据库 二、传统锁演示超卖现象使用JVM锁解决超卖解决方案JVM失效场景 使用一个SQL解决超卖使用mysql悲观锁解决超卖使用mysql乐观锁解决超卖四种锁比较Redis乐观锁集成Redis超卖现象redis乐观锁解决超卖 三、分布式锁概述四、Redis分布式锁实现方案…

羊大师解答,小孩是喝羊奶好还是牛奶好

小孩是喝羊奶还是牛奶好&#xff0c;这是一个经常让父母头疼的问题。羊奶和牛奶都是优质的乳制品&#xff0c;含有丰富的蛋白质、钙和维生素等营养成分&#xff0c;对小孩的生长发育都有重要作用。然而&#xff0c;从营养角度来看&#xff0c;两者还是有一些差异的。 羊奶和牛…

Netty 与 RPC(一)

Netty 与 RPC Netty 原理 Netty 是一个高性能、异步事件驱动的 NIO 框架&#xff0c;基于 JAVA NIO 提供的 API 实现。它提供了对TCP、UDP 和文件传输的支持&#xff0c;作为一个异步 NIO 框架&#xff0c;Netty 的所有 IO 操作都是异步非阻塞的&#xff0c;通过 Future-List…

Python程序设计 前言:Python最全面的知识体系都在这里了

文章目录 Python最全面的知识体系学会Python后,可以选择什么岗位的工作?Python最全面的知识体系 Python 是一种广泛使用的高级编程语言,以其清晰的语法和强大的功能而闻名。Python 知识体系可以大致分为以下几个部分: 基础语法: 变量和数据类型:了解 Python 中的基本数据…

信息安全等级保护的定义与意义

目录 前言 信息安全等级保护定义 广义上 狭义上 技术和管理 信息安全的基本要素 信息安全等级保护的意义 当前形式 形式严峻 国家安全 三个基本一个根本 预期目标 最终效果 实际意义 前言 信息安全等级保护是对信息和信息载体按照重要性等级分级进行保护的一种…

百度百科如何创建品牌词条?

你知道一个初创品牌想要打响知名度有多难吗&#xff1f;花费金钱去投广&#xff0c;结果往往石沉大海&#xff0c;人们越来越有品牌意识&#xff0c;买手机、买电脑、买化妆品先要看牌子&#xff0c;买品牌的会更有面子。所以品牌没有名气&#xff0c;真的举步维艰。 百度百科…

【大数据】NiFi 中的 Controller Service

NiFi 中的 Controller Service 1.Service 简介1.1 Controller Service 的配置1.1.1 SETTING 基础属性1.1.2 PROPERTIES 使用属性1.1.3 COMMENT 页签 1.2 Service 的使用范围 2.全局参数配置3.DBCPConnectionPool 的使用样例4.在 ExcuseGroovyScript 组件中使用 Service 1.Servi…