管道:介绍和基本服务
Posted 吴建明
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了管道:介绍和基本服务相关的知识,希望对你有一定的参考价值。
管道:介绍和基本服务
Pipes: introduction and basic services
与邮箱或队列相比,它们提供了在任务之间传递简单消息的更灵活的方式。
使用管道
在Nucleus SE中,管道是在构建时配置的。一个应用程序最多可以配置16个管道。如果未配置管道,则应用程序中不会包含与管道相关的数据结构或服务调用代码。
管道仅仅是一组存储位置,每个存储位置都足够大,足以容纳一个用户定义字节长度的数据项,对这些数据项的访问是受控制的,以便可以由多个任务安全地使用它。任务可以重复写入管道,直到所有位置都已满。任务可以从管道中读取,数据通常以先进先出(FIFO)的方式接收。尝试发送到完整管道或从空管道读取可能会导致错误或任务挂起,具体取决于在API调用和Nucleus SE配置中选择的选项。
管道和队列
Nucleus SE还支持队列,管道和队列之间的主要区别是消息大小。队列携带由单个ADDR组成的消息,这些地址通常是指针。管道承载任意字节长的消息;应用程序中每个管道的大小是固定的,并在配置时设置。
配置管道
管道数量
与Nucleus SE的大多数方面一样,管道的配置主要由nuse_config.h中的#define语句控制。键设置是NUSE_PIPE_NUMBER,它确定为应用程序配置了多少个管道。默认设置为0(即没有管道正在使用),您可以将其设置为最大16的任何值。错误的值将导致编译时错误,该错误由nuse_config_check.h中的测试生成(此错误包含在nuse_config.c中,因此使用此模块进行编译),从而导致编译一个#错误语句。
选择非零值是管道的“主启用”。这将导致一些数据结构被相应地定义和调整大小,在下一篇文章中会有更多内容。它还激活API启用设置。
API启用
Nucleus SE中的每个API函数(服务调用)在nuse_config.h中都有一个启用的#define符号。对于管道,包括:
NUSE_PIPE_SEND
NUSE_PIPE_RECEIVE
NUSE_PIPE_JAM
NUSE_PIPE_RESET
NUSE_PIPE_INFORMATION
NUSE_PIPE_COUNT
默认情况下,所有这些都设置为FALSE,从而禁用每个服务调用并禁止包含任何实现代码。要为应用程序配置管道,需要选择要使用的API调用,并将其启用符号设置为TRUE。
下面是从默认nuse_config.h文件中提取的内容。
#define
NUSE_PIPE_NUMBER 0 /* Number of pipes in the
system – 0-16 */
/* Service call enablers */
#define
NUSE_PIPE_SEND FALSE
#define
NUSE_PIPE_RECEIVE FALSE
#define
NUSE_PIPE_JAM FALSE
#define
NUSE_PIPE_RESET FALSE
#define
NUSE_PIPE_INFORMATION FALSE
#define
NUSE_PIPE_COUNT FALSE
如果启用了管道API函数而未配置管道(除非始终允许使用NUSE_pipe_Count()),则会导致编译时错误。如果您的代码使用尚未启用的API调用,则会导致链接时间错误,因为应用程序中不会包含任何实现代码。
管道服务电话
Nucleus RTOS支持10个与管道相关的服务调用,它们提供以下功能:
向管道发送消息。由Nucleus SE中的NUSE_Pipe_Send()实现。
从管道接收消息。由Nucleus SE中的NUSE_Pipe_Receive()实现。
在管道前面发个信息。由Nucleus SE中的NUSE_Pipe_Jam()实现。
将管道恢复到未使用状态,不暂停任何任务(重置)。由Nucleus SE中的NUSE_Pipe_Reset()实现。
提供有关指定管道的信息。由Nucleus SE中的NUSE_Pipe_Information()实现。
返回(当前)为应用程序配置的管道数的计数。由Nucleus SE中的NUSE_Pipe_Count()实现。
向应用程序添加新管道(创建)。未在Nucleus SE中实现。
从应用程序中删除管道(删除)。未在Nucleus SE中实现。
返回指向应用程序中所有管道(当前)的指针。未在Nucleus SE中实现。
向管道(广播)上挂起的所有任务发送消息。未在Nucleus SE中实现。
将详细检查每个服务调用的实现。
管道读写服务
可以在管道上执行的基本操作是向管道写入数据(有时称为发送)和从中读取数据(也称为接收)。也可以将数据写入管道前端,这也被称为堵塞。Nucleus RTOS和Nucleus SE各自为这些操作提供了三个基本API调用,这里将对此进行讨论。
写在管道上
Nucleus RTOS API对管道的写入调用非常灵活,允许您无限期地挂起,或者在操作无法立即完成的情况下暂停,也就是说,您尝试写入一个完整的管道。Nucleus SE提供相同的服务,除了task suspend是可选的并且不实现timeout。
Nucleus RTOS还提供了一个向管道广播的功能,但Nucleus SE不支持这种功能。
Nucleus RTOS API Call for Sending to a Pipe
Service call prototype:
STATUS
NU_Send_To_Pipe(NU_PIPE *pipe, VOID *message,
UNSIGNED size, UNSIGNED suspend);
Parameters:
pipe – pointer to the user-supplied pipe control block
message – a
pointer to the message to be sent
size – the number of
bytes in the message. If the pipe supports variable-length messages, this
parameter must be equal to or less than the message size supported by the pipe.
If the pipe supports fixed-size messages, this parameter must be exactly the
same as the message size supported by the pipe
suspend –
specification for task suspend; may be NU_NO_SUSPEND or NU_SUSPEND or a timeout value
Returns:
NU_SUCCESS – the call was completed successfully
NU_INVALID_PIPE – the pipe pointer is invalid
NU_INVALID_POINTER – the message pointer is NULL
NU_INVALID_SIZE – the message size is incompatible with the message size supported by the
pipe
NU_INVALID_SUSPEND – suspend was attempted from a non-task thread
NU_PIPEE_FULL – the pipe is full and suspend was not specified
NU_TIMEOUT – the pipe is still full even after suspending for the specified timeout
value
NU_PIPE_DELETED – the pipe was deleted while the task was suspended
NU_PIPE_RESET – the pipe was reset while the task was suspended
Nucleus SE API Call for Sending to a Pipe
This API call supports the key functionality of the Nucleus RTOS API.
Service call prototype:
<="" font="" >
<="" font="" >
STATUS NUSE_Pipe_Send(NUSE_PIPE pipe, U8
*message,
U8 suspend);
Parameters:
pipe – the index (ID) of
the pipe to be utilized
message – a pointer to the message to be sent, which is a
sequence of bytes as long as the configured message size of the pipe
suspend – specification for task suspend; may be NUSE_NO_SUSPEND or NUSE_SUSPEND
Returns:
NUSE_SUCCESS – the call was
completed successfully
NUSE_INVALID_PIPE – the pipe index is invalid
NUSE_INVALID_POINTER – the message pointer is NULL
NUSE_INVALID_SUSPEND – suspend was attempted from a non-task
thread or when blocking API calls were not enabled
NUSE_PIPE_FULL – the pipe is full and suspend was not
specified
NUSE_PIPE_WAS_RESET – the pipe was reset while the task was
suspended
管道发送的核SE实现
NUSE_Pipe_Send()–检查API函数是否已启用suspend()的条件编译(取决于u)的API是否已启用。我们将在这里分别研究这两种变体。
如果未启用阻塞,则此API调用的代码非常简单:
if (NUSE_Pipe_Items[pipe] == NUSE_Pipe_Size[pipe]) /* pipe full */{ return_value = NUSE_PIPE_FULL;}else /* pipe element available */{ data = &NUSE_Pipe_Data[pipe][NUSE_Pipe_Head[pipe]]; for (i=0; i<msgsize; i++) { *data++ = *message++; } NUSE_Pipe_Head[pipe] += msgsize; if (NUSE_Pipe_Head[pipe] == (NUSE_Pipe_Size[pipe] * msgsize)) { NUSE_Pipe_Head[pipe] = 0; } NUSE_Pipe_Items[pipe]++; return_value = NUSE_SUCCESS;}
该函数只检查管道中是否有空间,并使用NUSE_pipe_Head[]索引将消息存储在管道的数据区域中。
启用阻塞后,代码会变得更复杂:
do{ if (NUSE_Pipe_Items[pipe] == NUSE_Pipe_Size[pipe]) /* pipe full */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_PIPE_FULL; } else { /* block task */ NUSE_Pipe_Blocking_Count[pipe]++; NUSE_Suspend_Task(NUSE_Task_Active, (pipe << 4) | NUSE_PIPE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } else /* pipe element available */ { data = &NUSE_Pipe_Data[pipe][NUSE_Pipe_Head[pipe]]; for (i=0; i<msgsize; i++) { *data++ = *message++; } NUSE_Pipe_Head[pipe] += msgsize; if (NUSE_Pipe_Head[pipe] == (NUSE_Pipe_Size[pipe] * msgsize)) { NUSE_Pipe_Head[pipe] = 0; } NUSE_Pipe_Items[pipe]++; if (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; /* check whether a task is blocked on this pipe */ NUSE_Pipe_Blocking_Count[pipe]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; }} while (suspend == NUSE_SUSPEND);
对代码的一些解释可能有用:
代码包含在do…while循环中,当参数suspend的值为NUSE_suspend时,循环将继续。
如果管道已满,并且suspend设置为NUSE_NO_suspend,则API调用将以NUSE_pipe_full退出。如果suspend设置为NUSE_suspend,则任务将被挂起。返回时(即,当任务被唤醒时),如果返回值为NUSE_SUCCESS,表示任务被唤醒是因为消息已被读取(而不是重置管道),则代码循环返回顶部。
如果管道未满,则使用NUSE_pipe_Head[]索引存储提供的消息,以将消息存储在管道的数据区域中。检查管道上是否有任何任务挂起(等待接收)。如果有任务等待,第一个任务被唤醒。suspend变量被设置为NUSE_NO_suspend,API调用将退出并返回NUSE_SUCCESS。
从管子里读东西
从管道读取的Nucleus RTOS API调用非常灵活,使您能够无限期地挂起,或者在操作无法立即完成的情况下暂停,也就是说,您尝试从空管道中读取。Nucleus SE提供相同的服务,除了task suspend是可选的并且不实现timeout。
ucleus RTOS API Call for Receiving from a Pipe
Service call prototype:
STATUS NU_Receive_From_Pipe(NU_PIPE *pipe, VOID *message, UNSIGNED size, UNSIGNED *actual_size, UNSIGNED suspend);
Parameters:
pipe – pointer to user-supplied pipe control block
message –
pointer to storage for the message to be received
size –
the number of bytes in the message. This number must correspond to the message
size defined when the pipe was created
suspend –
specification for task suspend; may be NU_NO_SUSPEND or NU_SUSPEND or
a timeout value
Returns:
NU_SUCCESS –
the call was completed successfully
NU_INVALID_PIPE –
the pipe pointer is invalid
NU_INVALID_POINTER –
the message pointer is NULL
NU_INVALID_SUSPEND –
suspend was attempted from a non-task
NU_PIPE_EMPTY –
the pipe is empty and suspend was not specified
NU_TIMEOUT –
indicates that the pipe is still empty even after suspending for the specified
timeout value
NU_PIPE_DELETED –
the pipe was deleted while the task was suspended
NU_PIPE_RESET –
the pipe was reset while the task was suspended
Nucleus SE API Call for Receiving from a Pipe
This API call supports the key functionality of the Nucleus RTOS API.
Service call prototype:
STATUS
NUSE_Pipe_Receive(NUSE_PIPE pipe, U8 *message,
U8
suspend);
Parameters:
pipe –
the index (ID) of the pipe to be utilized
message –
a pointer to storage for the message to be received, which is a sequence of
bytes as long as the configured message size of the pipe
suspend –
specification for task suspend; may be NUSE_NO_SUSPEND or NUSE_SUSPEND
Returns:
NUSE_SUCCESS –
the call was completed successfully
NUSE_INVALID_PIPE –
the pipe index is invalid
NUSE_INVALID_POINTER –
the message pointer is NULL
NUSE_INVALID_SUSPEND –
suspend was attempted from a non-task thread or when blocking API calls were
not enabled
NUSE_PIPE_EMPTY –
the pipe is empty and suspend was not specified
NUSE_PIPE_WAS_RESET –
the pipe was reset while the task was suspended
Nucleus SE Implementation of Pipe Receive
管道接收的核SE实现
NUSE_Pipe_Receive()API函数的大部分代码(在参数检查之后)是通过条件编译选择的,这取决于是否启用了对阻塞(任务挂起)API调用的支持。我们将在这里分别研究这两种变体。
如果未启用阻塞,则此API调用的代码非常简单:
if (NUSE_Pipe_Items[pipe] == 0) /* pipe empty */{ return_value = NUSE_PIPE_EMPTY;}else{ /* message available */ data = &NUSE_Pipe_Data[pipe][NUSE_Pipe_Tail[pipe]]; for (i=0; i<msgsize; i++) { *message++ = *data++; } NUSE_Pipe_Tail[pipe] += msgsize; if (NUSE_Pipe_Tail[pipe] == (NUSE_Pipe_Size[pipe] * msgsize)) { NUSE_Pipe_Tail[pipe] = 0; } NUSE_Pipe_Items[pipe]--; *actual_size = msgsize; return_value = NUSE_SUCCESS;}
只需使用管道中的指针NUSE_Pipe_Tail[]从数据管道中返回数据,并通过管道返回数据。 启用阻塞后,代码会变得更复杂:
do{ if (NUSE_Pipe_Items[pipe] == 0) /* pipe empty */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_PIPE_EMPTY; } else { /* block task */ NUSE_Pipe_Blocking_Count[pipe]++; NUSE_Suspend_Task(NUSE_Task_Active, (pipe << 4) | NUSE_PIPE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } else { /* message available */ data = &NUSE_Pipe_Data[pipe][NUSE_Pipe_Tail[pipe]]; for (i=0; i<msgsize; i++) { *message++ = *data++; } NUSE_Pipe_Tail[pipe] += msgsize; if (NUSE_Pipe_Tail[pipe] == (NUSE_Pipe_Size[pipe] * msgsize)) { NUSE_Pipe_Tail[pipe] = 0; } NUSE_Pipe_Items[pipe]--; if (NUSE_Pipe_Blocking_Count[pipe] != 0) { U8 index; /* check whether a task is blocked */ /* on this pipe */ NUSE_Pipe_Blocking_Count[pipe]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_PIPE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == pipe)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } *actual_size = msgsize; return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; }} while (suspend == NUSE_SUSPEND);
对代码的一些解释可能有用:
代码包含在do…while循环中,当参数suspend的值为NUSE_suspend时,循环将继续。
如果管道为空,并且suspend设置为NUSE_NO_suspend,则API调用将以NUSE_pipe_empty退出。如果suspend设置为NUSE_suspend,则任务将被挂起。返回时(即,当任务被唤醒时),如果返回值为NUSE_SUCCESS,表示任务因已发送消息而被唤醒(而不是重置管道),则代码循环返回顶部。
如果管道包含任何消息,则使用NUSE_pipe_Tail[]索引返回存储的消息,以从管道的数据区域获取消息。检查管道上是否有任何任务被挂起(等待发送)。如果有任务等待,第一个任务被唤醒。suspend变量被设置为NUSE_NO_suspend,API调用将退出并返回NUSE_SUCCESS。
写在管道前面
Nucleus RTOS-API调用写入管道的前端非常灵活,允许您无限期地挂起,或者在操作无法立即完成的情况下暂停超时;也就是说,您尝试写入一个完整的管道。Nucleus SE提供相同的服务,除了task suspend是可选的并且不实现timeout。
Nucleus RTOS API Call for Jamming to a Pipe
Service call prototype:
STATUS NU_Send_To_Front_Of_Pipe(NU_PIPE *pipe, VOID *message,
UNSIGNED
size, UNSIGNED suspend);
Parameters:
pipe –
pointer to a user-supplied pipe control block
message –
a pointer to the message to be sent
size –
the number of bytes in the message. If the pipe supports variable-length
messages, this parameter must be equal to or less than the message size
supported by the pipe. If the pipe supports fixed-size messages, this parameter
must be exactly the same as the message size supported by the pipe.
suspend –
specification for task suspend; may be NU_NO_SUSPEND or NU_SUSPEND or
a timeout value
Returns:
NU_SUCCESS –
the call was completed successfully
NU_INVALID_PIPE –
the pipe pointer is invalid
NU_INVALID_POINTER –
the message pointer is NULL
NU_INVALID_SIZE –
the message size is incompatible with the message size supported by the pipe
NU_INVALID_SUSPEND –
suspend was attempted from a non-task thread
NU_PIPE_FULL –
the pipe is full and suspend was not specified
NU_TIMEOUT –
the pipe is still full even after suspending for the specified timeout value
NU_PIPE_DELETED –
the pipe was deleted while the task was suspended
NU_PIPE_RESET –
the pipe was reset while the task was suspended
Nucleus SE API Call for Jamming to a Pipe
This API call supports the key functionality of the Nucleus RTOS API.
Service call prototype:
STATUS
NUSE_Pipe_Jam(NUSE_PIPE pipe, ADDR *message,
U8
suspend);
Parameters:
pipe –
the index (ID) of the pipe to be utilized
message –
a pointer to the message to be sent, which is a sequence of bytes as long as
the configured message size of the pipe
suspend –
specification for task suspend; may be NUSE_NO_SUSPEND or NUSE_SUSPEND
Returns:
NUSE_SUCCESS –
the call was completed successfully
NUSE_INVALID_PIPE –
the pipe index is invalid
NUSE_INVALID_POINTER –
the message pointer is NULL
NUSE_INVALID_SUSPEND –
suspend was attempted from a non-task thread or when blocking API calls were
not enabled
NUSE_PIPE_FULL –
the pipe is full and suspend was not specified
NUSE_PIPE_WAS_RESET –
the pipe was reset while the task was suspended
Nucleus SE Implementation of Jamming to a Pipe
The bulk of the code of the NUSE_Pipe_Jam() API function is very similar to that of NUSE_Pipe_Send() , except that the data is stored using the NUSE_Pipe_Tail[] index, thus:
if (NUSE_Pipe_Items[pipe] == NUSE_Pipe_Size[pipe]) /* pipe full */{ return_value = NUSE_PIPE_FULL;}else /* pipe element available */{ if (NUSE_Pipe_Tail[pipe] == 0) { NUSE_Pipe_Tail[pipe] = (NUSE_Pipe_Size[pipe] - 1) * msgsize; } else { NUSE_Pipe_Tail[pipe] -= msgsize; } data = &NUSE_Pipe_Data[pipe][NUSE_Pipe_Tail[pipe]]; for (i=0; i<msgsize; i++) { *data++ = *message++; } NUSE_Pipe_Items[pipe]++; return_value = NUSE_SUCCESS;}
以上是关于管道:介绍和基本服务的主要内容,如果未能解决你的问题,请参考以下文章
[Go] 通过 17 个简短代码片段,切底弄懂 channel 基础