嵌入式驱动学习第一周——阻塞IO,进程的休眠与唤醒

前言

   本文介绍进程的休眠与唤醒。

   嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程,未来预计四个月将高强度更新本专栏,喜欢的可以关注本博主并订阅本专栏,一起讨论一起学习。现在关注就是老粉啦!

行文目录

  • 前言
  • 1. 阻塞和非阻塞
  • 2. 进程的几种状态
  • 3. 等待队列
    • 3.1 等待队列头
    • 3.2 等待队列项
  • 参考资料

1. 阻塞和非阻塞

   当应用程序对设备驱动进行操作时,如果不能获取到设备资源,那么阻塞IO就会将应用程序挂起,直到设备资源可以获取为止。其模式如下所示:

在这里插入图片描述

   应用程序调用read函数从设备中读取数据,当设备不可用或数据未准备好的时候进入到休眠态,等设备可用就会从休眠态唤醒,然后从设备中读取数据返回到应用程序。

   阻塞访问最大的好处就是当设备文件不可操作时进程进入休眠态,可以把CPU资源让出来。

   应用程序用阻塞IO访问的代码如下。默认读取方式就是阻塞式访问。

int fd;
int data = 0;fd = open("dev/xxx_dev", O_RDWR);			// 阻塞方式打开
ret = read(fd, &data, sizeof(data));		// 读取数据

   非阻塞式IO中,应用程序对应的线程不会挂起,要么一直轮询等待,直到设备资源可用,要么直接放弃,其模式如下:

在这里插入图片描述

   应用程序调用read函数从设备中获取数据,当设备不可用或数据未准备好时会立即向内核返回一个错误码,表示数据读取失败,应用程序会再次重新读取数据,这样循环往复,直到数据读取成功。

   应用程序用阻塞IO访问的代码如下,主要在open函数的第二个参数上有变化:

int fd;
int data = 0;fd = open("dev/xxx_dev", O_RDWR | O_NONBLOCK);			// 阻塞方式打开
ret = read(fd, &data, sizeof(data));		// 读取数据

2. 进程的几种状态

TASK_RUNNING: 正在运行或处于就绪状态:就绪状态是指进程申请到了CPU以外的其他所有资源,提醒:一般的操作系统教科书将正在CPU上执 行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在Linux下统一为 TASK_RUNNING状态.
  
TASK_INTERRUPTIBLE: 处于等待队伍中,等待资源有效时唤醒(比如等待键盘输入、socket连接、信号等等),但可以被中断唤醒.一般情况下,进程列表中的绝大多数进程都处于 TASK_INTERRUPTIBLE状态.毕竟皇帝只有一个(单个CPU时),后宫佳丽几千;如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来.
  
TASK_UNINTERRUPTIBLE:处于等待队伍中,等待资源有效时唤醒(比如等待键盘输入、socket连接、信号等等),但不可以被中断唤醒.
  
TASK_ZOMBIE:僵死状态,进程资源用户空间被释放,但内核中的进程PCB并没有释放,等待父进程回收.
  
TASK_STOPPED:进程被外部程序暂停(如收到SIGSTOP信号,进程会进入到TASK_STOPPED状态),当再次允许时继续执行(进程收到SIGCONT信号,进入TASK_RUNNING状态),因此处于这一状态的进程可以被唤醒.

3. 等待队列

   阻塞访问中,如果设备不可操作的时候,进程进入休眠态,但当设备文件可以操作时就必须唤醒进程,一般在终端中完成唤醒工作。Linux内核提供等待队列来实现阻塞进程的唤醒工作。

3.1 等待队列头

   如果要使用等待队列,必须先新建并初始化一个等待队列头,等待队列头使用结构体wait_queue_head_t

struct __wait_queue_head {spinlock_t lock;struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

   定义好等待队列头后就使用函数init_waitqueue_head进行初始化。

/** @description: 初始化等待队列头* @param-q    : 要初始化的等待队列头* @return     : 无*/
void init_waitqueue_head(wait_queue_head_t *q)

3.2 等待队列项

   等待队列头是一个等待队列的头部,每个访问设备的进程为一个队列项,当设备不可用时将这些进程对应的等待队列项添加到等待队列中,用结构体wait_queue_t表示等待队列项

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;

   DECLARE_WAITQUEUE 定义并初始化一个等待队列项

// 定义并初始化一个等待队列,name是等待队列项的名字,tsk是该等待队列项属于哪个任务(进程),一般设置为current
DECLARE_WAITQUEUE(name, tsk)

   以下是添加等待队列项的函数:

/** @description: 添加队列项* @param-q    : 等待队列项要加入的等待对猎头* @param-wait : 要加入的等待队列项* @return     : 无*/
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

   以下是删除等待队列项的函数:

/** @description: 删除队列项* @param-q    : 要删除等待队列项的等待对猎头* @param-wait : 要删除的等待队列项* @return     : 无*/
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

   唤醒休眠态进程有两个函数wake_up()wake_up_interruptible()

wake_up 可以唤醒处于TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE状态的进程
wake_up_interruptible只可以唤醒处于TASK_INTERRUPTIBLE的进程

/** @description: 环境休眠态的进程* @param-q    : 要唤醒的等待队列头,其中的所有线程都会唤醒* @return     : 无*/
void wake_up(wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)

