博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tomato的init启动流程分析(原创)
阅读量:5165 次
发布时间:2019-06-13

本文共 2330 字,大约阅读时间需要 7 分钟。

1.首先,路由器的启动有多种不同的阶段。

通电后,最先执行的CFE启动,CFE完成基本初始化后,把系统交给linux内核。

而linux内核也有一个初始化过程,比如文件系统啊,内存管理啊,系统资源调度啊,等等。

Linux内核初始化完成后,就执行系统的第1个进程init。本文所讲的启动流程是从init进程开始执行,路由器是如何运行的。

 

2.从主函数开始。init进程的主函数是 $(ROUTER)/rc/init.c 文件中的init_main( )函数。

注:为什么是init_main而不是main呢?这是因为init进程实际上链接到rc程序的,运行 ls -l /sbin/init就可以看到的。

从init_main( )开始,基本上就2个过程,先执行一些初始化,比如校验某些关键的nvram参数,加载驱动,设置某些内核参数等。

这些主要是由函数sysinit( )完成的。

然后就是信号集初始化,最后是一个for (;;)死循环,这里面完成各种路由器页面的功能,完成之后等待新的信号到来再次执行不同的服务。

(1)sysinit( )部分

加载驱动,设置内核参数以及某些相关的初始化就不说了,我们主要关注其中校验nvram参数的部分。与之有关的函数有2个,check_bootnv( )和init_nvram( )。

初看起来,这2个函数里貌似都有对nvram参数的操作,那是否可以随便使用呢?

答案是否定的。经过试验就会发现,如果在check_bootnv( )中修正某些nvram变量的默认值,会不起作用,什么原因呢?

而在init_nvram( )修改VLAN参数,在恢复出厂设置后的第1次启动,VLAN并没有生效,什么原因呢?

答案是:

check_bootnv( )是检查启动相关参数的,而init_nvram( )是修改初始化nvram参数的。

check_bootnv( )和底层关系更紧密一些,init_nvram( )和应用层的关系更紧密。

首先理解:系统恢复出厂设置后第1次启动的时候,nvram存储的只有CFE中的那些明文的nvram变量的,$(ROUTER)/nvram/defaults.c中的那个长长的结构体数组中的很多nvram变量都是没有的,所以需要有一个设置默认值的过程。

 

eval("nvram", "defaults", "--initcheck");

上面这一句就是设置出厂默认nvram值。这一句在check_bootnv( )之后并且在init_nvram( )之前执行。

追踪到$(ROUTER)/nvram/nvram.c文件的defaults_main( )函数,可以发现:

如果是恢复出厂设置后第1次启动,必然是force = 1的(原因自己看代码)。

既然force = 1,那很多nvram变量就被恢复成.../router/nvram/defaults.c中的默认值了。

 

至于在init_nvram( )中修改VLAN参数首次启动路由器的时候vlan不生效,那是因为驱动的加载是在init_nvram( )之前。

BCM驱动虽然没有开放源码,但是肯定是会读取某些nvram参数的。

结论:

不要在check_bootnv( )中修改defaults.c中的默认变量,最好是放到init_nvram( )去修改。

但是,如果要在init_nvram( )修改和vlan有关的变量以及系统底层驱动有关的变量,那最好是修改完成后执行nvram_commit( )后多重启1次。

 

(2)for循环部分

前面的代码好理解,搞一个switch检测不同的信号值,执行不同的动作。难理解的是最后3句:

 

1         chld_reap(0);        /* Periodically reap zombies. */2        check_services();3         sigwait(&sigset, &state);

 

第1句是回收某些子进程产生的僵尸进程(init进程是整个系统其他进程的祖宗啊)。

第2句是检测下面4个系统服务,如果异常终止,自动重启。现在就不用奇怪dnsmasq进程为啥kill干不掉了^_^。

而第3句sigwait(&sigset, &state)就很费解了^_^。这个可以自己baidu下。baidu上的那些人讲了很多废话^_^。其实在toamto里就是:监测系统信号集的变化(异步监测),如果信号集中的信号发生变化,就执行相应的信号动作,并且进一步执行返回到for循环开头执行,否则就一直休眠在那里等待直到信号集中的信号到来(这个和socket编程的select机制有点类似吧)。

比如dnsmasq异常退出了(比如被你人为干掉了)系统肯定会有SIGCHLD信号产生的,追踪到SIGCHLD信号的处理函数signal(SIGCHLD, handle_reap)--------------raise(SIGALRM),而SIGALRM是在所监视的信号集合中的。

init进程的信号集定义如下:

 

static int initsigs[] = {    SIGHUP,    SIGUSR1,    SIGUSR2,    SIGINT,    SIGQUIT,    SIGALRM,    SIGTERM};

 

至此toamto得init启动流程分析完毕,主要分析了其中比较难理解的部分,其它部分代码,基本看函数和变量命名就能猜出干啥的^_^。

 

 

转载于:https://www.cnblogs.com/bank/p/3618312.html

你可能感兴趣的文章
php 返回json 解析 报Wide character in print
查看>>
tomcat docBase 和 path
查看>>
jQuery代码性能小细节
查看>>
java默认语法、EL、JSTL表达式,JSTL和struts Tag标签的使用总结
查看>>
复杂度分析
查看>>
快乐的序列
查看>>
解密Redis持久化
查看>>
Vue笔记:使用 axios 发送请求
查看>>
利用反射动态调用类成员
查看>>
富文本编辑器 - RichEditor
查看>>
angular学习(十五)——Provider
查看>>
机器学习在租房信息判别中的应用
查看>>
iOS crash 崩溃问题的追踪方法
查看>>
定制Android透明按钮
查看>>
prototype for '类名::函数名'does not match any in class'类名'
查看>>
如何让.NET Core应用的配置与源文件保持同步?
查看>>
南京Uber优步司机奖励政策(2月1日~2月7日)
查看>>
成都Uber优步司机奖励政策(3月25日)
查看>>
linux shell脚本获得当前文件路径
查看>>
团队作业9——第二次项目冲刺1(Beta阶段)
查看>>