【ESP32+freeRTOS学习笔记-(五)队列Queue】

news2024/11/27 22:45:43

目录

  • 1、什么是队列Queue
  • 2、队列的多任务特性
    • 2.1 多任务的访问:
    • 2.2 队列读取阻塞:
    • 2.3 写队列阻塞:
    • 2.4 阻塞于多个队列:
  • 3、队列的使用
    • 3.1 创建队列--The xQueueCreate() API
    • 3.2 写入队列
    • 3.3 从队列中接收数据
    • 3.4 删除队列
  • 4、队列集
    • 4.1 创建队列集
    • 4.2 向队列集合添加队列
    • 4.3 从队列中读取以确定集合中哪个队列包含数据
    • 4.4 队例集合实例
  • 5、队列的特殊用法,Mailbox
    • 5.1 向邮箱写入数据
    • 5.2 从邮箱中读取数据
  • 6、总结

1、什么是队列Queue

Queues提供了一个任务到任务,任务到中断,以及中断到任务的通信机制。

学过数据结构的原理应该知道,队列是常用的数据结构的一种,是一种先入先出First IN First Out 的数据组织与操作形态的名称。因此在FreeRTOS中,队列Queue的本质也是一样的。对于其行为与组织形式,用原文的图解能更清楚地说明。

在这里插入图片描述

由上图可以清楚的看出,FreeRTOS的Queue就是一个实现了队列的存储组织形式以及完成了不同任务之能互相传递数据的操作实现。队列能保存有限数量的固定大小的数据项。队列能保存的项目的最大数叫“长度length”。如上例的长度是5。
为了适应不同的数据类型,因此当队列创建时,必须设置队列长度和每个数据项的大小。

2、队列的多任务特性

2.1 多任务的访问:

队列本身就是对象,任何知道它们存在的任务或 ISR 都可以访问这些对象。任意数量的任务可以写入同一个队列,任意数量的任务可以从同一个队列中读取。 在实践中,一个队列有多个写入者是很常见的,但一个队列有多个读取者的情况就少得多了。(常用于多写一读)

2.2 队列读取阻塞:

当一个任务试图从一个队列中读取数据项,那么可以为这个读取行为指定指定一个 阻塞blocked 时间。如果队列此时已经为空,这时这个任务将被保持在阻塞blocked状态,从而等待队列中具有可读的数据。当另一个任务或中断程序放置数据进队列后,这个被阻塞的任务会被移到Ready状态。这是
另一种情况,如果队列中具有可用的数据之前,读取队列的任务处于的阻塞状态的时间到了,这个阻塞的任务也会被自动移到Ready状态。从而不再阻塞等待读数据。
队列能有多个读取者,所以一个队列有可能有超过一个任务阻塞着等待数据。当这种情况下,当数据到时,只有一个任务将被解除阻塞。这个解除阻塞的任务总是那个有最高的优先级的等待数据的任务。如果阻塞任务都有相同的优先级,那么等待数据时间最长的任务将会解除阻塞。

2.3 写队列阻塞:

就像从队列中读取一样,任务写入队列时,能选择指定一个阻塞时间。这种情况下,如果队列已经满了,那么阻塞时间是任务应该保持在阻塞状态等待队列有可用空间的最大时间。
队列可以有多个写入者,所以一个满的队列有可能有超过一个任务阻塞着等待完成一个发送操作。当这种情况下,当队列上有空间可用时,只有一个任务将会解除阻塞。这个解除阻塞的任务将总是那个具有取高优先级的等待任务。如果阻塞任务都有相同的优先级,那么等待时间最长的那个任务将被解除阻塞。

2.4 阻塞于多个队列:

队列可以分组到集合中,允许任务进入阻塞状态以等待数据在集合中的任何队列上可用。 队列集在“从多个队列接收”中演示。

3、队列的使用

3.1 创建队列–The xQueueCreate() API

在这里插入图片描述
创建一个新的队列并返回一个引用队列的句柄。
注意事项:
队列用于在任务之间和任务与中断程序之间传递数据。
队列可以在调度器scheduler启动的前后创建。
configSUPPORT_DYNAMIC_ALLOCATION 必须在 FreeRTOSConfig.h 中设置为 1,或者简单地保持未定义,才能使用此函数。

