FreeBSD 内核中的SYSINIT分析【转】

FreeBSD?kernel是一个膨大的系统,?对于这样一个大系统,?里面往往包含了大量的子系统和??
模块,当系统初始化时这些模块就需要初始化,?按照通常的思路,这些初始化过程必须在某处??
被显式地调用,这样一来,当你新增某个模块,你必须再修改那个系统初始化的地方来调用这??
个新增模块的初始化过程,?而且由于ANSI?C语言的限制,调用某个函数最好先声明,这样当系??
统的初始化过程开始增加时,?那个调用初始化过程的文件开始大量包含那些本来不相关的头??
文件,?偶合度就增加了,?这是一种不好的设计.??

FreeBSD为了应付这种情况,?使用一种叫做SYSINIT的机制.?我们知道FreeBSD使用一种叫做??
ELF的二进制目标执行文件格式.?这种文件格式允许文件内部组织成结构化的方式,?文件内??
部可以由不同的组成部分(section),?FreeBSD正是利用了这种机制.???

FreeBSD使用GNU?GCC作为其C语言编译器,?这种编译器允许在C源程序中嵌入汇编语言代码,??
FreeBSD通过在C源程序中加入汇编指令来在目标文件中增加额外的section,?在文件??
/sys/sys/linker_set.h中定义如下:??

