diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md index 07cd64afb31bb53e3ab032559c2fd12336d5dc47..1042f744f871c6f81917467ff921c7b521ce7624 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/ipc1/ipc1.md @@ -50,7 +50,7 @@ struct rt_semaphore { struct rt_ipc_object parent; /* 继承自 ipc_object 类 */ - rt_uint16_t value; /* 信号量的值 */ + rt_uint16_t value; /* 信号量的值 */ }; /* rt_sem_t 是指向 semaphore 结构体的指针类型 */ typedef struct rt_semaphore* rt_sem_t; @@ -534,12 +534,15 @@ the consumer exit! 当持有信号量的线程完成它处理的工作时,释放这个信号量,可以把等待在这个信号量上的线程唤醒,让它执行下一部分工作。这类场合也可以看成把信号量用于工作完成标志:持有信号量的线程完成它自己的工作,然后通知等待该信号量的线程继续下一部分工作。 -#### 锁 +#### ~~锁~~(该功能仅做了解) 锁,单一的锁常应用于多个线程间对同一共享资源(即临界区)的访问。信号量在作为锁来使用时,通常应将信号量资源实例初始化成 1,代表系统默认有一个资源可用,因为信号量的值始终在 1 和 0 之间变动,所以这类锁也叫做二值信号量。如下图所示,当线程需要访问共享资源时,它需要先获得这个资源锁。当这个线程成功获得资源锁时,其他打算访问共享资源的线程会由于获取不到资源而挂起,这是因为其他线程在试图获取这个锁时,这个锁已经被锁上(信号量值是 0)。当获得信号量的线程处理完毕,退出临界区时,它将会释放信号量并把锁解开,而挂起在锁上的第一个等待线程将被唤醒从而获得临界区的访问权。 ![锁](figures/06sem_lock.png) +> [!NOTE] +> 注:在计算机操作系统发展历史上,人们早期使用二值信号量来保护临界区,但是在1990年,研究人员发现了使用信号量保护临界区会导致无界优先级反转的问题,因此提出了互斥量的概念。如今,我们已经不使用二值信号量来保护临界区,互斥量取而代之。 + #### 中断与线程的同步 信号量也能够方便地应用于中断与线程间的同步,例如一个中断触发,中断服务例程需要通知线程进行相应的数据处理。这个时候可以设置信号量的初始值是 0,线程在试图持有这个信号量时,由于信号量的初始值是 0,线程直接在这个信号量上挂起直到信号量被释放。当中断触发时,先进行与硬件相关的动作,例如从硬件的 I/O 口中读取相应的数据,并确认中断以清除中断源,而后释放一个信号量来唤醒相应的线程以做后续的数据处理。例如 FinSH 线程的处理方式,如下图所示。 @@ -575,7 +578,7 @@ the consumer exit! ![优先级反转 (M 为信号量)](figures/06priority_inversion.png) -在 RT-Thread 操作系统中,互斥量可以解决优先级翻转问题,实现的是优先级继承算法。优先级继承是通过在线程 A 尝试获取共享资源而被挂起的期间内,将线程 C 的优先级提升到线程 A 的优先级别,从而解决优先级翻转引起的问题。这样能够防止 C(间接地防止 A)被 B 抢占,如下图所示。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。 +在 RT-Thread 操作系统中,互斥量可以解决优先级翻转问题,实现的是优先级继承协议 (Sha, 1990)。优先级继承是通过在线程 A 尝试获取共享资源而被挂起的期间内,将线程 C 的优先级提升到线程 A 的优先级别,从而解决优先级翻转引起的问题。这样能够防止 C(间接地防止 A)被 B 抢占,如下图所示。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。 ![优先级继承 (M 为互斥量)](figures/06priority_inherit.png) @@ -616,12 +619,12 @@ rt_mutex 对象从 rt_ipc_object 中派生,由 IPC 容器所管理。 rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag); ``` -可以调用 rt_mutex_create 函数创建一个互斥量,它的名字由 name 所指定。当调用这个函数时,系统将先从对象管理器中分配一个 mutex 对象,并初始化这个对象,然后初始化父类 IPC 对象以及与 mutex 相关的部分。互斥量的 flag 标志设置为 RT_IPC_FLAG_PRIO,表示在多个线程等待资源时,将由优先级高的线程优先获得资源。flag 设置为 RT_IPC_FLAG_FIFO,表示在多个线程等待资源时,将按照先来先得的顺序获得资源。下表描述了该函数的输入参数与返回值: +可以调用 rt_mutex_create 函数创建一个互斥量,它的名字由 name 所指定。当调用这个函数时,系统将先从对象管理器中分配一个 mutex 对象,并初始化这个对象,然后初始化父类 IPC 对象以及与 mutex 相关的部分。互斥量的 flag 标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理。下表描述了该函数的输入参数与返回值: |**参数** |**描述** | |------------|-------------------------------------------------------------------| | name | 互斥量的名称 | -| flag | 互斥量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO | +| flag | 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理 | |**返回** | —— | | 互斥量句柄 | 创建成功 | | RT_NULL | 创建失败 | @@ -655,7 +658,7 @@ rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag); |----------|-------------------------------------------------------------------| | mutex | 互斥量对象的句柄,它由用户提供,并指向互斥量对象的内存块 | | name | 互斥量的名称 | -| flag | 互斥量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO | +| flag | 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理 | |**返回**| —— | | RT_EOK | 初始化成功 | @@ -1072,6 +1075,9 @@ rt_event_t rt_event_create(const char* name, rt_uint8_t flag); | RT_NULL | 创建失败 | | 事件对象的句柄 | 创建成功 | +> [!NOTE] +> 注:RT_IPC_FLAG_FIFO 属于非实时调度方式,除非应用程序非常在意先来后到,并且你清楚地明白所有涉及到该事件集的线程都将会变为非实时线程,方可使用 RT_IPC_FLAG_FIFO,否则建议采用 RT_IPC_FLAG_PRIO,即确保线程的实时性。 + 系统不再使用 rt_event_create() 创建的事件集对象时,通过删除事件集对象控制块来释放系统资源。删除事件集可以使用下面的函数接口: ```c diff --git a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md index 02a79db46c9bccf13a1049fbd3f63adf08850546..03088e61cdc9a71af5b60bbc4a81149a4b81c463 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2.md @@ -66,6 +66,9 @@ rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag); | RT_NULL | 创建失败 | | 邮箱对象的句柄 | 创建成功 | +> [!NOTE] +> 注:RT_IPC_FLAG_FIFO 属于非实时调度方式,除非应用程序非常在意先来后到,并且你清楚地明白所有涉及到该邮箱的线程都将会变为非实时线程,方可使用 RT_IPC_FLAG_FIFO,否则建议采用 RT_IPC_FLAG_PRIO,即确保线程的实时性。 + 当用 rt_mb_create() 创建的邮箱不再被使用时,应该删除它来释放相应的系统资源,一旦操作完成,邮箱将被永久性的删除。删除邮箱的函数接口如下: ```c @@ -445,6 +448,9 @@ rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size, | 消息队列对象的句柄 | 成功 | | RT_NULL | 失败 | +> [!NOTE] +> 注:RT_IPC_FLAG_FIFO 属于非实时调度方式,除非应用程序非常在意先来后到,并且你清楚地明白所有涉及到该消息队列的线程都将会变为非实时线程,方可使用 RT_IPC_FLAG_FIFO,否则建议采用 RT_IPC_FLAG_PRIO,即确保线程的实时性。 + 当消息队列不再被使用时,应该删除它以释放系统资源,一旦操作完成,消息队列将被永久性地删除。删除消息队列的函数接口如下: ```c