参数

参数解释
UBaseType_t uxQueueLength队列在任何时刻所能保存的数据项的最大数量
UBaseType_t uxItemSize队列中每个数据项的字节大小
返回值解释
返回值如果返回NULL,则表明无法创建队列,因为没有足够的堆内存可供FreeRTOS分配队列数据结构和存储区域。如果返回的非NULL值表示已成功创建队列。返回的值应存储为创建的队列的句柄。

实例
在这里插入图片描述

3.2 写入队列

在这里插入图片描述

说明
将项目发送(写入)到队列的前面或后面。
xQueueSend()和xQueueSendToBack()执行相同的操作,因此是等效的。
两者都将数据发送到队列的后面。xQueueSend()是最初的版本,现在建议使用xQueueSendToBack()代替它。

参数

参数解释
xQueue将数据发送(写入)到的队列的句柄。队列句柄将从用于创建队列的xQueueCreate()或xQueueCreateStatic()调用中返回。
pvItemToQueue指向要复制到队列中的数据的指针。队列可以容纳的每个项目的大小在创建队列时设置,许多字节将从pvItemToQueue复制到队列存储区域。
xTicksToWait如果队列已满,则任务应保持在“阻止”状态以等待队列上的可用空间的最长时间。如果xTicksToWait为零且队列已满,则xQueueSend()、xQueueSendToFront()和xQueueSendToBack()将立即返回。块时间以刻度周期指定,因此它表示的绝对时间取决于滴答频率。pdMS_TO_TICKS()宏可用于将以毫秒为单位指定的时间转换为以刻度为单位的时间。如果在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend设置为1,则将xTicksToWait设置为portMAX_DELAY将导致任务无限期等待(不超时)。

返回值

解释
pdPASS如果数据已成功发送到队列,则返回pdPASS。如果指定了等待时间(xTicksToWait不为零),则返回pdPASS说明,在等待时间到期前,数据已成功写入队列。
errQUEUE_FULL如果由于队列已满而无法将数据写入队列,则返回该错误。另一种情况是,如果指定了阻塞时间(xTicksToWait不为零),则一直到阻塞时间到期,都无法将数据写入队列。

实例
在这里插入图片描述
在这里插入图片描述

3.3 从队列中接收数据

在这里插入图片描述

参数

参数解释
xQueue需要从中接收数据的队列的句柄。队列句柄将从用于创建队列的xQueueCreate()或xQueueCreateStatic()调用中返回。
pvBuffer指向将接收数据复制到其中的内存的指针。缓冲区的长度必须至少等于队列项目大小。项目大小将由用于创建队列的xQueueCreate()或xQueueCreateStatic()调用的uxItemSize参数设置。
xTicksToWait如果队列已经为空,则任务应保持在“阻塞”状态以等待队列上的数据可用的最长时间。如果xTicksToWait为零,则如果队列已为空,xQueueReceive()将立即返回。阻塞时间以tick周期指定,因此它表示的绝对时间取决于tick频率。pdMS_TO_TICKS()宏可用于将以毫秒为单位指定的时间转换为以tick为单位的时间。如果在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend设置为1,则将xTicksToWait设置为portMAX_DELAY将导致任务无限期等待(不超时)。

返回值

解释
pdPASS如果成功从队列中读取数据,则返回pdPASS。如果指定了阻塞时间(xTicksToWait不为零),在阻塞的时间到期之前已成功从队列中读取数据。
errQUEUE_EMPTY如果由于队列已为空而无法从队列中读取数据,则返回该出错。如果指定了阻塞时间(xTicksToWait不为零),则在阻塞时间到期后队列中仍无法读到数据。

实例

在这里插入图片描述

在这里插入图片描述

3.4 删除队列

在这里插入图片描述
概要
该函数用于删除之前用xQueueCreate()或xQueueCreateStatic()创建的队列。也可被用来删除信号量。

参数

pxQueueToDelete : 正在删除的队列的句柄。也可以使用信号句柄。

