openharmony标准系统移植之init启动流程分析

接上篇移植openharmony标准系统后,系统进入终端后,发现执行指令特别卡顿,太影响调试了。目前还不知道是什么问题导致的,不知道是不是cpu性能不够,但是感觉不太像是这个问题,卡顿如下图。基本一个操作需要卡半天。
在这里插入图片描述
为了解决下这个问题,也为了更熟悉openharmony代码启动流程。现在追踪下启动过程。记录如下。
首先可以知道的是openharmony系统启动后,执行的是init进程。可以查看源码目录下base/startup/init_lite/services/BUILD.gn文件,首先是不管小型还是标准系统都会参与编译的文件。在这里插入图片描述
然后根据系统类型会选择编译不同的文件,我们是标准系统,那么我们具体编译的文件如下图所示。
在这里插入图片描述
很明显,我们找到了init的入口了,即为源码目录下的base/startup/init_lite/services/init/main.c文件,文件内容如下,

static const pid_t INIT_PROCESS_PID = 1;

int main(int argc, char * const argv[])
{
   
    int isSecondStage = 0;
    // Number of command line parameters is 2
    if (argc == 2 && (strcmp(argv[1], "--second-stage") == 0)) {
    //我们一般启动都是不带参数
        isSecondStage = 1;
    }
    if (getpid() != INIT_PROCESS_PID) {
    //正常情况下init进程都为1
        INIT_LOGE("Process id error %d!", getpid());
        return 0;
    }

    if (isSecondStage == 0) {
    //前面说了,我们是没有参数,此值肯定为0,
        SystemPrepare();
    } else {
   
        LogInit();
    }
    SystemInit();
    SystemExecuteRcs();
    SystemConfig();
    SystemRun();
    return 0;
}

所以具体执行的函数为如下顺序:

  1. SystemPrepare();
  2. SystemInit();
  3. SystemExecuteRcs();
  4. SystemConfig();
  5. SystemRun();
    那么我们就需要展开查看这五个函数的执行过程了。
    1.1 首先执行的是SystemPrepare()函数,我们查找的话,是有如下图地方的地方存在改函数。
    在这里插入图片描述
    而我们是标准系统,那么我们执行的函数肯定就是base/startup/init_lite/services/init/standard/init.c文件中的SystemPrepare()函数了。这边我们可以添加打印函数进行验证。
void SystemPrepare(void)
{
   
    INIT_LOGI("base/startup/init_lite/services/init/standard/init.c"); //添加的打印测试
    MountBasicFs();
    LogInit();
    // Make sure init log always output to /dev/kmsg.
    EnableDevKmsg();
    CreateDeviceNode();
    // Only ohos normal system support
    // two stages of init.
    // If we are in updater mode, only one stage of init,
    INIT_LOGI("DISABLE_INIT_TWO_STAGES not defined");
    if (InUpdaterMode() == 0) {
   
        StartInitSecondStage();
    }
}
void MountBasicFs(void)
{
   
    if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755") != 0) {
   
        INIT_LOGE("Mount tmpfs failed. %s", strerror(errno));
    }
    if (mount("tmpfs", "/mnt", "tmpfs", MS_NOSUID, "mode=0755") != 0) {
   
        INIT_LOGE("Mount tmpfs failed. %s", strerror(errno));
    }
    if (mkdir("/dev/pts", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
   
        INIT_LOGE("mkdir /dev/pts failed. %s", strerror(errno));
    }
    if (mount("devpts", "/dev/pts", "devpts", 0, NULL) != 0) {
   
        INIT_LOGE("Mount devpts failed. %s", strerror(errno));
    }
    if (mount("proc", "/proc", "proc", 0, "hidepid=2") != 0) {
   
        INIT_LOGE("Mount procfs failed. %s", strerror(errno));
    }
    if (mount("sysfs", "/sys", "sysfs", 0, NULL) != 0) {
   
        INIT_LOGE("Mount sysfs failed. %s", strerror(errno));
    }
    if (mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL) != 0) {
   
        INIT_LOGE("Mount selinuxfs failed. %s", strerror(errno));
    }
}
其中mount函数说明如下:
int mount( const char* source, const char* target, const char* filesystemtype, unsigned long mountflags, const void * data);
source : 待挂载的文件系统,通常是一个设备名
target : 挂载点
filesystemtype : 文件系统的类型,例如:"ext2""ext3""msdos""proc""nfs4""iso9660"
mountflags : 指定文件系统的读写访问标志.
值通常如下 : 
MS_BIND : 执行bind挂载,使文件或者子目录树在文件系统内的另一个点上可视。
MS_DIRSYNC : 同步目录的更新。
MS_MANDLOCK : 允许在文件上执行强制锁。
MS_MOVE : 移动子目录树。
MS_NOATIME : 不要更新文件上的访问时间。
MS_NODEV : 不允许访问设备文件。
MS_NODIRATIME : 不允许更新目录上的访问时间。
MS_NOEXEC : 不允许在挂上的文件系统上执行程序。
MS_NOSUID : 执行程序时,不遵照set-user-ID 和 set-group-ID位。
MS_RDONLY : 指定文件系统为只读。
MS_REMOUNT : 重新加载文件系统。允许改变现存文件系统的mountflag和数据而无需先卸载再挂上文件系统
MS_SYNCHRONOUS : 同步文件的更新。
MNT_FORCE : 强制卸载,即使文件系统处于忙状态。
MNT_EXPIRE : 将挂载点标志为过时。
data : 文件系统特有的参数。

函数成功执行时返回0。失败返回-1,errno被设为以下的某个值
EACCES : 权能不足,可能原因是:路径的一部分不可搜索或者挂载只读的文件系统时,没有指定 MS_RDONLY 标志。
EAGAIN : 成功地将不处于忙状态的文件系统标志为过时。
EBUSY : 1.源文件系统已被挂上或者不可以以只读的方式重新挂载,因为它还拥有以写方式打开的文件。2.目标处于忙状态。
EFAULT : 内存空间访问出错。
EINVAL : 操作无效,可能是源文件系统超级块无效。
ELOOP : 路径解析的过程中存在太多的符号连接。
EMFILE : 无需块设备要求的情况下,无用设备表已满。
ENAMETOOLONG : 路径名超出可允许的长度。
ENODEV : 内核不支持某中文件系统。
ENOENT : 路径名部分内容表示的目录不存在。
ENOMEM : 核心内存不足。
ENOTBLK : source不是块设备。
ENOTDIR : 路径名的部分内容不是目录。
EPERM : 调用者权能不足。
ENXIO : 块主设备号超出所允许的范围。

然后执行LogInit函数,依然在base/startup/init_lite/services/init/standard/init.c文件中。

void LogInit(void)
{
   
    CloseStdio();
    int ret = mknod("/dev/kmsg", S_IFCHR | S_IWUSR | S_IRUSR,
        makedev(MEM_MAJOR, DEV_KMSG_MINOR));
    if (ret == 0) {
   
        OpenLogDevice(); // base/startup/init_lite/services/log/init_log.c文件中,做了打开文件,获取设备描述符的操作。
    }
}

然后是函数CreateDeviceNode(),在文件base/startup/init_lite/services/init/standard/device.c中。

void CreateDeviceNode(void)
{
   
    if (mknod("/dev/null", S_IFCHR | DEFAULT_RW_MODE, makedev(MEM_MAJOR, DEV_NULL_MINOR)
  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值