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

医院加强网站建设综合社区网站开发费用

医院加强网站建设,综合社区网站开发费用,课题网站建设培训简讯,关键词优化是什么如何管理驱动模块 由于Linux驱动模块众多,系统对模块加载顺序有要求,一些基础模块在系统启动时需要很早就被加载;开发者加入自己的模块时,需要维护一个模块初始化列表,上面两方面的做起来很困难,为了科学地…

如何管理驱动模块

由于Linux驱动模块众多,系统对模块加载顺序有要求,一些基础模块在系统启动时需要很早就被加载;开发者加入自己的模块时,需要维护一个模块初始化列表,上面两方面的做起来很困难,为了科学地管理这些模块,首先要解决两个问题:

  1. 如何方便开发者快捷加入自己的模块
  2. 如何管理模块的加载顺序

Linux 内核开发者是怎么实现的呢?在内核镜像文件中,自定义了一个段,这个段里面专门用来存放这些初始化函数的地址,内核启动时,只需要在这个段地址处取出函数指针,依次执行即可。

对模块的开发者,Linux内核提供了统一的宏定义接口,驱动开发者只需要将驱动程序用这些宏定义来修饰,这个模块的初始化函数接口的指针就被自动添加到了上述的段中,开发者完全不需要关心这个实现的细节。

对于各种各样的驱动而言,会存在一定的依赖关系,需要遵循先后顺序来进行初始化,考虑到这个问题,Linux内核开发者也对这一部分的初始化顺序做了分级处理。


Linux驱动模块的加载方式

Linux 驱动模块有两种加载方式,一是静态编译链接进内核,在系统启动过程中进行初始化;另外一是编译成可动态加载的module,通过insmod动态加载重定位到内核。


Linux 使用宏定义

Linux提供了一组宏定义对模块进行静态和动态加载,同时对不同的模块加载顺序做了处理,提供了不同的宏定义方法。这组宏定义在路径/kernel/include/linux/init.h中。

#ifndef MODULE#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn#define early_initcall(fn)	    	__define_initcall("early",fn,early)
#define pure_initcall(fn)		    __define_initcall("0",fn,0)
#define core_initcall(fn)		    __define_initcall("1",fn,1)
#define core_initcall_sync(fn)		__define_initcall("1s",fn,1s)
#define postcore_initcall(fn)		__define_initcall("2",fn,2)
#define postcore_initcall_sync(fn)	__define_initcall("2s",fn,2s)
#define arch_initcall(fn)		    __define_initcall("3",fn,3)
#define arch_initcall_sync(fn)		__define_initcall("3s",fn,3s)
#define subsys_initcall(fn)		    __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn)	__define_initcall("4s",fn,4s)
#define fs_initcall(fn)			    __define_initcall("5",fn,5)
#define fs_initcall_sync(fn)		__define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)		    __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)		    __define_initcall("6",fn,6)
#define device_initcall_sync(fn)	__define_initcall("6s",fn,6s)
#define late_initcall(fn)		    __define_initcall("7",fn,7)
#define late_initcall_sync(fn)		__define_initcall("7s",fn,7s)
#define __initcall(fn)              device_initcall(fn)
#define module_init(x)	            __initcall(x);#else /* MODULE *//* Don't use these in modules, but some people do... */
#define early_initcall(fn)		module_init(fn)
#define core_initcall(fn)		module_init(fn)
#define postcore_initcall(fn)	module_init(fn)
#define arch_initcall(fn)		module_init(fn)
#define subsys_initcall(fn)		module_init(fn)
#define fs_initcall(fn)			module_init(fn)
#define device_initcall(fn)		module_init(fn)
#define late_initcall(fn)		module_init(fn)
#define security_initcall(fn)	module_init(fn)/* Each module must use one module_init(). */
#define module_init(initfn)					\static inline initcall_t __inittest(void)		\{ return initfn; }					\int init_module(void) __attribute__((alias(#initfn)));/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)					\static inline exitcall_t __exittest(void)		\{ return exitfn; }					\void cleanup_module(void) __attribute__((alias(#exitfn)));
#endif /*end ifndef MODULE*/