注意事项
如果当前有任何任务阻塞在队列/信号量上,则不得删除队列/信号量

实例
在这里插入图片描述

4、队列集

应用程序的设计经常需要一个任务去接收不同大小的数据,不同含义的数据,不同来源的数据。在这种情况下,可以使
用“队列集”。
队列集允许任务从多个队列接收数据,而无需任务依次轮询每个队列以确定哪个队列(如果有)包含数据。
队列集提供了一种机制,允许RTOS任务同时阻止(挂起)来自多个RTOS队列或信号量的读取操作。
在使用队列集之前,必须使用对xQueueCreateSet()的调用显式创建队列集。创建后,可以使用对xQueueAddToSet()的调用将标准FreeRTOS队列和信号量添加到集合中。然后使用xQueueSelectFromSet()确定集合中包含的队列或信号量中的哪一个(如果有的话)处于队列读取或信号量获取操作将成功的状态。

使用队列集具体过程
1、创建一个队列集合。
2、向队列集合添加队列
信号量也可被添加到队列集中。集号量将在本书后面描述。
3、从队列集合中读取以确定集合中哪一个队列包含数据
当集合中的一个队列接收数据,接收队列的句柄被发送给队列集,并且当一个任务调用一个从队列集中读到的函数时,该
句柄被返回。因此,如果一个队列句柄被从队列集中返回,那么就知道被该句柄所引用的队列包含有数据,然后这个任务可
以直接从队列中读。

4.1 创建队列集

在这里插入图片描述
参数

参数解释
uxEventQueueLength队列集存储发生在队列上的事件和集中包含的信号量。uxEventQueueLength指定一次可以排队的最大事件数。为了绝对确定事件不会丢失,uxEventQueueLength必须设置为添加到集合中的队列长度之和,其中二进制信号量和互斥量的长度为1,计数信号量的长度由其最大计数值设置。例如:
 如果队列集包含长度为5的队列、长度为12的另一个队列和二进制信号量,则uxEventQueueLength应设置为(5+12+1)或18。
 如果队列集包含三个二进制信号量,则uxEventQueueLength应设置为(1+1+1)或3。
 如果队列集要保存最大计数为5的计数信号量和最大计数为3的计数信号,则uxEventQueueLength应设置为(5+3)或8。

返回值

返回值解释
NULL返回NULL,则说明没有创建队列集
除NULL以外的任何其它值已成功创建队列集。返回的值是一个句柄,通过它可以引用创建的队列集。

注意事项
对包含互斥锁的队列集进行阻塞不会导致互斥锁持有者继承被阻塞任务的优先级。
添加到队列集的每个队列中的每个空间需要额外的4字节RAM。因此,不应将具有高最大计数值的计数信号量添加到队列集。
除非对xQueueSelectFromSet()的调用首先向队列集成员返回了句柄,否则不能对队列集的成员执行接收(对于队列)或接收(对于信号量)操作。
configUSE_QUEUE_SETS必须在FreeRTOSConfig.h中设置为1,xQueueCreateSet()API函数才能可用。

4.2 向队列集合添加队列

在这里插入图片描述

描述
将队列或信号量添加到先前通过调用xQueueCreateSet()创建的队列集。
在针对队列集的成员队列执行接收(对于队列)或接收(对于信号量)操作之前,必须先通过xQueueSelectFromSet()函数取得队列集中的某个队列的句柄。
参数

参数解释
xQueueOrSemaphore要添加到队列集的队列或信号量的句柄(强制转换为QueueSetMemberHandle_t类型)。
xQueueSet要添加队列或信号量的队列集的句柄。

返回值

返回值解释
pdPASS队列或信号已成功添加到队列集。
pdFAIL无法将队列或信号量添加到队列集中,因为它已经是其他集合的成员。

4.3 从队列中读取以确定集合中哪个队列包含数据

描述
xQueueSelectFromSet()从队列集的成员中选择包含数据(在队列的情况下)或可获取数据(在信号量的情况下,)的队列或信号量。xQueueSelectFromSet()有效地允许任务同时阻止(挂起)队列集中所有队列和信号量的读取操作。