   除了主动唤醒,也可以设置等待队列等待某个事件来唤醒等待队列的线程。

/** @description    : 当condition为真时,唤醒以wq为等待对猎头的等待队列,condition为假时,会一直阻塞。* 					此函数会将进程设置为TASK_UNINTERRUPTIBLE 状态* @param-wq       : 要唤醒的等待队列头,其中的所有线程都会唤醒* @param-condition: 唤醒条件* @return         : 无*/
wait_event(wq, condition)
/** @description    : 功能和 wait_event 类似,但是此函数可以添加超时时间,以 jiffies 为单位* @param-wq       : 要唤醒的等待队列头,其中的所有线程都会唤醒* @param-condition: 唤醒条件* @param-timeout  : 超时时间* @return         : 0,表示超时时间到,而且 condition为假。1,表示 condition 为真,也就是条件满足了*/
wait_event_timeout(wq, condition, timeout)
/** @description    : 与 wait_event 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断*/
wait_event_interruptible(wq, condition)
/** @description    : 与 wait_event_timeout 函数类似,但是此函数将进程设置为 TASK_INTERRUPTIBLE,就是可以被信号打断*/
wait_event_interruptible_timeout(wq, condition, timeout)

总结:使用等待队列实现阻塞访问重点注意两点:

①、将任务或者进程加入到等待队列头
②、在合适的点唤醒等待队列,一般都是中断处理函数里面。

参考资料

[1] 【正点原子】I.MX6U嵌入式Linux驱区动开发指南 第五十二章

[2] linux内核任务调度-- wait_event

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

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

相关文章

鸿蒙全栈开发必学!码牛课堂《HarmonyOS NEXT星河版零基础入门到实战教程》,学到就是赚到!

众所周知,码牛发布的免费教程不仅质量高,而且更新快,帮助无数大学生成功踏入IT行业,被同学们亲切的称为“IT启蒙导师”。 今年被称为鸿蒙元年,各行业急缺鸿蒙相关人才,从招聘情况来看,鸿蒙人才…

巨控GRM561/562/563Y系列标准OPCUA客户端协议访问西门子1200PLC

驱动名称: 标准OPCUA客户端协议 描述: 本机作主机,读写其他外部设备数据 ◆寄存器类型: UA寄存器 地址范围: 可用数据类型:[BIT] [INT8] [INT16] [INT32] [INT64] [UINT8] [UINT16] [UINT32] [UINT64] [FLOAT32] [FLOAT64] ◆寄存器类型: 设备特殊寄存器 地址…

Transforms

数据并不总是以训练机器学习算法所需的最终处理形式出现。我们使用变换来对数据进行一些处理,使其适合训练。 所有的 TorchVision 数据集都有两个参数: transform 用于修改特征和 target_transform 用于修改标签,它们接受包含转换逻辑的 callables。tor…

表达式和语句

本文参考C Primer Plus进行C语言学习 文章目录 表达式语句 副作用和序列点复合语句(块)类型转换 1.表达式 表达式由运算符和运算对象组成。下面是一些表达式: 4 -6 421 a*(bc/d)/20 q5*2 xq%3 q>3 每个表达式都有一…

【Lattice FPGA 开发】IP核的调用

本文介绍Diamond开发软件进行IP核调用与对应官方文档查找方法。 文章目录 1. IP核的调用1.1 IPexpress调用IP核1.2 Clarity Designer调用IP核 2. IP核相关文档查找2.1 方法一2.2 方法二2.3 方法三 3 问题 1. IP核的调用 Diamond软件中,根据所选目标FPGA器件型号的…

rabbitmq4

独占队列:我们的队列只能被当前通道所绑定,不能被其他的连接所绑定,如果有其他的通道或连接再使用此队列的话,会直接报错,一般设置为false: autoDelete:消费者在消费完队列,并且彻底…

vue-路由跳转和路由传参!!!

需求&#xff1a;在修改商品时&#xff0c;会进行页面跳转&#xff0c;通过点击修改按钮进行页面跳转。这时我们需要将商品的id携带过去 一、首先我们在查询页面实现路由跳转并携带参数。 1.1、修改按钮 <el-button type"primary" size"small" click&qu…

万物皆可模块化分解

引言 为何要模块化&#xff0c;这里的主体是人&#xff0c;客体是事物。当事物很小时&#xff0c;人可以很轻松的解决&#xff1b;但是当事物远大于人能处理的范围时&#xff0c;我们就可以考虑对它进行模块化分解。模块化是一种解决复杂问题的方式&#xff0c;放之四海而皆可…

LVS负载均衡集群+NAT部署

一. LVS集群相关知识 1. 集群和分布式 系统性能扩展方式&#xff1a; Scale UP&#xff1a;垂直扩展&#xff0c;向上扩展,增强&#xff0c;性能更强的计算机运行同样的服务 升级单机的硬件设备 Scale Out&#xff1a;水平扩展&#xff0c;向外扩展,增加设备&#xff0c;并行…

【windows下,串口问题+无法连接+统一解决思路:打开串口失败!!!,或者其它错误。请选择正确得串口或者串口被占用 >>>问题解决与记录】

【windows下&#xff0c;串口问题&#xff1a;打开串口失败&#xff01;&#xff01;&#xff01;&#xff0c;或者其它错误。请选择正确得串口或者串口被占用 >>>问题解决与记录】 1、前言2、环境说明3、问题说明4、自我尝试解决方式&#xff08;1&#xff09;尝试插…

【数据结构】用队列实现栈

下面是一些思路分析和代码分享&#xff0c;有需要借鉴即可。 1.问题描述 我想用队列来实现栈的功能&#xff0c;具体而言是用两个队列做底层做出栈的功能来。 有人可能会疑问会不会多次一举&#xff0c;这里仅作练习&#xff0c;为了更加进一步了解栈/队列的性质 2.思路分析 …

UE4 Niagara 关卡3.4官方案例解析

Texture sampling is only supported on the GPU at the moment.(纹理采样目前仅在GPU上受支持) 效果&#xff1a;textures can be referenced within GPU particle systems。this demo maps a texture to a grid of particles&#xff08;纹理可以在GPU粒子系统中被引用这个演…