QNX操作系统C语言&嵌入式

QNX Neutrino微内核

2019-01-01  本文已影响1人  Loyen

QNX相关历史文章:

介绍

QNX Neutrino微内核procnto实现了嵌入式实时系统中常用的核心POSIX功能,并提供基本的消息传递服务。而未实现的POSIX功能(比如文件、设备IO)则可以通过可选的进程和共享库来提供。
微内核包含了一些基本对象以及操作这些对象的例程,这些对象定义得很具体,而且高度可重用,整个操作系统在此之上构建的。

QNX微内核

系统服务

QNX Neutrino微内核提供了一系列系统调用来支持以下服务:

线程和进程

在开发应用程序时(实时、嵌入式、图形等),通常会用到POSIX线程模型来实现多个算法同时执行。线程是微内核中最小的执行和调度单元,进程可以认为是线程的“容器”,定义了线程将在其中执行的“地址空间”,进程会包含一个或多个线程。
应用程序中的线程有可能相互独立,也可能紧密的联系,QNX Neutrino提供了丰富的IPC和同步服务。
其中不涉及微内核线程调用的POSIX接口有如下:


下表中的POSIX接口,微内核中有对应的接口实现一样的功能,允许自己来选择:


线程属性

尽管进程中的线程共享进程地址空间中的所有内容,但每个线程仍然有一些“私有”数据,在某些情况下,这些私有数据在内核中受到保护,比如线程ID/进程ID;而其他的可能不受保护,比如线程的堆栈。
值得注意的私有数据有:

线程生命周期

线程是动态创建的,创建时涉及到资源分配和初始化,销毁时涉及到资源回收,当线程执行时,它的状态通常描述为“就绪”或“阻塞”,具体来说有以下状态:


线程状态

线程调度

当执行内核调用、异常、硬件中断时,当前的执行线程会被挂起,每当任何线程的执行状态发生改变时,都会做出调度决策。通常被挂起的线程将会被恢复,这时线程调度器将进行一次上下文切换。
有三种情况会发生上下文切换:

调度优先级

每个线程都会分配一个优先级,QNX Neutrino支持256级优先级,non-root线程可以将优先级设置为1-63,与调度策略无关,root线程(有效uid为0),能将优先级设置为63之上。通常会采用优先级继承来应对优先级反转的问题。
下图中描述了一个就绪队列中,B-F是就绪,G-Z是阻塞,A正在运行:


就绪队列

调度策略

QNX Neutrino支持三种调度策略,这个也跟Nuttx系统一样:

  1. FIFO调度
    在FIFO调度下,线程会在两种情况下放弃执行:1)主动放弃CPU;2)高优先级线程抢占;


    FIFO调度
  2. Round-Robin调度
    在Round-Robin调度下,线程会在三种情况下放弃执行:1)主动放弃CPU;2)高优先级线程抢占;3)时间片消耗完毕;


    Round-Robin调度

    时间片为4倍时钟周期。与FIFO调度不同的是多了一个时间片的控制。

  3. Sporadic调度
    Sporadic调度策略通常用于在给定时间段内提供线程执行时间的上限,Sporadic调度会为线程执行提供“预算”。与FIFO调度一样,在阻塞或被抢占的情况下会放弃执行。Sporadic调度会自动降低线程的优先级,可以更精确的控制线程的行为。
    Sporadic调度时,线程优先级会在前台正常优先级N和后台低优先级L之间动态调整,通过使用下列参数控制调度条件:

在正常优先级N时,线程会执行budget时间C,当时间耗尽后,线程的优先级会调整至L。当Replenishment发生后又将恢复到原来的优先级,在一个T的时间周期内,线程将会有机会最大去执行C的运行时间,也能保证一个线程在N优先级的情况下只消耗C/T比例的系统资源。假设在一个系统中,线程不会被阻塞或抢占,运行情况如下图所示:


image.png

关于优先级和调度策略的设置,有以下接口来实现:

同步服务

QNX Neutrino提供POSIX标准线程级别的同步原语:



上述同步机制中,大部分都是由内核直接实现,除了以下几种:

  1. Mutex
    互斥锁是最简单的同步服务,用于对临界区的互斥访问,通常会用phtread_mutext_lock()/pthread_mutex_timedlock()来获取锁,使用phread_mutext_unlock()来释放锁,当获取不到锁的时候线程会阻塞等待,也可以使用非阻塞函数pthread_mutex_trylock()来测试Mutex是否已经被锁。

  2. Condvars
    条件变量用于在临界区来阻塞线程,直到满足某些条件,这些条件可以是任意复杂的,并且与Condvar无关。Condvar必须始终与Mutex一起使用。
    条件变量支持三种操作:

3.Barriers
屏障是一种同步机制,可以用于将多个协作线程阻塞在某个点等待,直到所有线程都完成后才可以继续。pthread_join()函数是用于等待线程终止,而屏障是用于等待多个线程在某个点“集合”,当线程都达到后,就可以取消阻塞所有线程并继续运行了。有以下接口:

4.Sleepon locks
Sleepon locksCondvar很像,也都是等待条件的满足,不同的是Condvars必须为每个检查的condition分配Mutex,而Sleepon locks可以复用一个Mutex

  1. Reader/writer locks
    读写锁通常用于“多个读取者,单个写入者”场景的同步,它的开销远大于Mutex,但是在这种数据访问模式下很有用。通常使用pthread_rwlock_rdlock()/pthread_rwlock_wrlock()/pthread_rwlock_unlock()接口。

  2. Semaphores
    信号量是常用的同步形式,允许线程在一个信号量上postwait来控制线程何时唤醒和休眠。信号量与其他同步原语的一个显著区别是信号量是异步安全的,可以由信号处理程序操作。如果想让一个信号处理程序唤醒一个线程,信号量是正确的选择。
    对于单个进程中的线程之间同步,互斥锁比信号量更有效。

  3. 通过调度策略来同步
    可以使用FIFO调度策略来保证同一优先级的线程不会在非SMP系统中并发运行。

  4. 通过消息传递来同步
    Send/Receive/ReplyIPC消息传递天然就是一种同步机制,在很多情况下让其他同步机制变得不必要。它们也是唯一可以跨网络使用的同步和IPC原语。

  5. 通过原子操作来同步
    QNX Neutrino提供以下原子操作:

时钟和定时器服务

时钟服务用于维护系统时间,同时内核也会使用时间服务来完成定时器操作。
时钟相关的接口如下:


QNX提供了POSIX定时器所有功能函数集,定时器模型很丰富,有以下几种timer类型:

中断处理

在实时系统中,减少不必要的CPU cycles是至关重要的,需要关注两个latency:中断latency,调度latency。

中断latency

中断latency指的是从硬件中断触发到执行驱动程序中中断处理函数的第一条指令之间的时间间隔。在QNX中,一直维持中断使能,但在某些特殊的代码中需要关闭中断,可能会造成大的延迟,在QNX中这个时间很短。


中断latency

调度latency

调度latency指的是从中断处理函数中返回后到驱动线程第一条指令执行的时间间隔,通常包括保存当前执行上下文,加载驱动线程上下文。尽管这个时间大于中断latency,但是QNX中调度延迟仍然很小。


调度latency

中断嵌套

QNX支持中断嵌套,中断嵌套时序比较复杂,考虑以下这种情况:进程A在运行,中断IRQx触发Intx运行,在处理时又被IRQy抢占触发Inty运行,Inty返回一个事件导致线程B运行,Intx返回一个事件导致线程C运行,如下图:


中断嵌套

中断接口

中断API
上一篇 下一篇

猜你喜欢

热点阅读