参数

参数解释
xQueueSet这个函数要操作的队列集合。
xTicksToWait调用该函数的任务阻塞着,以等待该函数获得集合中某队列或集号量可用的最大时间,以tick为单位计时

返回值

返回值解释
NULL返回NULL说明目前集合中的队列或信号量都不可用
任何其它值队列集中包含有数据的队列句柄(强制转换为QueueSetMemberHandle_t类型),或队列集中包含数据的信号量句柄(强制转化为QueueSet MemberHandle_t类型)。

注意事项
对包含互斥锁的队列集进行阻塞不会导致互斥锁持有者继承被阻塞任务的优先级。除非对xQueueSelectFromSet()的调用首先向队列集成员返回了句柄,否则不能对队列集的成员执行receive(对于队列)或take(对于信号量)操作。

4.4 队例集合实例

使用队列集合的例子:
这个例子创建了两个发送任务和一个接收任务。发送任务在两个单独的队列中发送数据给接收任务,每个队列对应一个任务。这两个队列加入到一个队列集合中,并且接收任务从队列集合中读取并确定哪一个队列包含数据。
任务,队列,和队列集合,都是在main()函数中创建的。

在这里插入图片描述
接下来,实现两个发送数据的任务函数,每100毫秒,第一个发送任务使用xQueue1来发送字符指针给接收任务。每200毫秒,第二个发送任务用xQueue2发送字符指针给接收任务。字符指针是指向一个由发送任务定义的字符串。
在这里插入图片描述
发送任务写入的队列是相同的队列集合中的成员。每一次任务发送到其中一个队列中,这个队列的句柄被发送给队列集合,接收队列调用xQueueSelelctFromSet()从队列集合中读取队列句柄。在接收任务从队列集合中接收到队列句柄后,就知道这个句柄所引用的队列包含数据,所以直接从队列中读取数据。从队列中所读取的数据是一个指向字符串的指针,接收任务将其打印出来。

如果调用xQueueSelectFromSet()超时,那么它将返回NULL。例子中,xQueueSelectFromSet()以无限期方式被调用,所以永远不会超时,并只能返回一个有效的队列句柄。因此,接收任务不必检查该函数是否返回NULL。如果句柄所引用的队列包含数据,则 xQueueSelectFromSet()将只会返回一个队列句柄,所以当读取队列时不需要使用阻塞时间这个参数。
在这里插入图片描述

5、队列的特殊用法,Mailbox

"邮箱Mailbox"用于指长度为 1 的队列。邮箱用于保存可由任何任务或任何中断服务程序读取的数据。数据不通过邮箱传递,而是保留在邮箱中,直到被覆盖。发件人覆盖邮箱中的值。接收者从邮箱中读取值,但不从邮箱中删除值。

5.1 向邮箱写入数据

xQueueOverwrite()用于长度为1的队列,这意味着队列要么为空,要么已满。

参数

参数解释
xQueue数据要发送到的队列的句柄。
pvItemToQueue指向要放置在队列中的项目的指针。队列可以容纳的每个项目的大小在创建队列时设置,许多字节将从pvItemToQueue复制到队列存储区域。

返回的值
xQueueOverwrite()是一个调用xQueueGenericSend()的宏,因此具有与xQueueSendToFront()相同的返回值。然而,pdPASS是唯一可以返回的值,因为即使队列已满,xQueueOverwrite()也会写入队列。

5.2 从邮箱中读取数据

在这里插入图片描述

描述
从队列中读取项目,但不从队列中删除该项目。下次使用xQueueReceive()或xQueuePeek()从同一队列中获取项目时,将返回相同的项目。

参数

参数解释
xQueue要读取的队列的句柄
pvBuffer指向将从队列中读取的数据复制到其中的内存的指针。缓冲区的长度必须至少等于队列项目大小。项目大小将由用于创建队列的xQueueCreate()或xQueueCreateStatic()调用的uxItemSize参数设置。
xTicksToWait如果读不到数据时,任务进入阻塞等待多少时间。

返回值

