1 前言

大家都知道在windows操作系统中,可以运行一个个程序,这些程序大都是exe可执行文件,它是一个编译好的二进制文件,在windows下,在一个程序中还可以使用dll动态链接库dll文件,它同样也是编译好的二进制文件。相对的在Linux下也有可执行文件可动态链库.so文件,那么在rt-thread中是否也存在一种这样的文件,可以在系统的运行过程中,由用户启动并执行它,答案是有。在rt-thead中这样的可执行文件或是动态链表库文件,这种文件是elf格式的文件。本文将从内核的角度来看rt-thead是如何将这样一个elf格式的文件装载到内存并执行它。

2 模块控制块

在/include/rtdef.h文件中有模块控制块的定义,如下:

struct rt_module
{struct rt_object             parent;              //内核对象控制块rt_uint8_t                  *module_space;        //模块空间,保存装载elf文件后的内存空间void                        *module_entry;          //此模块对应的线程入口rt_thread_t                  module_thread;       //此模块对应的线程rt_uint32_t                  stack_size;            //此模块对应的线程栈rt_uint32_t                  thread_priority;    //此模块对应的线程优先级#ifdef RT_USING_SLAB/* module memory allocator */void                        *mem_list;              //此模块对应的内存堆链表void                        *page_array;            //内存页rt_uint32_t                  page_cnt;              //内存页数量
#endifrt_uint32_t                  nsym;                  //此模块包含的符号个数struct rt_module_symtab     *symtab;                //此模块包含的符号表rt_uint32_t                  nref;                  //此模块被关联的次数/* object in this module, module object is the last basic object type */struct rt_object_information module_object[RT_Object_Class_Unknown];//此模块包含的内核对象容器
};
typedef struct rt_module *rt_module_t;

一般来说,rt-thread在装一个elf格式文件时,会自动为它创建一个线程来执行它,这个线程就是指上面的模块对应的线程。

下现来看看 rt-thread是如何装一个so文件(elf格式),并执行它的。

3 装载so文件

在/components/libdl/dlfcn.h头文件中存在dlopen函数声明:

void *dlopen (const char *filename, int flag);

就是这个dlopen函数负责打开so文件并装载进内存(保存到rt_moudule结构体中),那么下面来分析一下这个dlopen函数:

void* dlopen(const char *filename, int flags)
{rt_module_t module;char *fullpath;const char*def_path = MODULE_ROOT_DIR;/* check parameters */RT_ASSERT(filename != RT_NULL);//处理相对路径if (filename[0] != '/') /* it's a absolute path, use it directly */{fullpath = rt_malloc(strlen(def_path) + strlen(filename) + 2);/* join path and file name */rt_snprintf(fullpath, strlen(def_path) + strlen(filename) + 2, "%s/%s", def_path, filename);}/* find in module list */module = rt_module_find(fullpath);//在内核对象容器中尝试查找此模块if(module != RT_NULL) module->nref++;//如果找到,那么将它的被关联次数加1else module = rt_module_open(fullpath);//打开模块rt_free(fullpath);return (void*)module;
}

上面代码只是处理了一下路径问题,最终还是调用rt_module_open来处理,从源码中可知,其实形参flags是没有用到的.rt_module_open函数如下:

rt_module_t rt_module_open(const char *path)
{int fd, length;struct rt_module *module;struct stat s;char *buffer, *offset_ptr;char *name;RT_DEBUG_NOT_IN_INTERRUPT;/* check parameters */RT_ASSERT(path != RT_NULL);//读取输入的文件信息if (stat(path, &s) !=0){rt_kprintf("Module: access %s failed\n", path);return RT_NULL;}//以下代码是将输入的文件打开并读入内存缓冲区buffer = (char *)rt_malloc(s.st_size);if (buffer == RT_NULL){rt_kprintf("Module: out of memory\n");return RT_NULL;}offset_ptr = buffer;fd = open(path, O_RDONLY, 0);if (fd < 0){rt_kprintf("Module: open %s failed\n", path);rt_free(buffer);return RT_NULL;}do{length = read(fd, offset_ptr, 4096);if (length > 0){offset_ptr += length;}}while (length > 0);/* close fd */close(fd);if ((rt_uint32_t)offset_ptr - (rt_uint32_t)buffer != s.st_size){rt_kprintf("Module: read file failed\n");rt_free(buffer);return RT_NULL;}//获取文件名name   = _module_name(path);//将保存到内存中的so文件内容解释并执行module = rt_module_load(name, (void *)buffer);rt_free(buffer);rt_free(name);return module;
}

