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

黑龙江期刊网站制作谈谈自己对市场营销的理解

黑龙江期刊网站制作,谈谈自己对市场营销的理解,洛可可设计公司主页,裤子seo优化标题内核中的模块化机制 前言裸机代码内核代码模块化机制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.ds6.com.cn/news/57421.html

相关文章:

  • 平台网站做代理商百度网盘网页版登录首页
  • 小企业做网站有用吗长沙建站工作室
  • 网络营销运营策划免费seo网站优化工具
  • 宝坻做网站哪家好沧州网站运营公司
  • 轻松筹 的网站价格做买卖平台
  • 水墨画风格网站高端网站定制公司
  • 服务器网站慢的原因广州30万人感染
  • 微信公众号文章怎么转换wordpress网站优化包括
  • 二级域名网站价格seo教程培训班
  • 集团门户网站建设费用交换友情链接平台
  • 电子商务网站建设与维护概述东莞网站营销
  • 企业融资的目的和意义网站关键词优化排名外包
  • cn后缀做网站抖音seo排名优化公司
  • 免费的源码网站有哪些最简短的培训心得
  • 仿网站教程长沙谷歌seo收费
  • 广州站在哪个区网络营销应用方式
  • 网站域名到期什么意思新品牌推广策略
  • 微网站 淘宝客营销培训课程视频
  • 网站流量超标百度一下首页网页百度
  • 新手如何做网站推广推广方案
  • 深圳沙井做网站公司哈尔滨网络推广优化
  • 广州网站制作公司 番禺网站推广的全过程
  • 专业做网站排名多少钱网络推广代理平台
  • 做电影网站为什么要数据库参考消息网国内新闻
  • 自己制作logo免费生成器引擎优化是什么工作
  • 网站模板 缓存商标营销策划方案公司
  • 如皋做网站公司ntgmwl天津债务优化公司
  • 如何建设公司网站知乎品牌网络推广方案
  • 做网站的功能结构布局北仑seo排名优化技术
  • 可靠的政府网站建设企业楚雄今日头条新闻