返回值解释
pdPASS如果成功从队列中读取数据,则返回pdPASS。如果指定了阻塞时间(xTicksToWait不为零),则在阻塞时间到期前,成功返回了数据,则函数也返回pdPASS。
errQUEUE_EMPTY如果由于队列已为空而无法从队列中读取数据,则返回这个错误。如果指定了阻塞时间(xTicksToWait不为零),则在阻塞时间到期后仍读不到数据,则也返回这个错误提示。

6、总结

在FreeRTOS中,队列Queue做为任务间数据传递的一种重要机制,在实际编程过程中会经常使用。在单片机的使用过程中,一个外设的数据的传递即可能涉及到任务与任务之间,也可能是在任务与中断之间的数据传递场景。因此在这个过程中,同步必须考虑任务阻塞的问题,即什么条件下,发送数据进入阻塞,阻塞多长时间,接收数据的任务什么时候必须阻塞,阻塞多长时间。
队列机制在函数的调用以及队列的实现中已相当完美的解决了多任务情况下的任务阻塞以及任务调度的机制。
队列是一个通用机制,信号量也是队列的一种应用。因此,掌握了队列的实用,信号量也就信手拈来。

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

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

相关文章

ReactDOM.render在react源码中执行之后发生了什么?

ReactDOM.render 通常是如下图使用,在提供的 container 里渲染一个 React 元素,并返回对该组件的引用(或者针对无状态组件返回 null)。本文主要是将ReactDOM.render的执行流程在后续文章中会对创建更新的细节进行分析&#xff0c…

MATLAB-plot3/ezplot3三维绘图

(1) plot3是三维绘图的基本函数,调用格式如下。1、plot3( X,Y,Z):绘制简单的三维曲线,当X、Y、Z是长度相同的向量时,plot3命令将绘制以向量X、Y、Z为(x, y,z)坐标值的三维曲线;当X、Y、Z是mn矩阵时,plot3命令将绘制m条…

Android 虚拟分区详解(四) 编译开关

Android Virtual A/B 系统简称 VAB,我将其称为虚拟分区。 本系列文章基于 Android R(11) 进行分析,如果没有特别说明,均基于代码版本 android-11.0.0_r46 请已经购买《Android 虚拟分区》专栏的朋友加我 wx 进 "虚拟分区专栏 VIP 答疑"群,作为本专栏文章的附加服…

(6)元对象系统与信号与槽机制

1. 元对象系统 元对象系统是一个基于标准C的扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。 什么是元对象 在计算机科学中,元对象是这样一个东西:它可以操纵、创建、描述、或执行其他对象。元对象描述的对象称为基对象。元对象可…

记一次搭建备库,使用连接串主库无法连接到备库

主库使用连接串连接备库失败 SQL> conn sys/oracleorcldg as sysdba ERROR: ORA-12528: TNS:listener: all appropriate instances are blocking new connections 备库已经建立了静态监听 # listener.ora Network Configuration File: /u01/app/oracle/product/11.2.0/db_1/…

安全寒假第一堂课

一、状态码 200 – 服务器成功返回网页 404 – 请求的网页不存在 503 – 服务器超时 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态码。 100(继续) 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一…

OpenCV实战(5)——图像运算详解

OpenCV实战(5)——图像运算详解0. 前言1. 图像基本运算2. 重载图像运算符2.1 加法运算符重载2.2 分割图像通道2.3 完整代码3. 图像重映射3.1 OpenCV 重映射函数3.2 完整代码小结系列链接0. 前言 图像可以以不同的方式进行组合,因为它们是正则…

XGBoost论文阅读

XGBoost: A Scalable Tree Boosting System 目录 XGBoost: A Scalable Tree Boosting System 1.摘要 2.方法 2.1 正则化学习目标 2.2 梯度提升树 2.3 收缩率和列采样 2.4分裂点查找算法 1.摘要 提出了一种新的稀疏性感知算法,用于稀疏数据和加权全图草图&a…

Python教程:什么是三级模式和二级映像?

