阻塞 IO(BIO)

文章目录

  • 阻塞 IO(BIO)
    • 模型
    • 等待队列头
      • init_waitqueue_head
      • DECLARE_WAIT_QUEUE_HEAD
    • 等待队列项
    • 使用方法
    • 驱动程序
    • 应用程序
    • 模块使用
    • 参考

阻塞 IO(BIO)

模型

在这里插入图片描述

等待队列是内核实现阻塞和唤醒的内核机制。
等待队列以循环链表为基础结构,链表头和链表项分别为等待队列头等待队列元素
整个等待队列由等待队列头进行管理。

等待队列头

等待队列头使用结构体 wait_queue_head_t,定义在 linux/wait.h,结构体原型如下:

struct wait_queue_head {spinlock_t		lock;struct list_head	head;
};
typedef struct wait_queue_head wait_queue_head_t;

等待队列头的初始化有两种方法

init_waitqueue_head

  • 定义一个等待队列头 wait_queue_head_t read_wait
  • 等待 队列头初始化 init_waitqueue_head(&read_wait)

DECLARE_WAIT_QUEUE_HEAD

DECLARE_WAIT_QUEUE_HEAD 宏一次性完成等待队列头的定义和初始化

等待队列项

等待队列项使用结构体 struct wait_queue_entry,定义在 linux/wait.h,结构体原型如下:

struct wait_queue_entry {unsigned int		flags;void			*private;wait_queue_func_t	func;struct list_head	entry;
};

使用方法

  • 初始化等待队列头,并将条件设置为假的(condition=0)
  • 在需要阻塞的地方调用 wait_event 或者 wait_event_interruptible,使进程进入休眠
  • 当条件满足时,需要解除休眠,先将条件设置为真(condition=1),然后调用 wake_up 或者 wake_up_interruptible函数唤醒等待队列中的休眠进程。

驱动程序

#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;wait_event_interruptible(dev->read_wait, dev->flag);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_RDWR 默认以阻塞的方式打开

#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);if (fd < 0) {printf("u: can't open file %s\r\n", filename);return -1;}/* 从驱动文件读取数据 */if (atoi(argv[2]) == 1) {retvalue = read(fd, readbuf, 50);if (retvalue < 0) {printf("u: read file %s failed!\r\n", filename);} 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
input            tty0             tty40            ttyp2
kmsg             tty1             tty41            ttyp3

模块使用

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

参考

  • https://juejin.cn/post/7129070726249709599#heading-0

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【linux】用grep或者pgrep查找进程ID

一、用grep ps aux|grep 字符串|awk {print $2} 像上面这样运行&#xff0c;还会同时显示grep的进程ID。 需要再添加grep的反向查找命令&#xff0c;即查找不含有 "grep" 字段的行&#xff1a;grep -v grep。 ps aux | grep 字符串 | grep -v grep | awk {print …

高级算法设计与分析(七) -- 概率算法和NP完全性理论

系列文章目录 高级算法设计与分析&#xff08;一&#xff09; -- 算法引论 高级算法设计与分析&#xff08;二&#xff09; -- 递归与分治策略 高级算法设计与分析&#xff08;三&#xff09; -- 动态规划 高级算法设计与分析&#xff08;四&#xff09; -- 贪心算法 高级…

Instagram账号被封?必须了解的原因与防封技巧

您是否曾因 Instagram 帐户被暂停封禁而感到沮丧&#xff1f;这是一个常见问题&#xff0c;了解您的帐户被暂停的原因以及如何恢复帐户至关重要。 在本文中&#xff0c;我们将深入探讨 Instagram 帐户被封停的常见原因&#xff0c;并探讨重新获得访问权限的步骤。这个方法对于管…

融资项目——vue之事件监听

vue通过v-on进行事件监听&#xff0c;在标签中使用v-on:xxx&#xff08;事件名称&#xff09;进行监听&#xff0c;事件触发的相应方法定义在Vue对象中的methods中。如下图所示&#xff1a; 上述代码对按钮进行监听&#xff0c;点击按钮后就会触发solve函数。

Fastjson 常用语法

一.Json数据格式回顾 1.1 什么是json JSON:(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript(欧洲计算机协会制定的js规范)的一个子集&#xff0c;采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSO…

hbase用shell命令新建表报错ERROR: KeeperErrorCode = NoNode for /hbase/master

或者HMster开启后几秒消失问题解决 报错如图&#xff1a; 首先jps命令查看当前运行的内容有没有HMaster,如果没有&#xff0c;开启一下hbase,稍微等一会儿&#xff0c;再看一下HMaster,如果仍和下图一样没有&#xff0c;就基本找到问题了 本人问题原因&#xff1a;hbase-site…

网络基础【网线的制作、OSI七层模型、集线器、交换机介绍、路由器的配置】

目录 一.网线的制作 1.1.网线的标准 1.2.水晶头的做法 二.OSI七层模型、集线器、交换机介绍 集线器&#xff08;Hub&#xff09;&#xff1a; 交换机&#xff08;Switch&#xff09;&#xff1a; 三.路由器的配置 3.1.使用 3.2.常用的功能介绍 1、如何管理路由器 2、家…

WebGL在教育和培训的应用

WebGL在教育和培训领域具有广泛的应用&#xff0c;其强大的图形渲染能力和跨平台性使得它成为创建交互式、视觉化的数字内容的理想选择。以下是一些WebGL在教育和培训上的应用示例&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司…

算法训练营Day22

#Java #回溯 开源学习资料 Feeling and experiences&#xff1a; 进入到回溯算法的章节&#xff0c;在代码随想录中有详细的回溯算法理论基础 在此总结归纳&#xff1a; 刚开始接触到回溯时&#xff0c;看到了终止条件&#xff0c;递归调用.....等&#xff0c;发现了其与递…

【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化

引言 动态内存管理的函数有&#xff1a;malloc,calloc,ralloc,free,本文讲解动态内存函数和使用&#xff0c;如何进行动态内存管理,实现通讯录联系人容量的动态化&#xff0c;对常见动态内存错误进行总结。 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》…

Vue.js 教程

Vue.js&#xff08;读音 /vjuː/, 类似于 view&#xff09; 是一套构建用户界面的渐进式框架。 Vue 只关注视图层&#xff0c; 采用自底向上增量开发的设计。 Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 阅读本教程前&#xff0c;您需要了解的…

FPGA编程入门——基于Quartus件完成一个1位全加器的设计

基于Quartus件完成一个1位全加器的设计&#xff0c;分别采用&#xff1a;1&#xff09;原理图输入 以及 2&#xff09;Verilog编程 这两种设计方法。开发板基于Intel DE2-115。 在此基础上&#xff0c;用原理图以及Verilog 编程两种方式&#xff0c;完成4位全加器的设计&#x…