#ifdef?__alpha__??
#define?MAKE_SET(set,?sym)??????????????????????????????????????????????\??
????????static?void?const?*?const?__set_##set##_sym_##sym?=?&sym;???????\??
????????__asm(".align?3");??????????????????????????????????????????????\??
????????__asm(".section?.set."?#set?",\"aw\"");?????????????????????????\??
????????__asm(".quad?"?#sym);???????????????????????????????????????????\??
????????__asm(".previous")??
#else??
#define?MAKE_SET(set,?sym)?????
#define?MAKE_SET(set,?sym)??????????????????????????????????????????????\??
????????static?void?const?*?const?__set_##set##_sym_##sym?=?&sym;???????\??
????????__asm(".section?.set."?#set?",\"aw\"");?????????????????????????\??
????????__asm(".long?"?#sym);???????????????????????????????????????????\??
????????__asm(".previous")??
#endif??
#define?TEXT_SET(set,?sym)?MAKE_SET(set,?sym)??
#define?DATA_SET(set,?sym)?MAKE_SET(set,?sym)??

程序一旦在某处调用DATA_SET宏指令,?就会将相应的汇编符号加入到目标文件.?例如:??
int?myint;??
DATA_SET(myset,?myint);??
这两句话将导致在目标文件中创建一个myset?section,?并且myint的地址将被放入这个??
section中.??

系统的初始化必须按严格的顺序进行,?为此FreeBSD定义了很多子系统的顺序号,?这些顺序??
连同SYSINIT的许多相关定义在/sys/sys/kernel.h头文件中:??

enum?sysinit_sub_id?{??
????????SI_SUB_DUMMY????????????=?0x0000000,????/*?not?executed;?for?linker*/??
????????SI_SUB_DONE?????????????=?0x0000001,????/*?processed*/??
????????SI_SUB_CONSOLE??????????=?0x0800000,????/*?console*/??
????????SI_SUB_COPYRIGHT????????=?0x0800001,????/*?first?use?of?console*/??
????????SI_SUB_TUNABLES?????????=?0x0700000,????/*?establish?tunable?values?*/??
????????SI_SUB_VM???????????????=?0x1000000,????/*?virtual?memory?system?init*/??
????????SI_SUB_KMEM?????????????=?0x1800000,????/*?kernel?memory*/??
????????SI_SUB_KVM_RSRC?????????=?0x1A00000,????/*?kvm?operational?limits*/??
????????SI_SUB_CPU??????????????=?0x1e00000,????/*?CPU?resource(s)*/??
????????SI_SUB_KLD??????????????=?0x1f00000,????/*?KLD?and?module?setup?*/??
????????SI_SUB_INTRINSIC????????=?0x2000000,????/*?proc?0*/??
????????SI_SUB_VM_CONF??????????=?0x2100000,????/*?config?VM,?set?limits*/??
????????SI_SUB_RUN_QUEUE????????=?0x2200000,????/*?the?run?queue*/??
????????SI_SUB_CREATE_INIT??????=?0x2300000,????/*?create?the?init?process?*/??
????????SI_SUB_DRIVERS??????????=?0x2400000,????/*?Let?Drivers?initialize?*/??
????????SI_SUB_CONFIGURE????????=?0x3800000,????/*?Configure?devices?*/??
????????SI_SUB_VFS??????????????=?0x4000000,????/*?virtual?file?system*/??
????????SI_SUB_CLOCKS???????????=?0x4800000,????/*?real?time?and?stat?clocks*/??
????????SI_SUB_MBUF?????????????=?0x5000000,????/*?mbufs*/??
????????SI_SUB_CLIST????????????=?0x5800000,????/*?clists*/??
????????SI_SUB_SYSV_SHM?????????=?0x6400000,????/*?System?V?shared?memory*/??
????????SI_SUB_SYSV_SEM?????????=?0x6800000,????/*?System?V?semaphores*/??
????????SI_SUB_SYSV_MSG?????????=?0x6C00000,????/*?System?V?message?queues*/??
????????SI_SUB_P1003_1B?????????=?0x6E00000,????/*?P1003.1B?realtime?*/??
????????SI_SUB_PSEUDO???????????=?0x7000000,????/*?pseudo?devices*/??
????????SI_SUB_EXEC?????????????=?0x7400000,????/*?execve()?handlers?*/??
????????SI_SUB_PROTO_BEGIN??????=?0x8000000,????/*?XXX:?set?splimp?(kludge)*/??
????????...??
};??

子系统内还有顺序号:??
enum?sysinit_elem_order?{??
????????SI_ORDER_FIRST??????????=?0x0000000,????/*?first*/??
????????SI_ORDER_SECOND?????????=?0x0000001,????/*?second*/??
????????SI_ORDER_THIRD??????????=?0x0000002,????/*?third*/??
????????SI_ORDER_MIDDLE?????????=?0x1000000,????/*?somewhere?in?the?middle?*/??
????????SI_ORDER_ANY????????????=?0xfffffff?????/*?last*/??
};??

FreeBSD为每个想要在系统初始化时被调用的函数,?定义两个函数类型:??
typedef?void?(*sysinit_nfunc_t)?__P((void?*));??
typedef?void?(*sysinit_cfunc_t)?__P((const?void?*));??
它们是系统初始化被调用时使用的函数原型.??
两个重要的宏使得初始化函数能够在系统开始时被执行:??

#define?C_SYSINIT(uniquifier,?subsystem,?order,?func,?ident)????\??
????????static?struct?sysinit?uniquifier?##?_sys_init?=?{???????\??
????????????????subsystem,??????????????????????????????????????\??
????????????????order,??????????????????????????????????????????\??
????????????????func,???????????????????????????????????????????\??
????????????????ident???????????????????????????????????????????\??
????????};??????????????????????????????????????????????????????\??
????????DATA_SET(sysinit_set,uniquifier?##?_sys_init);??

#define?SYSINIT(uniquifier,?subsystem,?order,?func,?ident)??????\??
????????C_SYSINIT(uniquifier,?subsystem,?order,?????????????????\??
????????(sysinit_cfunc_t)(sysinit_nfunc_t)func,?(void?*)ident)??

其中每个初始化函数被存储成这样一个结构:??
????????struct?sysinit?{??
???????????unsigned?int????subsystem;??????????????/*?subsystem?identifier*/??
???????????unsigned?int????order;??????????????????/*?init?order?within?subsystem*/??
???????????sysinit_cfunc_t?func;???????????????????/*?function?????????????*/??
???????????const?void??????*udata;?????????????????/*?multiplexer/argument?*/??
????????};??
这个结构包含了子系统编号,?子系统中的顺序号,?初始化函数的地址,?以及这个函数??
使用的参数.??

现在如果有个函数想要在系统启动时自动被调用,?并且知道这个函数是为VM子系统做准备工??
作,?可以这样申明:??

long?myvar;??
void?init_myvar(void?*p)??
{??
?????*(long?*)p?=?2;??
}??
SYSINIT(init_myvar,?SI_SUB_VM,?1000,?init_myvar,?&myvar)??

这样声明的初始化过程分布在很多目标文件中,?当gcc的连接编辑器ld运行时就会把属于同??
一个section的数据合并到一个连续的地址块中.??
由于在这个section中包含的只能是指向sysinit结构的指针,这样FreeBSD就可以把这个地址??
当成一个sysinit*?的数组,?FreeBSD找出这个sysinit_set地址,?边历这个数组并调用其中??
的初始化函数.?为了确切知道这个section的大小(直接读ELF是可能的,但是那样太复杂,要??
知道kernel调用初始化过程时文件系统可能还没有初始化呢),?系统中包含一个工具??
gensetdefs,?这个工具能扫描给出的一组.o目标文件,?并找到任何名字是由.set.开头的??
section,?它统计有多少个这样的的初始化函数,?并在sysinit_set的开头生成一个长整形??
计数器.?gensetdefs生成三个文件:??
setdef0.c?setdef1.c?setdefs.h??

文件setdef0.c的内容:??

--------------------------------------------------------??
/*?THIS?FILE?IS?GENERATED,?DO?NOT?EDIT.?*/??

#define?DEFINE_SET(set,?count)??????????????????\??
__asm__(".section?.set."?#set?",\"aw\"");???????\??
__asm__(".globl?"?#set);????????????????????????\??
__asm__(".type?"?#set?",@object");??????????????\??
__asm__(".p2align?2");??????????????????????????\??
__asm__(#set?":");??????????????????????????????\??
__asm__(".long?"?#count);???????????????????????\??
__asm__(".previous")??

#include?"setdefs.h"????????????/*?Contains?a?`DEFINE_SET‘?for?each?set?*/??
--------------------------------------------------------??

这里的DEFINE_SET效果就是申明一C结构:??
struct?linker_set?{??
????????int?????ls_length;??
????????void????*ls_items[1];???????????/*?really?ls_length?of?them,??
????????????????????????????????????????????????*?trailing?NULL?*/??
};??

文件setdef1.c的内容:??

--------------------------------------------------------??
/*?THIS?FILE?IS?GENERATED,?DO?NOT?EDIT.?*/??

#define?DEFINE_SET(set,?count)??????????????????????????\??
__asm__(".section?.set."?#set?",\"aw\"");???????\??
__asm__(".long?0");?????????????????????\??
__asm__(".previous")??

#include?"setdefs.h"????????????/*?Contains?a?`DEFINE_SET‘?for?each?set?*/??

这个DEFINE_SET在某个section中放入一个?long?0.??
--------------------------------------------------------??

文件setdefs.h的内容:??

DEFINE_SET(cons_set,?3);??
DEFINE_SET(kbddriver_set,?2);??
DEFINE_SET(periphdriver_set,?5);??
DEFINE_SET(scrndr_set,?9);??
DEFINE_SET(scterm_set,?1);??
DEFINE_SET(sysctl_set,?552);??
DEFINE_SET(sysinit_set,?323);??
DEFINE_SET(sysuninit_set,?155);??
DEFINE_SET(vga_set,?9);??
DEFINE_SET(videodriver_set,?4);??

当kernel被连接时,?在Makefile中setdef0.o被安排最前面,?这样ld就把这个初始化函数的??
计数器安排在这个section的最前面.?FreeBSD?kernel就能从这个section的开头读到这个计??
数器,?也就知道了有多少个初始化函数.?在Makefile中被安排在中间的的是FreeBSD的其他??
.o文件,?最后由setdef1.o压阵.?setdef1.c定义了一个空指针,用以表示这个section的结束??
,这种安排,?我把它叫做夹三明治.??

初始化过程的调用被安排在内核?/sys/kern/init_main.c的mi_startup函数中,?mi_startup??
是系统启动过程中,?第一个被执行的C语言函数,??它做的第一件事情就是调用这些初始化函??
数,?开始时对所有的初始化过程做优先级排序,?然后顺序调用它们.??

void??????????????????????
mi_startup(void)??
{?????????????????
??????????????????????????
????????register?struct?sysinit?**sipp;?????????/*?system?initialization*/??
????????register?struct?sysinit?**xipp;?????????/*?interior?loop?of?sort*/??
????????register?struct?sysinit?*save;??????????/*?bubble*/??

restart:??????????
??????????
????????这是优先级别排序,?这里没有使用那个在setdef0.c中定义的计数器,?而是使用??
????????了setdef1.c中定义的空指针作为结束标志.??
??????????
????????/*????????
?????????*?Perform?a?bubble?sort?of?the?system?initialization?objects?by??
?????????*?their?subsystem?(primary?key)?and?order?(secondary?key).??
?????????*/???????
????????for?(sipp?=?sysinit;?*sipp;?sipp++)?{??
????????????????for?(xipp?=?sipp?+?1;?*xipp;?xipp++)?{??
????????????????????????if?((*sipp)->subsystem?< (*xipp)->subsystem?||??
?????????????????????????????((*sipp)->subsystem?==?(*xipp)->subsystem?&&??
??????????????????????????????(*sipp)->order?<= (*xipp)->order))??
????????????????????????????????continue;???????/*?skip*/??
????????????????????????save?=?*sipp;??
????????????????????????*sipp?=?*xipp;??
????????????????????????*xipp?=?save;??
????????????????}??
????????}??

????????/*??
?????????*?Traverse?the?(now)?ordered?list?of?system?initialization?tasks.??
?????????*?Perform?each?task,?and?continue?on?to?the?next?task.??
?????????*??
?????????*?The?last?item?on?the?list?is?expected?to?be?the?scheduler,??
?????????*?which?will?not?return.??
?????????*/??
????????for?(sipp?=?sysinit;?*sipp;?sipp++)?{??

????????????????if?((*sipp)->subsystem?==?SI_SUB_DUMMY)??
????????????????????????continue;???????/*?skip?dummy?task(s)*/??

这是按顺序调用:??
/*??
?????????*?Traverse?the?(now)?ordered?list?of?system?initialization?tasks.??
?????????*?Perform?each?task,?and?continue?on?to?the?next?task.??
?????????*??
?????????*?The?last?item?on?the?list?is?expected?to?be?the?scheduler,??
?????????*?which?will?not?return.??
?????????*/??
????????for?(sipp?=?sysinit;?*sipp;?sipp++)?{??

????????????????if?((*sipp)->subsystem?==?SI_SUB_DUMMY)??
????????????????????????continue;???????/*?skip?dummy?task(s)*/??

????????????????if?((*sipp)->subsystem?==?SI_SUB_DONE)??
????????????????????????continue;??

????????????????/*?Call?function?*/??
????????????????(*((*sipp)->func))((*sipp)->udata);??

????????????????/*?Check?off?the?one?we‘re?just?done?*/??
????????????????(*sipp)->subsystem?=?SI_SUB_DONE;??

????????????????/*?Check?if?we‘ve?installed?more?sysinit?items?via?KLD?*/??
????????????????if?(newsysinit?!=?NULL)?{??
????????????????????????if?(sysinit?!=?(struct?sysinit?**)sysinit_set.ls_items)??
????????????????????????????????free(sysinit,?M_TEMP);??
????????????????????????sysinit?=?newsysinit;??
????????????????????????newsysinit?=?NULL;??
????????????????????????goto?restart;??
????????????????}??
????????}??

????????panic("Shouldn‘t?get?here!");??
}??
??????

SRC=http://www.moon-soft.com/program/bbs/readelite432617.htm

FreeBSD 内核中的SYSINIT分析【转】,布布扣,bubuko.com

时间: 2024-08-25 15:07:53

FreeBSD 内核中的SYSINIT分析【转】的相关文章

FreeBSD中的SYSINIT框架【转】

SYSINIT是一个通用的调用排序与分别执行机制的框架.FreeBSD目前使用它来进行内核的动态初始化.SYSINIT使得FreeBSD的内核各子系统可以在内核或模块动态加载链接时被重整.添加.删除.替换,这样,内核和模块加载时就不必去修改一个静态的有序初始化安排表甚至重新编译内核.这个体系也使得内核模块(现在称为KLD可以与内核不同时编译.链接.在引导系统时加载,甚至在系统运行时加载.这些操作是通过"内核链接器"(kernel linker)和"链接器集合"(li

Linux内核中的list用法和实现分析

这些天在思考知识体系的完整性,发现总是对消息队列的实现不满意,索性看看内核里面的链表实现形式,这篇文章就当做是学习的i笔记吧.. 内核代码中有很多的地方使用了list,而这个list的用法又跟我们平时在教科书中常见的用法有很大的不同,所以有必要详细了解下这里面的门道. 内核里面的list(如没有特殊说明,下文说的list都是指内核里面的list)可称之为侵入式链表.这种list最突出的特征就是其节点中不含有任何数据,相反,list节点是嵌入到特定的数据结构中的.大家自然就会问了,这样实现有什么好

Linux内核分析--内核中的数据结构双向链表续【转】

在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_HEAD()这个宏: #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) 需要注意的是,Linux 的每个双循

linux内核中socket的创建过程源码分析(总结性质)

http://www.jianshu.com/p/5d82a685b5b6 在漫长地分析完socket的创建源码后,发现一片浆糊,所以特此总结,我的博客中同时有另外一篇详细的源码分析,内核版本为3.9,建议在阅读本文后若还有兴趣再去看另外一篇博文.绝对不要单独看另外一篇. 一:调用链: 二:数据结构 一一看一下每个数据结构的意义: 1) socket, sock, inet_sock, tcp_sock的关系创建完sk变量后,回到inet_create函数中: 这里是根据sk变量得到inet_s

Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datasheet会把pin controller的内容放入GPIO controller的章节中),主要功能包括: (1)pin multiplexing.基于ARM core

Socket与系统调用深度分析 ——X86 64环境下Linux5.0以上的内核中

1.Socket与系统调用——概述 Socket API编程接口之上可以编写基于不同网络协议的应用程序: Socket接口在用户态通过系统调用机制进入内核: 内核中将系统调用作为一个特殊的中断来处理,以socket相关系统调用为例进行分析: socket相关系统调用的内核处理函数内部通过“多态机制”对不同的网络协议进行的封装方法: 下面会将Socket API编程接口.系统调用机制及内核中系统调用相关源代码. socket相关系统调用的内核处理函数结合起来分析,并在X86 64环境下Linux5

Openvswitch原理与代码分析(5): 内核中的流表flow table操作

? 当一个数据包到达网卡的时候,首先要经过内核Openvswitch.ko,流表Flow Table在内核中有一份,通过key查找内核中的flow table,即可以得到action,然后执行action之后,直接发送这个包,只有在内核无法查找到流表项的时候,才会到用户态查找用户态的流表.仅仅查找内核中flow table的情况被称为fast path. ? ? 第一步:从数据包中提取出key ? 实现函数为int ovs_flow_key_extract(const struct ip_tun

[原理分析]linux内核中的链表原理实践

摘要: 本文根据linux内核的链表定义,尝试自己来完成相关的接口设计,并且构建测试用例,来体会下链表接口的用法. 正文: 首先来看下linux内核提供的链表接口大致如下: struct head_node{ struct head_node* pre; struct head_node* next; }; 即节点中只有两个指针:一个指向前一个元素,一个指向后一个元素:假设我们现在要使用该接口,跟一个int值联合在一起形成数据节点,那么存在两种可能的方式: case1: typedef stru

Linux中的日志分析及管理

日志文件对于诊断和解决系统中的问题很有帮助,因为在Linux系统中运行的程序通常会把系统消息和错误消息写入相应的日志文件,这样系统一旦出现问题就会"有据可查".此外,当主机遭受攻击时,日志文件还可以帮助寻找攻击者留下的痕迹.一.主要日志文件在Linux系统中,日志数据主要包括以下三种类型:[内核及系统日志][用户日志][程序日志]Linux系统本身和大部分服务器程序的日志文件默认情况下都放置在目录"/var/log"中.一部分程序公用一个日志文件,一部分程序使用单个