美国国家标准学会(American National Standards Institute,ANSI)所属的标准计划与需求委员会(Standards Planning and Requirements Committee,SPARC)在1971年公布的研究报告中提出了ANSI-SPARC体系结构,即三级模式结构(或称为三层体系结构&a…

ArcGIS基础实验操作100例--实验53导出线、面要素的坐标值

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台:ArcGIS 10.6 实验数据:请访问实验1(传送门) 高级编辑篇--实验53 导出线、面要素的坐标值 目录 一、实验背景 二、实验数据 三、实验步骤 &#xf…

笔记杂项(一)

都是踩过的坑,趟过的水。 ubuntu虚拟机终端字体太小的设置方法:ubuntu18.04调整终端字体大小 这个方法试试看:https://zhuanlan.zhihu.com/p/139305626 容器里面编译内核代码,进程被杀掉的原因是触发了内核OOM killer&#xff0c…

干货| app自动化测试之Andriod微信小程序的自动化测试

随着微信小程序的功能和生态日益完善,很多公司的小程序项目页面结构越来越多,业务逻辑也越来越复杂。如何做好小程序的自动化测试就成为测试同学普遍面临的一大痛点难题。微信小程序小程序内嵌于微信内部,页面包含 Native 原生元素和 Web 元素…

华为防火墙与二层交换机对接配置VLAN上网设置

拓扑图 一、防火墙设置 1、G1/0/0接口设置IP&#xff0c;G1/0/1接口切换二层口设置VLAN&#xff0c;G1/0/0 桥接了本地无线网卡来模拟公网地址 <USG6000V1>sys [USG6000V1]sys FW1 [FW1]un in en# 设置公网IP [FW1]int g1/0/0 [FW1-GigabitEthernet1/0/0]ip addr 192.1…

package.json配置解读之入门

文章目录前言一、描述配置nameversionrepositorydescriptionkeywordslicenseauthor二、文件配置filestypemainbrowsermoduleexportsworkspaces三、脚本配置scriptsconfig四、结语前言 package.json是每个前端项目都会有的json文件&#xff0c;位于项目的根目录中。很多脚手架在…

RHCE(chrony服务器)

chrony服务器 chrony服务器是一个开源自由的网络时间协议NTP的客户端和服务器的软件&#xff0c;他能让计算机保持系统时钟和时钟服务器保持同步&#xff0c;让计算机保持精确的时间&#xff0c;chrony也可以作为服务端软件为其他计算机提供时间同步服务 chrony由两部分组成&…

openAI--十拳剑助你做AI时代的弄潮儿

AI它厉害&#xff08;diao&#xff09;吗&#xff1f; 最近大家玩chatgpt还好吗&#xff1f; 有被它的恋爱情商暴击到吗&#xff1f; 有没有觉得那在leetcode上所向无敌的技巧都是浮云吗&#xff1f; 今天&#xff0c;我为大家带来十个很好的AI平台。这一篇先介绍一下&…

【远程桌面】nomachine下载安装使用教程、zerotier下载安装使用教程超详细

文章目录一、软件介绍二、NoMachine远程桌面1.Windows下载安装使用2.Linux下载安装使用3.Android下载安装使用4.ARM下载安装使用&#xff08;未实践&#xff09;三、ZeroTier内网穿透0.官网注册账户1.Windows下载安装使用2.Linux下载安装使用3.Android下载安装使用4.ARM下载安装…

Android 学习 - 不完善

SharedPreference 共享参数用法 SharedPreference 是 Android 的一个轻量级存储工具, 采用的存储结构是Key-Value的键值对方式. 共享参数的存储介质是符合XML规范的配置文件. 保存路径是: /data/data/应用包名/shared_prefs/文件名.xml 利用元数据配置快捷菜单 (1)元数据的met…

【阶段二】Python数据分析Pandas工具使用11篇:探索性数据分析:数据的检验:卡方检验与t检验

本篇的思维导图: 探索性数据分析:数据的检验 卡方检验 在实际的学习或工作中,也会碰到关于离散型变量之间的探索性分析,如两个离散变量之间是否相互独立。对于该问题的解答,就需要运用统计学中的卡方检验了。卡方检验属于非参数的检验方法,其原假设是两个离散变…

Spring——最全Spring目录

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…