Makefile是如何控制模块的加载模式的?在init.h中可以看到下面的宏定义:

#ifndef MODULE
// 宏定义--->(静态加载方式)
#else 
// 宏定义--->(动态加载方式)
#endif

当配置Makefiel时,将某个module配置为obj-m时,MODULE 这个宏就被定义,此时当前模块就被编译到内核代码中,内核启动时这个模块就被静态加载,反之,模块配置为obj-y,当前模块被配置为动态加载方式。


宏定义 __define_initcall 分析

我们看看如何解析这个宏__define_initcall:

#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn

_ * attribute * _ () 是gnu C中的扩展语法,它可以用来实现很多灵活的定义行为。
_ * attribute * _ ((_ * section * _ (“.initcall” #id “.init”)))表示编译时将目标符号放置在括号指定的段中。

在宏定义中,# 的作用是将目标字符串化,## 在宏定义中的作用是符号连接,将多个符号连接成一个符号,并不字符串化。

level是一个数字或者是数字+s,这个数字代表这个fn执行的优先级,数字越小,优先级越高,带s的fn优先级低于不带s的fn优先级

__used是一个宏定义

#define __used __attribute__((__used__))

使用前提是在编译器编译过程中,如果定义的符号没有被引用,编译器就会对其进行优化,不保留这个符号,而__attribute__((used))的作用是告诉编译器这个静态符号在编译的时候即使没有使用到也要保留这个符号。

这里的 initcall_t 是函数指针类型,对应的段:.initcall,如下:

typedef int (*initcall_t)(void);

上面所述,这个宏将我们的初始化函数放在".initcall" level ".init"中。这个段可以在Vmlinux.lds.h里面找到,如下:

#define INITCALLS                              \*(.initcall0.init)                       \*(.initcall0s.init)                      \*(.initcall1.init)                       \*(.initcall1s.init)                      \*(.initcall2.init)                       \*(.initcall2s.init)                      \*(.initcall3.init)                       \*(.initcall3s.init)                      \*(.initcall4.init)                       \*(.initcall4s.init)                      \*(.initcall5.init)                       \*(.initcall5s.init)                      \*(.initcallrootfs.init)                  \*(.initcall6.init)                       \*(.initcall6s.init)                      \*(.initcall7.init)                       \*(.initcall7s.init)

INITCALL 可以在vmlinux.lds.S里面找到:

.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {__init_begin = .;_sinittext = .;*(.init.text)_einittext = .;}.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { *(.init.data) }. = ALIGN(16);.init.setup : AT(ADDR(.init.setup) - LOAD_OFFSET) {__setup_start = .;*(.init.setup)__setup_end = .;}.initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {__initcall_start = .;INITCALLS //这里__initcall_end = .;}.con_initcall.init : AT(ADDR(.con_initcall.init) - LOAD_OFFSET) {__con_initcall_start = .;*(.con_initcall.init)__con_initcall_end = .;}

Vmlinux.lds.h 中是系统启动时存放初始化数据的指针,执行完成后会被释放掉内存。根据上面的内存布局,可以列出初始化宏和 内存的对应关系

_init_begin        |-------------------||  .init.text       | ---- __init|-------------------||  .init.data       | ---- __initdata
_setup_start       |-------------------||  .init.setup      | ---- __setup_param
__initcall_start   |-------------------||  .initcall1.init  | ---- core_initcall|-------------------||  .initcall2.init  | ---- postcore_initcall|-------------------||  .initcall3.init  | ---- arch_initcall|-------------------||  .initcall4.init  | ---- subsys_initcall|-------------------||  .initcall5.init  | ---- fs_initcall|-------------------||  .initcall6.init  | ---- device_initcall|-------------------||  .initcall7.init  | ---- late_initcall
__initcall_end     |-------------------||                   ||    ... ... ...    ||                   |
__init_end         |-------------------|

宏定义 #define module_init(initfn)

#define module_init(initfn)					        \static inline initcall_t __inittest(void)		\{ return initfn; }					            \int init_module(void) __attribute__((alias(#initfn)));

前两句话只是做了一个检测,当传进来的函数指针的参数和返回值与initcall_t不一致时,就会有告警。第三句,是使用alias将initfn变名为init_module,当调用insmod将module加载进内核时,就会去找init_module作为入口地址,即传进来的initfn, 这样module就被加载了。


举例:

为了更方便地理解,我们举个例子来说明,开发者声明了这样一个函数:module_init(hello_init);

首先宏展开成:__define_initcall(“6”,hello_init, 6)
然后接着展开:static initcall_t __initcall_hello_init6 = hello_init; 定义了函数指针变量。
同时声明 __initcall_hello_init6 这个变量即使没被引用也保留符号,且将其放置在内核镜像的.initcall6.init段处。


xxx_initcall()宏定义调用追踪


从上面的分析,我们知道xxx_initcall是如何被定义,知道目标函数的放置位置,那么使用xxx_initcall()修饰的函数是怎么被调用的呢?下面就从内核 init/main.c函数起始部分start_kernel开始往下追踪,它的调用顺序为:

start_kernel  -> rest_init();-> kernel_thread(kernel_init, NULL, CLONE_FS);-> kernel_init()-> do_basic_setup();-> do_initcalls();

rest_init();启动 RCU 锁调度器 ,调用函数 kernel_thread 创建 kernel_init 进程,也就是 init 内核进程, init 进程的 PID 为 1。调用函数 kernel_thread 创建 kthreadd 内核进程,此内核进程的 PID 为 2。kthreadd 进程负责所有内核进程的调度和管理。

do_initcalls() 在这个函数中执行所有使用xxx_initcall()声明的函数,完成 Linux 下驱动模型子系统的初始化。

static void __init do_initcalls(void)
{initcall_t *fn;for (fn = __early_initcall_end; fn < __initcall_end; fn++)do_one_initcall(*fn);
}

函数中的 fn 为函数指针,fn++ 相当于函数指针+1,相当于内存地址+sizeof(fn)

int do_one_initcall(initcall_t fn)
{ret.result = fn();//执行功能函数
}

在do_one_initcall函数里执行被初始化的模块。

http://www.yayakq.cn/news/697188/

相关文章:

  • 公司网站设计与制百度收录提交查询
  • 免费做直播网站网站服务器空间不足
  • 制作购物网站需要多少钱深圳个性化网站建设公司电话
  • 学校网站建设项目需求报告建设摩托车官网旗舰店
  • 手机怎么搭建属于自己的网站wordpress的文章多重筛选
  • WordPress渗透思路wordpress 分页seo
  • 静安免费网站制作网站收录网
  • 河北高端网站设计公司创建网站基本流程
  • 网站建设的基础知识与维护织梦做的网站织梦修改网页
  • 怎么创建免费的个人网站企业门户网站运营推广
  • 自助建网站哪个好163cm邮箱登录入口
  • 什么网站可以做期货电子商务创建网站
  • 免费建站有哪些哪些网站可以做问卷
  • 九江有哪些免费推广网站资格证网站怎么做
  • 关于企业网站开发与设计论文企业门户网站开发平台的设计
  • 视频上传网站源码深圳网站制作公司建设
  • 江苏中小企业网站建设白银做网站的董事
  • 河南专业网站建设公司郑州专业网站制作服务费用
  • 西安市住宅和城乡建设局网站自己搭建视频播放网站
  • 新竹自助网站seo最新快速排名
  • 企业网阳江一中成绩查询苏州首页关键词优化
  • 网站开发试题营销型网站建设哪家专业
  • 论坛打赏网站开发北京网站建设方案系统
  • 哈尔滨网站建设制作价格宁波建站平台
  • 兼职给企业做网站网站动画是怎么做的
  • 自建网站怎么做二级页跳转常州网站外包
  • 重庆做网站建设的公司哪家好建设银行大连招聘网站
  • 有了域名怎样做网站聊城网站开发个人
  • 3733手游网站在哪里做的安装完整wordpress主题
  • 福建大舟建设集团有限公司 网站网上作业网站怎么做的