当前位置: 首页 > news >正文

服装品牌建设网站的目的华为云建设网站

服装品牌建设网站的目的,华为云建设网站,成都企业展厅设计公司,装修公司起名内核中的模块化机制 前言裸机代码内核代码模块化机制C语言热身module_init深入宏封装布局段属性定义段机制-在内核执行过程部分实例 衍生 前言 本文从module_init展开讲述,Linux中的段机制,以及它带来的好处。在写驱动代码中最常用的就是module_init以及…

内核中的模块化机制

  • 前言
  • 裸机代码
  • 内核代码
  • 模块化机制
    • C语言热身
    • module_init深入宏封装
    • 布局
    • 段属性定义
    • 段机制-在内核执行过程部分
    • 实例
  • 衍生

前言

本文从module_init展开讲述,Linux中的段机制,以及它带来的好处。在写驱动代码中最常用的就是module_init以及module_exit函数。在Linux系统启动的时候就会自己去执行用module_init定义的函数。
那么为什么Linux内核启动的过程会自动执行,它是如何实现的呢?这个是本文的重点。

裸机代码

在讨论内核驱动代码之前,先看下之前我们写单片机的代码中如何实现一个驱动的。

int main(void)
{ delay_init();   //初始化延时函数LED_Init();     //初始化LED端口I2C_Init();		//i2c初始化LCD_Init();		//LCD初始化while(1){GPIO_ResetBits(GPIOB,GPIO_Pin_5);  //LED0对应引脚GPIOB.5拉低,亮  等同LED0=0;GPIO_SetBits(GPIOE,GPIO_Pin_5);   //LED1对应引脚GPIOE.5拉高,灭 等同LED1=1;delay_ms(300);  //延时300msGPIO_SetBits(GPIOB,GPIO_Pin_5);    //LED0对应引脚GPIOB.5拉高,灭  等同LED0=1;GPIO_ResetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉低,亮 等同LED1=0;delay_ms(300);                     //延时300ms}
} 

由此可见每当我们需要添加一个外设比如ADC,DMA都需要在main函数中执行相对应的初始化代码。
然后在其他地方实现他的功能代码,在代码量小的时候这样做没什么,也能控制,但在内核代码中这样实现就显得不那么明智。

内核代码

我们知道Linux很庞大,驱动-只是它启动过程的一小部分,还有很多如内存管理、调度、算法等等。那么每次需要添加一个设备的驱动就要在启动的main函数中加初始化,就很不灵活。同时内核系统庞大,多人协同不方便,修改内核启动代码容易出错。所以在内核中利用宏来处理我们所定义的初始化代码,然后在Linux内核启动过程中统一 一个地方来调用我们定义的初始化代码,做到灵活,统一可控的代码结构。

模块化机制

Linux中的模块化机制实际上利用宏,把我们的初始化代码统一声明,然后利用编译手段将初始化代码的函数地址统一收集归类,打包进kernel中,在系统启动的时候去特定的地址取出函数地址,并转化为函数指针对其进行调用,达到初始化函数的调用。

C语言热身

在讲段机制之前,先大致了解下基本原理。函数指针

int Func(void);   /*声明一个函数*/
int main(void)
{Func();//我们可以这样调用一个函数
}
--------------------------------------
int (*p) (void);  /*定义一个函数指针*/
p = Func;          /*将Func函数的首地址赋给指针变量p*/
p();//也可以通过函数指针来调用一个函数typedef int (*initcall_t)(void); //利用typedef定义一个函数指针类型 ,Linux就是这样操作的
initcall_t q = Func;
q();

module_init深入宏封装

先看module_init的定义-》/include/linux/module.h
__initcall的定义在-》\kernel\kernel\include\linux\init.h

#define module_init(x)	__initcall(x);
---------------------------------------
特定的宏表示了不一样的优先级,常见的module_init优先级在6
#define __initcall(fn) device_initcall(fn)
#define core_initcall(fn)		__define_initcall(fn, 1)
#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
...
#define arch_initcall(fn)		__define_initcall(fn, 3)
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
#define fs_initcall(fn)			__define_initcall(fn, 5)
#define device_initcall(fn)		__define_initcall(fn, 6)
....
#define late_initcall_sync(fn)		__define_initcall(fn, 7s)
-----------------------------------------
#define __define_initcall(fn, id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" #id ".init"))) = fn;
------------------------------------typedef int (*initcall_t)(void); //定义函数指针类型typedef void (*exitcall_t)(void);
  1. 可以看到各式各样的宏定义都调用了__define_initcall,后面带有数字,这个数组表达的是有限制,即越小的排在越前面,Linux内核执行的时候也越早执行。
  2. __define_initcall也很好理解,定义了一个变量 而这个变量的值就是我们声明初始化函数的地址,而他带有段属性,属于某个段,在这里宏用到极致,随着段id也就是优先级的变化,段属性也在变。
  3. 而这个段属性_section__有什么用? 这个就会在Linux镜像布局中起作用,意味着我把这个定义的变量 放在哪。标红的变量是同一个变量。

布局

这是我网上摘来的图,里面详细描述了各个段的布局以作参考。
在这里插入图片描述

段属性定义

