鸿蒙OS的系统调用是如何实现的? | 解读鸿蒙源码

本文将首先带您回顾“系统调用”的概念以及它的作用,然后从经典的Hello World开始,逐行代码层层分析——鸿蒙OS的系统调用是如何实现的。

写在前面

9月10号 华为开发者大会(HDC)上,华为向广大开发者宣布了鸿蒙2.0系统开源,源码托管在国内源码托管平台“码云”上:https://openharmony.gitee.com/

我也第一时间从码云下载了鸿蒙系统的源代码,并进行了编译和分析。当晚回看了HDC上的关于鸿蒙OS 2.0的主题演讲,个人最为好奇的是——这次开源的liteos-a内核。因为它支持了带MMU(内存管理单元)的ARM Cortex-A设备;我们知道,在带有MMU的处理器上,可以实现虚拟内存,进而实现进程之间的隔离、内核态和用户态的隔离等等这些功能。

系统调用简介

引用一张官方文档中的图片,看看liteos-a内核在整个系统中的位置。

在这里插入图片描述

这次开源的鸿蒙系统中同时包含了两个内核,分别是liteos-a和liteos-m,其中的liteos-m和以前开源的LiteOS相当,而liteos-a是面向应用处理器的操作系统内核,提供了更为丰富的内核功能。此前已经开源的LiteOS,只是一个实时操作系统(RTOS),它主要面向的是内存和闪存配置都比较低的微控制器。

我们先来简单回顾一下操作系统课程的一个知识点——系统调用,以及为什么会有系统调用?它的作用是什么?如果你对于这两个问题以及了然于心,可以直接跳过本段,看后面的源码分析部分。

在微控制器这样的系统资源较少的硬件系统(比如STM32、MSP430、AVR、8051)上,通常直接裸跑程序(也就是不使用任何操作系统),或者使用像FreeRTOS、Zephyr这一类的实时操作系统(RTOS)。这些实时操作系统中,应用程序和内核程序直接运行在同一个物理内存空间(因为这些设备一般没有MMU)上。而RTOS只提供了线程(或者叫任务),线程间同步、互斥等基础设施;应用程序可以直接调用内核函数(用户程序和内核程序只是逻辑上的划分,本质上并没有太大不同);一旦有一个线程发生异常,整个系统就会重启。

而在ARM Cortex-A、x86、x86-64这样的系统资源丰富的硬件系统上,SoC或CPU芯片内部一般集成了MMU,而且CPU有特权级别状态(状态寄存器的某些位)。基于特权级别状态,可以实现部分硬件相关的操作只能在内核态进行,例如访问外设等,用户态应用程序不能访问硬件设备。在这样的系统上,系统调用是用户态应用程序调用内核功能的请求入口。通俗的说,系统调用就是在有内核态和用户态隔离的操作系统上,用户态进程访问内核态资源的一种方式。

从Hello World开始

接下来,我们一起从鸿蒙系统源码分析它在liteos-a内核上是如何实现系统调用的。鸿蒙OS使用了musl libc,应用程序和系统服务都通过musl libc封装的系统调用API接口访问内核相关功能。

下面,我们就从经典的helloworld分析整个系统调用的流程。鸿蒙系统目前官方支持了三个芯片平台,分别是Hi3516DV300(双核ARM Cortex A-7 @ 900M Hz),Hi3518EV300(单核ARM Cortex A-7 @ 900MHz 内置64MB DDR2内存)和Hi3861V100(单核RISC-V @160M Hz 内置 SRAM 和 Flash)。其中Hi3516和Hi3518是带有Cortex A7内核的芯片,鸿蒙系统在这两个平台使用的内核自然是liteos-a。根据官方指导文档,我们知道这两个平台的第一个应用程序示例都是helloworld,源码路径为:applications/sample/camera/app/src/helloworld.c,除去头部注释,代码内容为:

#include <stdio.h>#include "los_sample.h"int main(int argc, char **argv){
   
    printf("\n************************************************\n");
    printf("\n\t\tHello OHOS!\n");
    printf("\n************************************************\n\n");

    LOS_Sample(g_num);

    return 0;
}

musl libc的printf函数实现分析

文件路径:third_party/musl/src/stdio/printf.c:

int printf(const char *restrict fmt, ...){
   	int ret;
	va_list ap;
	va_start(ap, fmt);
	ret = vfprintf(stdout, fmt, ap);
	va_end(ap);	return ret;
}

我们看到了,这里使用标准库的stdout作为第一个参数调用了vfprintf,我们继续向下分析third_party/musl/src/stdio/vfprintf.c文件:

int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
{
   // 删减若干和参数 f 无关的代码行
	FLOCK(f);
	olderr = f->flags & F_ERR;	if (f->mode < 1) f->flags &= ~F_ERR;	if (!f->buf_size) {
   
		saved_buf = f->buf;
		f->buf = internal_buf;
		f->buf_size = sizeof internal_buf;
		f->wpos = f->wbase = f->wend = 0;
	}	if (!f->wend && __towrite(f)) ret = -1;	else ret = printf_core(f, fmt, &ap2, nl_arg, nl_type);	if (saved_buf) {
   
		f->write(f, 0, 0);		if (!f->wpos) ret = -1;
		f->buf = saved_buf;
		f->buf_size = 0;
		f->wpos = f->wbase = f->wend = 0;
	}	if (f->flags & F_ERR) ret = -1;
	f->flags |= olderr;
	FUNLOCK(f);
	va_end(ap2);	return ret;
}

这里,我们继续关注三处带有参数f的调用:__towrite(f),printf_core(f, fmt, &ap2, nl_arg, nl_type),f->write(f, 0, 0);

其中,__towrite的实现位于third_party/musl/src/stdio/__towrite.c(可见和系统调用无关):

int __towrite(FILE *f)
{
   
	f->mode |= f->mode-1;	if (f->flags & F_NOWR) {
   
		f->flags |= F_ERR;		return EOF;
	}	/* Clear read buffer (easier than summoning nasal demons) */
	f->rpos = f->rend = 0;	/* Activate write through the buffer. */
	f->wpos = f->wbase = f->buf;
	f->wend = f->buf + f->buf_size;	return 0;
}

从内容上看,__towrite函数的作用是更新文件结构FILE的wpos、wbase、wend成员,以指向待写入实际文件的内存缓冲区域,同时将rpos、rend值为零。

printf_core的实现也位于src/stdio/vfprintf.c文件:

static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type){
   
    // 删除了变量定义部分
	for (;;) {
   		/* This error is only specified for snprintf, but since it's
		 * unspecified for other forms, do the same. Stop immediately
		 * on overflow; otherwise %n could produce wrong results. */
		if (l > INT_MAX - cnt) goto overflow;		/* Update output count, end loop when fmt is exhausted */
		cnt += l;		if (!*s) break;		/* Handle literal text and %% format specifiers */
		for (a=s; *s && *s!='%'; s++);		for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2);		if (z-a > INT_MAX-cnt) goto overflow;
		l = z-a;		if (f) out(f, a, l);		if (l) continue;		if (isdigit(s[1]) && s[2]=='$') {
   
			l10n=1;
			argpos = s[1]-'0';
			s+=3;
		} else {
   
			argpos = -1;
			s++;
		}		/* Read modifier flags */
		for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++)
			fl |= 1U<<*s-' ';		/* Read field width */
		if (*s=='*') {
   			if (isdigit(s[1]) && s[2]=='$') {
   
				l10n=1;
				nl_type[s[1]-'0'] = INT;
				w = nl_arg[s[1]-'0'].i;
				s+=3;
			} else if (!l10n) {
   
				w = f ? va_arg(*ap, int) : 0;
				s++;
			} else goto inval;			if (w<0) fl|=LEFT_ADJ, w=-w;
		} else if ((w=getint(&s))<0) goto overflow;		/* Read precision */
		if (*s=='.' && s[1]=='*') {
   			if (isdigit(s[2]) && s[3]=='$') {
   
				nl_type[s[2]-'0'] = INT;
				p = nl_arg[s[2]-'0'].i;
				s+=4;
			} else if (!l10n) {
   
				p = f ? va_arg(*ap, int) : 0;
				s+=2;
			} else goto inval;
			xp = (p>=0);
		} else if (*s=='.') {
   
			s++;
			p = getint
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值