购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.3 内部服务层

内部服务层提供了服务程序,供PRTOS内核的其他组件使用,这些服务对分区来说是不可见的。内部服务层包含KLIBC、分区引导程序以及队列操作数据结构。

3.3.1 KLIBC

PRTOS内核使用KLIBC(内核组件可用)有以下两个原因:一是PRTOS内核程序无法调用处于分区模式下的标准C库;二是即使PRTOS可以使用标准C库中的程序,但是为了减少对外部库的依赖,并简化内核实现,也很有必要使用一个仅供自己调用的C库。

KLIBC包含两部分:一部分是与平台无关的代码;另一部分是出于效率考虑,使用平台相关代码。KLIBC的API列表如代码清单3-4所示。

代码清单3-4 KLIBC的API列表

//源码路径:core/klibc

01 void *memset(void *dst, prtos_s32_t s, prtos_size_t count)

02 void *memcpy(void *dst, const void *src, prtos_size_t count) // 平台相关

03 prtos_s32_t memcmp(const void *dst, const void *src, prtos_size_t count)

04 char *strcpy(char *dst, const char *src)

05 char *strncpy(char *dst, const char *src, prtos_size_t n)

06 char *strcat(char *s, const char* t)

07 char *strncat(char *s, const char *t, prtos_size_t n)

08 prtos_s32_t strcmp(const char *s, const char *t)

09 prtos_s32_t strncmp(const char *s1, const char *s2, prtos_size_t n)

10 prtos_size_t strlen(const char *s)

11 char *strrchr(const char *t, prtos_s32_t c)

12 char *strchr(const char *t, prtos_s32_t c)

13 char *strstr(const char *haystack, const char *needle)

14 void *memmove(void *dst, const void *src, prtos_size_t count)

15 unsigned long strtoul(const char *ptr, char **endptr, prtos_s32_t base)

16 long strtol(const char *nptr, char **endptr, prtos_s32_t base)

17 prtos_u64_t strtoull(const char *ptr, char **endptr, prtos_s32_t base)

18 prtos_s64_t strtoll(const char *nptr, char **endptr, prtos_s32_t base)

19 char *basename(char *path)

20 prtos_s32_t vprintf(const char *fmt, va_list args)

21 prtos_s32_t sprintf(char *s, char const *fmt, ...)

22 prtos_s32_t snprintf(char *s, prtos_s32_t n, const char *fmt, ...)

23 prtos_s32_t kprintf(const char *format, ...)

24 prtos_s32_t eprintf(const char *format, ...)


3.3.2 分区引导程序

PBL(Partition Boot Loader,分区引导程序)位于每个分区的内存区域中,用于PRTOS分区映像的加载。

PBL的主要职责如下。

1)加载分区操作系统或者裸机应用。PBL从存储介质中读取操作系统或者裸机应用映像,并将其加载到指定分区的内存中。

2)跳转到分区操作系统或者裸机应用入口。一旦操作系统或者裸机应用被加载到内存中,PBL就将控制权转移到分区中操作系统的启动代码入口或者裸机应用的启动代码入口,从而启动操作系统或者裸机应用。

PBL在vCPU0上运行。一旦PBL初始化并加载了操作系统或者裸机应用,控制权就会被传递给该操作系统或者裸机应用。该操作系统或者裸机应用将在其自己的vCPU上运行。

3.3.3 队列操作数据结构

PRTOS内核使用队列操作, 而队列操作并不专属于PRTOS的某个模块(如中断管理、内存管理等)。比如,如果需要维持一个foo数据结构的双链队列,常用的办法是在这个数据结构的类型定义中加入两个指针,以实现队列操作,如代码清单3-5所示。

代码清单3-5 加入两个指针实现队列操作

01 typedef struct foo

02 {

03 struct foo *prev;    //指向当前结构的前驱节点

04 struct foo *next;    //指向当前结构的后继节点

05 ...

06 } foo;


之后,为这种数据结构写一套用于各种队列操作的子程序。由于用来维持队列的两个指针的类型是固定的(都指向foo数据结构),因此这些子程序不能用于其他数据结构的队列操作。换言之,需要维持多少种数据结构的队列,就需要提供多少套队列操作子程序。这对使用队列较少的应用程序来说或许不是一个问题,但对频繁使用队列的PRTOS内核就成问题了。所以PRTOS内核中采用了一套通用的、可以用于各种不同数据结构的队列操作。PRTOS把指针prev和next从具体的“宿主”数据结构中抽象出来,使其成为一种数据结构。这种数据结构既可以“寄宿”在具体的宿主数据结构内部,成为该数据结构的一个“连接件”;也可以独立存在而成为—个队列的头。具体实现请参考PRTOS源码core/include/list.h。

例如,PRTOS的定时器struct ktimer的数据结构代码清单3-6所示。

代码清单3-6 PRTOS定时器的数据结构

//源码路径:core/include/ktimer.h

01 typedef struct ktimer {

02   struct dyn_list_node dyn_list_ptrs;

03   hw_time_t value;

04   hw_time_t interval;

05   prtos_u32_t flags;

06 #define KTIMER_ARMED (1 << 0)

07   void *action_args;

08   void (*action)(struct ktimer *, void *);

09 } ktimer_t;


如果要将一个ktimer_t结构的对象插入一个队列,可将其队列头dyn_list_ptrs作为链接件,调用dyn_list_insert_head链入一个队列,示例代码如代码清单3-7所示。

代码清单3-7 定时器对象初始化

//源码路径:core/kernel/ktimer.c

01 void init_ktimer(int cpu_id, ktimer_t *ktimer, void (*act)(ktimer_t *, void *), void *args, void *kthread) {

02   kthread_t *k = (kthread_t *)kthread;

03

04   memset((prtos_s8_t *)ktimer, 0, sizeof(ktimer_t));

05   ktimer->action_args = args;

06   ktimer->action = act;

07   if (dyn_list_insert_head((k) ? &k->ctrl.local_active_ktimers : &local_processor_

info[cpu_id].time.global_active_ktimers, &ktimer->dyn_list_ptrs)) {

08     cpu_ctxt_t ctxt;

09     get_cpu_ctxt(&ctxt);

10     system_panic(&ctxt, "[KTIMER] Error allocating ktimer");

11   }

12 }


在上述代码中,第07行表示队列操作通过dyn_list_ptrs实现。由于连接件在宿主结构的首部,所以连接件的地址就是宿主结构的地址,这样我们可以通过函数traverse_ktimer_queue()遍历这个队列来遍历宿主结构。traverse_ktimer_queue()函数的实现请参考源码core/kernel/ktimer.c。 9ial6rN+qzegaugO6bq9VLc6SarUACGkggO7vBpZDDJp7DuGgvvLD9pTLsVhsLuI

点击中间区域
呼出菜单
上一章
目录
下一章
×