上面描述的宏只是定义了你的初始化函数指针变量的段属性。而真正确定变量布局是在编译过程,文件在-》arch\arm64\kernel\vmlinux.lds 32位的在arm目录下,注意这个文件是编译后才会生成的。

.init.data : {
...
__dtb_start = .; *(.dtb.init.rodata) __dtb_end = .; 
__setup_start = .; *(.init.setup) __setup_end = .;__initcall_start = .; KEEP(*(.initcallearly.init)) __initcall0_start = .;KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init)) __initcall1_start = .; KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init)) __initcall2_start = .; KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init)) __initcall3_start = .; KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init)) __initcall4_start = .; KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init)) __initcall5_start = .; KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init))__initcallrootfs_start = .; KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init))__initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init))__initcall7_start = .; KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init)) __initcall_end = .;....}

段机制-在内核执行过程部分

在上述的lds中得到了各个段的其实地址。XXX_start。
.\kernel\init\main.c

int __init_or_module do_one_initcall(initcall_t fn)
{
....
ret = fn();
}
static void __init do_initcall_level(int level) //执行一个level的initcall
{initcall_t *fn;...for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)do_one_initcall(*fn);
}
static initcall_t *initcall_levels[] __initdata = {__initcall0_start,//这里start的值就是lds文件中排列的那些编译链接后就得到了确定的地址。__initcall1_start,__initcall2_start,__initcall3_start,__initcall4_start,__initcall5_start,__initcall6_start,__initcall7_start,__initcall_end,
};
static void __init do_initcalls(void)
{int level;for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)do_initcall_level(level);
}
do_initcalls《-do_basic_setup(void)-kernel_init_freeable(void)-kernel_init(void *unused)
static noinline void __ref rest_init(void)
{
kernel_thread(kernel_init, NULL, CLONE_FS);//init进程中执行的。
}
rest_init《-void __init start_kernel(void)
  1. start_kernel中代码很庞大,倒着梳理会比较好。即由内往外剥离梳理比较合适。
  2. start_kernel类似于裸机中的main函数,在启动汇编文件中执行调用。各大平台各种实现跳转。
    ./arch/arm/kernel/head-common.S:104: b start_kernel
    ./arch/m68k/68000/head.S:238: jsr start_kernel /* start Linux kernel /
    ./arch/m68k/coldfire/head.S:293: jsr start_kernel /
    start Linux kernel */

实例

举个例子,除了常用的module_init进行驱动的初始化,还有如下串口的初始化
在这里插入图片描述
文件系统的初始化:文件系统的初始化

衍生

当当uboot的bootargs匹配上时调用解析函数mtdpart_setup
在这里插入图片描述
查看__setup的定义,可以看到这个时候定义的是结构体变量,
在这里插入图片描述

static bool __init obsolete_checksetup(char *line)
{const struct obs_kernel_param *p;bool had_early_param = false;p = __setup_start;do {int n = strlen(p->str);if (parameqn(line, p->str, n)) {if (p->early) {/* Already done in parse_early_param?* (Needs exact match on param part).* Keep iterating, as we can have early* params and __setups of same names 8( */if (line[n] == '\0' || line[n] == '=')had_early_param = true;} else if (!p->setup_func) {pr_warn("Parameter %s is obsolete, ignored\n",p->str);return true;} else if (p->setup_func(line + n))return true;}p++;} while (p < __setup_end);return had_early_param;
}
  1. 调用关系obsolete_checksetup 《-unknown_bootoption《-start_kernel
    意思也就是说假如bootargs参数中有mtdparts字符是,就会调用mtdpart_setup函数。
http://www.yayakq.cn/news/210413/

相关文章:

  • 网站开发合同付款比例网页制作专业服务
  • 繁昌网站建设北京响应式网站制作公司
  • 汽车专业科技网站建设作文网有哪些
  • 网站平台开发报价表怎么做网站文章伪原创如何做
  • 网站网址和域名如何迅速k掉网站
  • 网站建设人员组织html网页源代码查看
  • 下载网站app移动商城app下载
  • 邢台网红餐厅百度seo关键词排名技术
  • 网站内容建设的布局我的家乡网页设计模板图片
  • 企业网站模板哪里好如何建立公众号的步骤
  • 网站建设广州公司哪家好电子商务网站建设收益举例
  • 发不了软文的网站怎么做关键词优化wordpress 云
  • 备案时网站建设方案书中国建设网站下载安装
  • 十堰网站建设电话兰州seo网站建设
  • 个人网站成功案例建站技术
  • 企业网站 php 下载建网站报价明细表
  • 做百度移动网站优化排wordpress如何用API采集
  • 做别人一摸一样的网站犯法吗北京上地网站建设
  • 建设网站公司怎么收费html后台网站模板
  • 极速网站建设多少钱怎么把网站做的小程序
  • 成都做网站的公司有侧边栏的网站
  • 不会写程序如何做网站软件开发工具的公司有哪些
  • 这样制作公司网站网页制作 培训
  • 做ppt接单的网站虚拟币网站开发制作
  • 柳州正规网站制作白山网站制作
  • 手机网站小程序网络营销案例分析范文
  • asp网站如何打开网站顶部广告素材
  • 广州网站建设小程序godday网站建设
  • 网站开发费用计入科目企业建设网站好处
  • 重庆网站建设外贸实力app开发公司