从以上代码可以,到这里还只是将so文件读入内存,真正解析so文件(elf格式)并执行的是在rt_module_load函数,其定义如下代码:

rt_module_t rt_module_load(const char *name, void *module_ptr)
{rt_module_t module;RT_DEBUG_NOT_IN_INTERRUPT;RT_DEBUG_LOG(RT_DEBUG_MODULE, ("rt_module_load: %s ,", name));/* check ELF header */if (rt_memcmp(elf_module->e_ident, RTMMAG, SELFMAG) != 0 &&//判断elf格式文件的头部信息的文件标志是否为"\177RTM"或"\177ELF",否则将返回空,也就是说,此函数只能处理这两种文件标识的elf文件rt_memcmp(elf_module->e_ident, ELFMAG, SELFMAG) != 0){rt_kprintf("Module: magic error\n");return RT_NULL;}/* check ELF class */if (elf_module->e_ident[EI_CLASS] != ELFCLASS32)//检查此elf格式文件是否为32位机器码{rt_kprintf("Module: ELF class error\n");return RT_NULL;}//判断elf文件的类型,这里处理可重定位文件和共享目标文件if (elf_module->e_type == ET_REL){module = _load_relocated_object(name, module_ptr);//装载可重定位文件}else if (elf_module->e_type == ET_DYN){module = _load_shared_object(name, module_ptr);//装载共享目标文件}else{rt_kprintf("Module: unsupported elf type\n");return RT_NULL;}if (module == RT_NULL)return RT_NULL;/* init module object container */rt_module_init_object_container(module);//在模块内部的内核对象容器/* increase module reference count */module->nref ++;//模块的被索引次数加1//如果模块的入口地址存在if (elf_module->e_entry != 0){rt_uint32_t *stack_size;rt_uint8_t  *priority;#ifdef RT_USING_SLAB/* init module memory allocator */module->mem_list = RT_NULL;/* create page array */module->page_array = (void *)rt_malloc(PAGE_COUNT_MAX * sizeof(struct rt_page_info));//为模块创建内存页module->page_cnt = 0;
#endif/* get the main thread stack size */module->stack_size = 2048;//模块的线程栈设为2Kmodule->thread_priority = RT_THREAD_PRIORITY_MAX - 2;//模块的线程优先级设为最大优先级-2/* create module thread *///创建线程,将模块入口地址设置为此线程的入口函数module->module_thread =rt_thread_create(name,(void(*)(void *))module->module_entry,RT_NULL,module->stack_size,module->thread_priority,10);RT_DEBUG_LOG(RT_DEBUG_MODULE, ("thread entry 0x%x\n",module->module_entry));/* set module id */module->module_thread->module_id = (void *)module;//设置模块线程的模块ID为本模块module->parent.flag = RT_MODULE_FLAG_WITHENTRY;/* startup module thread */rt_thread_startup(module->module_thread);//启动模块线程}else//如果模块无入口地址{/* without entry point */module->parent.flag |= RT_MODULE_FLAG_WITHOUTENTRY;//设置标志为无入口地址}#ifdef RT_USING_HOOKif (rt_module_load_hook != RT_NULL){rt_module_load_hook(module);//使用勾子函数}
#endifreturn module;
}

elf文件是一种标准的二进制文件格式,它有好几种类型,除了上面代码是提供到可重定位文件(ET_REL)和共享目标文件(ET_DYN)之外,还存在可执行文件(ET_EXEC),当然还存在一些其它的类型,这里就不具体说明.当前rt-thread的最新版本是V1.1.0,从源码可知,当前版本只支持可重定位文件和共享目标文件这两种文件.

上述代码大致流程如下,首先从传入的elf文件内存获取elf头部信息,判断一些信息是否满足条件,之后判断elf文件类型,是否为可重定位文件或共享目标文件,如果是,则分别用不用的代码解析它,并将它装进rt_module_t对应的内存结构体中,然后创建一个线程并启动它.就这样,一个so的执行过程就完了,它是通过为一个模块创建一个线程来执行的.

再次回到模块控制块结构定义中,这里特别说明一个,模块控制块成员module_space保存了elf文件中表示程序的内容,而module_entry指向modul_space中的入口.

有关elf文件格式的具体说明请参考:http://blog.csdn.net/flydream0/article/details/8719036

或参考:http://en.wikipedia.org/wiki/Elf_format

这里就不做具体说明了,另_load_relocated_object函数(处理可重定位文件)和函数_load_shared_object(处理共享目标文件),这两个函数由于与elf文件格式密切相关,不建议在没有深刻理解elf文件格式之前去看这两个函数的实现,这里暂且将理解为分别解析可重定位文件和共享目标文件即可.

因此这里先建议了解elf文件格式再进一步去看这两个函数的实现,后续我将再写博文来讨论,敬请关注!

OK,先这样!

rt-thread的moudle源码分析相关推荐

  1. Thread源码分析之join方法

    2019独角兽企业重金招聘Python工程师标准>>> join方法示例1 源码 import java.util.concurrent.TimeUnit;public class ...

  2. 02.并发编程(2)Thread类源码分析

    概述 在说线程之前先说下进程,进程和线程都是一个时间段的描述,是CPU工作时间段的描述. 进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位.每一 ...

  3. Thread源码分析-java8

    1.Thread特性分析 守护线程Daemon 定性:支持性线程,主要用于程序中后台调度以及支持性工作. 当JVM中不存在Daemon线程时,JVM将会退出. 将一个线程设定为Daemon的方法: 调 ...

  4. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  5. 【转】Spark源码分析之-scheduler模块

    原文地址:http://jerryshao.me/architecture/2013/04/21/Spark%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8B- ...

  6. 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

    前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...

  7. Java高并发程序设计学习笔记(五):JDK并发包(各种同步控制工具的使用、并发容器及典型源码分析(Hashmap等))...

    转自:https://blog.csdn.net/dataiyangu/article/details/86491786#2__696 1. 各种同步控制工具的使用 1.1. ReentrantLoc ...

  8. 鸿蒙轻内核源码分析:MMU协处理器

    摘要:本系列首先了解下ARM CP15协处理器的知识,接着介绍下协处理器相关的汇编指令,最后分析下MMU相关汇编代码. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列六 MMU协处理器> ...

  9. Anbox源码分析(四)——Anbox渲染原理(源码分析)

    Anbox源码分析(四) 上篇文章我们从源码分析了一下Anbox是怎样一步步的准备了OpenGL ES的渲染环境的,这篇文章,我们继续分析Android的渲染指令是如何到达宿主机进行渲染的. 宿主机端 ...

最新文章

  1. nodejs linux 32位下载安装,Linux32位怎么装nodejs?
  2. Nginx教程系列五:实现负载均衡配置
  3. BZOJ 4849 [NEERC2016] Mole Tunnels (模拟费用流)
  4. python入门系列:深入Python的set和dict
  5. Running /usr/bin/wineserver -w. This will hang until all wine processes XXXX terminate
  6. Jython 安装使用
  7. linux 关闭web服务器端口,linux(solaris)下如何关掉端口及服务
  8. ASP.NET Core 企业级开发架构简介及框架汇总
  9. 论文浅尝 - ISWC2020 | KnowlyBERT: 知识图谱结合语言模型补全图谱查询
  10. Object C语法快速入门
  11. LeNet-5实战minist——搭建卷积网络模型
  12. 乌镇互联网大会 | 王恩东院士谈人工智能:计算力就是生产力
  13. MyEclipse:新导入一个项目时中文乱码
  14. NOIP2016提高A组 B题 【HDU3072】【JZOJ4686】通讯
  15. 疯狂python讲义这本书怎么样_疯狂Python讲义(读书笔记)
  16. Ortholab has been moved to Google Code
  17. 一文读懂:云上用户如何灵活应用定制化网络服务
  18. Java删除文件和文件夹的方式
  19. 电容或电感的电压_纯电阻、纯电感和纯电容电路
  20. 模2除法(计算CRC校验码)

热门文章

  1. node安装步骤以及环境配置
  2. 树莓派想使用 aptitude 解决依赖包问题,但是使用 apt-get 安装 aptitude 时出现依赖包错误怎么办?
  3. 【quartz】quartz定时任务service注入失败
  4. Premiere Pro快捷键大全
  5. Linux errno详解
  6. Oracle数据类型介绍
  7. 将子网掩码取反_子网掩码
  8. Java+MySQL 基于ssm的老年人健康饮食管理系统#毕业设计
  9. sublime text3安装emmet插件
  10. 等价类划分法的步骤和示例