378 lines
13 KiB
Markdown
378 lines
13 KiB
Markdown
# 线程间通信
|
||
XiUOS 的线程间通信使用邮箱、消息队列和信号三种方式。下面将分别介绍。
|
||
|
||
## 邮箱
|
||
### 邮箱的工作机制
|
||
XiUOS 的每一封邮件大小为4字节,也就是一个指针的大小。典型的邮箱应用是,线程或中断服务发送一封4字节长度的邮件到邮箱中,其他的线程可以从邮箱中接收邮件并进行处理。</br></br>
|
||
当一个线程向邮箱发送邮件时,如果邮箱没满,就将邮件复制到邮箱中。如果邮箱满了并且超时时间设置为0,则返回`-XS_EFULL`;如果超时时间不为0,先将该发送线程挂起,当邮箱中的邮件被取出空出空间后,等待挂起的发送线程被唤醒继续发送。但是如果到达超时时间仍没有空间,则返回`-XS_EFULL`。</br></br>
|
||
当一个线程从邮箱中接收邮件时,如果邮箱中有邮件,就读出该邮件的内容,并返回XS_EOK。如果邮箱中没有邮件并且超时设置为0,则返回`-XS_ETIMEOUT`;如果超时时间不为0,则将该线程挂在邮箱的等待线程中,有邮件时唤醒。但是如果到达超时时间仍没有邮件,返回`-XS_ETIMEOUT`。
|
||
### 结构体定义和函数接口
|
||
|
||
#### 邮箱控制块
|
||
```C
|
||
struct xs_Mailbox
|
||
{
|
||
char name[XS_NAME_MAX];
|
||
xs_uint8 style;
|
||
|
||
xs_uint8 sign;
|
||
|
||
xs_list_t link;
|
||
|
||
xs_list_t pend_list;
|
||
|
||
xs_ubase *msg_buf;
|
||
xs_uint16 size;
|
||
xs_uint16 index;
|
||
xs_uint16 in_offset;
|
||
xs_uint16 out_offset;
|
||
|
||
xs_list_t pend_sender_list;
|
||
};
|
||
|
||
typedef struct xs_Mailbox *xs_mailbox_t;
|
||
```
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| name | 邮箱名称 |
|
||
| style | 线程等待的方式,可以取FIFO或PRIO |
|
||
| sign | 标记邮箱的创建方式,静态创建或动态创建 |
|
||
| link | 邮箱存放的列表 |
|
||
| pend_list | 线程等待队列 |
|
||
| msg_buf | 邮箱缓冲区开始地址 |
|
||
| size | 邮箱最大容纳的邮件数 |
|
||
| index | 邮箱中邮件的索引 |
|
||
| in_offset | 邮箱缓冲的进指针 |
|
||
| out_offset | 邮箱缓冲的出指针 |
|
||
| pend_sender_list | 发送线程的挂起等待队列 |
|
||
|
||
#### 函数接口
|
||
|
||
```C
|
||
xs_err_t xs_InitMailbox(xs_mailbox_t mb,
|
||
const char* name,
|
||
void* msgpool,
|
||
xs_size_t size,
|
||
xs_uint8 flag);
|
||
```
|
||
该函数用于创建一个静态邮箱对象,并将它加入邮箱管理列表中,创建成功返回`XS_OK`。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mb | 邮箱对象 |
|
||
| name | 邮箱名称 |
|
||
| msgpool | 缓冲区指针 |
|
||
| size | 邮箱容量 |
|
||
| flag | 线程等待的方式,可以取FIFO或PRIO |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_DetachMailbox(xs_mailbox_t mb);
|
||
```
|
||
该函数用于脱离静态初始化的邮箱对象。该函数唤醒所有挂在该邮箱上的线程,然后将该邮箱从邮箱存放的列表中脱离,返回`XS_OK`。
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mb | 邮箱对象 |
|
||
|
||
```C
|
||
xs_mailbox_t xs_CreateMailbox(const char *name, xs_size_t size, xs_uint8 flag);
|
||
```
|
||
该函数用于创建一个动态邮箱对象,并将它加入邮箱管理列表中。创建成功则返回`XS_OK`,创建失败返回`XS_NULL`。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| name | 邮箱名称 |
|
||
| size | 邮箱容量 |
|
||
| flag | 线程等待的方式,可以取FIFO或PRIO |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_DeleteMailbox(xs_mailbox_t mb);
|
||
```
|
||
该函数用于删除动态邮箱对象。先唤醒挂在邮箱对象上的所有线程,然后释放该邮箱的使用内存,最后删除邮箱对象。返回`XS_OK`。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mb | 邮箱对象 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MailboxSendWait(xs_mailbox_t mb, xs_ubase value, xs_int32 timeout);
|
||
```
|
||
该函数用于发送邮件。如果邮箱满了并且过了timeout时间仍然没有空位,则返回`-XS_FULL`;否则将邮件存放入邮箱。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mb | 邮箱对象 |
|
||
| value | 邮件内容 |
|
||
| timeout | 超时时间 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MailboxSend(xs_mailbox_t mb, xs_ubase value);
|
||
```
|
||
该函数用于发送邮件。与`xs_MailboxSendWait`唯一不同的地方是,该函数的`timeout == 0`。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mb | 邮箱对象 |
|
||
| value | 邮件内容 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MailboxRecv(xs_mailbox_t mb, xs_ubase *value, xs_int32 timeout);
|
||
```
|
||
该函数用于接受邮件。
|
||
|
||
```C
|
||
xs_err_t xs_CmdControlMailbox(xs_mailbox_t mb, int cmd, void *arg);
|
||
```
|
||
该函数用于获取或设置邮箱的其他属性。目前当`cmd == XS_LINKLIST_CMD_RESET`时,重新初始化邮箱。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mb | 邮箱对象 |
|
||
| cmd | 需要执行的命令 |
|
||
| arg | 命令的参数 |
|
||
</br>
|
||
|
||
## 消息队列
|
||
### 消息队列的工作机制
|
||
消息队列能够接收线程或中断产生的不固定长度的消息,并储存到自己的内存空间中。其他线程可以从消息队列中读取相应的消息。当消息队列为空时,挂起读取线程,当有新的消息到达时,唤醒读取线程进行读取。
|
||
### 结构体定义和函数接口
|
||
#### 结构体定义
|
||
```C
|
||
struct xs_MessageQueue
|
||
{
|
||
char name[XS_NAME_MAX];
|
||
xs_uint8 style;
|
||
|
||
xs_uint8 sign;
|
||
|
||
|
||
xs_list_t link;
|
||
|
||
xs_list_t pend_list;
|
||
|
||
void *msg_buf;
|
||
|
||
xs_uint16 size;
|
||
xs_uint16 max_msgs;
|
||
|
||
xs_uint16 index;
|
||
|
||
void *mq_listhead;
|
||
void *mq_listtail;
|
||
void *mq_free;
|
||
|
||
xs_list_t pend_sender_thread;
|
||
};
|
||
typedef struct xs_MessageQueue *xs_messagequeue_t;
|
||
```
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| name | 消息队列名称 |
|
||
| style | 线程等待的方式,可以取FIFO或PRIO |
|
||
| sign | 标记消息队列的创建方式,静态创建或动态创建 |
|
||
| link | 消息队列存放的列表 |
|
||
| pend_list | 线程等待队列 |
|
||
| msg_buf | 存放消息的缓存区 |
|
||
| size | 缓存区大小 |
|
||
| max_msgs | 一条消息的最大长度 |
|
||
| index | 当前消息数 |
|
||
| mq_listhead | 消息链表头 |
|
||
| mq_listtail | 消息链表尾 |
|
||
| mq_free | 空闲消息链表 |
|
||
| pend_sender_thread | 发送线程的挂起等待队列 |
|
||
|
||
|
||
#### 函数接口
|
||
```C
|
||
xs_err_t xs_InitMessageQueue(xs_messagequeue_t mq,
|
||
const char *name,
|
||
void *msgpool,
|
||
xs_size_t msg_size,
|
||
xs_size_t pool_size,
|
||
xs_uint8 flag)
|
||
```
|
||
该函数用于初始化一个静态消息队列,并将它加入消息队列管理列表中。创建成功后返回`XS_OK`。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
| name | 消息队列名称 |
|
||
| msgpool | 指向存放消息的内存空间的指针 |
|
||
| msg_size | 一条消息的最大长度 |
|
||
| pool_size | 存放消息的内存区大小 |
|
||
| flag | 消息队列采用的等待方式,可取FIFO或PRIO |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_DetachMessageQueue(xs_messagequeue_t mq)
|
||
```
|
||
该函数用于将一个静态消息队列从消息队列管理列表中移除。移除成功则返回`XS_OK`。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
</br>
|
||
|
||
```C
|
||
xs_messagequeue_t xs_CreateMessageQueue(const char *name,
|
||
xs_size_t msg_size,
|
||
xs_size_t max_msgs,
|
||
xs_uint8 flag)
|
||
```
|
||
该函数用于动态创建一个消息队列,并将消息队列加入消息队列管理列表。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| name | 消息队列名称 |
|
||
| msg_size | 缓冲区的大小 |
|
||
| max_msgs | 一条消息的最大长度 |
|
||
| flag | 消息队列采用的等待方式,可取FIFO或PRIO |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_DeleteMessageQueue(xs_messagequeue_t mq)
|
||
```
|
||
该函数用于将由`xs_CreateMessageQueue`创建的消息队列从消息队列管理列表中删除。</br>
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MessageQueueSendwait(xs_messagequeue_t mq,
|
||
const void *buffer,
|
||
xs_size_t size,
|
||
xs_int32 timeout)
|
||
```
|
||
发送消息时,消息队列对象先从空闲消息链表上取下一个空闲消息块,把线程或者中断服务程序发送的消息内容复制到消息块上,然后把该消息块挂到消息队列的尾部,成功则返回`XS_EOK`。如果消息长度大于最大长度,则返回`XS_ERROR`。如果消息队列满了,则根据超时时间设定进行等待,超时则返回`-XS_EFULL`。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
| buffer | 消息内容 |
|
||
| size | 消息大小 |
|
||
| timeout | 超时时间 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MessageQueueSend(xs_messagequeue_t mq, const void *buffer, xs_size_t size)
|
||
```
|
||
该函数是timeout为0的`xs_MessageQueueSendwait`。
|
||
</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
| buffer | 消息内容 |
|
||
| size | 消息大小 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MessageQueueUrgentSend(xs_messagequeue_t mq, const void *buffer, xs_size_t size)
|
||
```
|
||
|
||
发送紧急消息和发送普通消息的区别是:当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
| buffer | 消息内容 |
|
||
| size | 消息大小 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_MessageQueueReceive(xs_messagequeue_t mq,
|
||
void *buffer,
|
||
xs_size_t size,
|
||
xs_int32 timeout)
|
||
```
|
||
当队列中有消息时,接收消息。队列中没有消息时,根据超时时间设定进行等待。接收一个消息后消息队列上的队首消息被转移到了空闲消息链表的尾部。成功收到消息返回`XS_EOK`,超时返回`XS_ETIMEOUT`。接收消息失败返回`-XS_ERROR`。
|
||
</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
| buffer | 消息内容 |
|
||
| size | 消息大小 |
|
||
| timeout | 超时时间 |
|
||
</br>
|
||
|
||
```C
|
||
xs_err_t xs_CmdControlMessageQueue(xs_messagequeue_t mq, int cmd, void *arg)
|
||
```
|
||
该函数用于获取或设置消息队列的其他属性。目前当cmd == XS_LINKLIST_CMD_RESET时,重新初始化消息队列。
|
||
</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| mq | 消息队列对象指针 |
|
||
| cmd | 需要执行的命令 |
|
||
| arg | 命令的参数 |
|
||
</br>
|
||
|
||
|
||
## 信号
|
||
### 信号的工作机制
|
||
信号是在软件层次对中断机制的一种模拟。XiUOS中提供了信号用作异步通信的方式。</br></br>
|
||
收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类:</br></br>
|
||
第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。</br></br>
|
||
第二种方法是,忽略某个信号,对该信号不做任何处理。</br></br>
|
||
第三种方法是,对该信号的处理保留系统的默认值。</br></br>
|
||
具体来说,假设其他线程要向线程1通信,则在线程1中安装一个信号并解除阻塞,在安装的同时,规定对该信号的异常处理方式。其他线程给线程1发送信号,触发线程1对该信号的处理。
|
||
### 函数接口
|
||
```C
|
||
xs_sighandler_t xs_InstallSignal(int signo, xs_sighandler_t handler)
|
||
```
|
||
该函数用于给当前线程安装一个新的信号。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| signo | 信号值 |
|
||
| handler | 设置对信号值的处理方式,可以是用户自定义的函数,`SIG_IGN`或`SIG_DFL` |
|
||
</br>
|
||
|
||
handler的值为`SIG_IGN`时,忽略该信号;</br>
|
||
handler的值为`SIG_DFL`时,执行信号默认处理函数`_SignalDefaultHandler`;</br>
|
||
handler为函数句柄时,执行用户函数。 </br>
|
||
|
||
```C
|
||
void xs_MaskSignal(int signo)
|
||
```
|
||
该函数用于屏蔽一个信号。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| signo | 信号值 |
|
||
</br>
|
||
|
||
```C
|
||
void xs_UnmaskSignal(int signo)
|
||
```
|
||
该函数用于解除对一个信号的屏蔽。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| signo | 信号值 |
|
||
</br>
|
||
|
||
```C
|
||
int xs_WaitSignal(const xs_sigset_t *set, xs_siginfo_t *si, xs_int32 timeout)
|
||
```
|
||
等待`set`信号到来。如果没等到这个信号,则将该线程挂起直到等到信号或超过超时时间。如果等到信号,则将`set`信号指针存入`si`。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| set | 指定等待的信号 |
|
||
| set | 指向存储等到信号信息的指针 |
|
||
| set | 超时时间 |
|
||
</br>
|
||
|
||
```C
|
||
int xs_KThreadKill(TaskDescriptorPointer tid, int sig)
|
||
```
|
||
该函数用于给线程`tid`发送信号`sig`。</br>
|
||
|
||
| 参数 | 描述 |
|
||
| ------ | ------ |
|
||
| tid | 线程 |
|
||
| sig | 信号值 |
|
||
|
||
```C
|
||
int xs_SysInitSignal(void)
|
||
```
|
||
该函数用于在内存池中给信号分配一片内存区。 |