参阅CU上的很多关于rc的帖子,于是很想弄清楚Linux/Unix到底是如何启动的?rc脚本有是如何起作用的?

幸亏goole什么都知道。

罗列一篇,方便类我等菜鸟来温习和查阅,以Solaris为例

按下电源,首先是BIOS取得系统控制权,BIOS进行最初的引导工作,然后交控制权交给引导分区,由引导分区加载内核并调用start_kernel函数。

内核首先引导核心数据结构的初始化,在start_kernel函数中完成如下工作:

  • 输出Linux版本信息(printk(linux_banner))
  • 配置和体系结构相关的环境(setup_arch())
  • 页表结构初始化(paging_init())
  • 使用"arch/alpha/kernel/entry.S"中的入口点配置系统自陷入口(trap_init())
  • 使用alpha_mv结构和entry.S入口初始化系统IRQ(init_IRQ())
  • 核心进程调度器初始化(包括初始化几个缺省的Bottom-half,sched_init())
  • 时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等,time_init())
  • 提取并分析核心启动参数(从环境变量中读取参数,配置相应标志位等待处理,(parse_options())
  • 控制台初始化(为输出信息而先于PCI初始化,console_init())
  • 剖析器数据结构初始化(prof_buffer和prof_len变量)
  • 核心Cache初始化(描述Cache信息的Cache,kmem_cache_init())
  • 延迟校准(获得时钟jiffies和CPU主频ticks的延迟,calibrate_delay())
  • 内存初始化(配置内存上下界和页表项初始值,mem_init())
  • 创建和配置内部及通用cache("slab_cache",kmem_cache_sizes_init())
  • 创建uid taskcount SLAB cache("uid_cache",uidcache_init())
  • 创建文档cache("files_cache",filescache_init())
  • 创建目录cache("dentry_cache",dcache_init())
  • 创建和虚存相关的cache("vm_area_struct","mm_struct",vma_init())
  • 块设备读写缓冲区初始化(同时创建"buffer_head"cache用户加速访问,buffer_init())
  • 创建页cache(内存页hash表初始化,page_cache_init())
  • 创建信号队列cache("signal_queue",signals_init())
  • 初始化内存inode表(inode_init())
  • 创建内存文档描述符表("filp_cache",file_table_init())
  • 检查体系结构漏洞(对于alpha,此函数为空,check_bugs())
  • SMP机器其余CPU(除当前引导CPU)初始化(对于没有配置SMP的内核,此函数为空,smp_init())
  • 启动init过程(创建第一个核心线程,调用init()函数,原执行序列调用cpu_idle() 等待调度,init())

至此start_kernel()结束,基本的核心环境已建立起来了。

start_kernel最后一项是启动了init函数,接着由他来完成外设的初始化

  • 总线初始化(比如pci_init())
  • 网络初始化(初始化网络数据结构,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,将调用protocols结构中包含的任何协议的初始化过程,sock_init())
  • 创建bdflush核心线程(bdflush()过程常驻核心空间,由核心唤醒来清理被写过的内存缓冲区,当bdflush()由kernel_thread()启动后,他将自己命名为kflushd)
  • 创建kupdate核心线程(kupdate()过程常驻核心空间,由核心按时调度执行,将内存缓冲区中的信息更新到磁盘中,更新的内容包括终极块和inode表)
  • 配置并启动核心调页线程kswapd(为了防止kswapd启动时将版本信息输出到其他信息中间,核心线调用kswapd_setup()配置kswapd运行所需要的环境,然后再创建 kswapd核心线程)
  • 创建事件管理核心线程(start_context_thread()函数启动context_thread()过程,并重命名为keventd)
  • 设备初始化(包括并口parport_init()、字符设备chr_dev_init()、块设备 blk_dev_init()、SCSI设备scsi_dev_init()、网络设备net_dev_init()、磁盘初始化及分区检查等等,device_setup())
  • 执行文档格式配置(binfmt_setup())
  • 启动任何使用__initcall标识的函数(方便核心研发者添加启动函数,do_initcalls())
  • 文档系统初始化(filesystem_setup())
  • 安装root文档系统(mount_root())

这些步骤结束后,init()搜索文档系统中的init程式,并创建他,也就是我们通常所说的init进程,他是系统任何进程的起点,进程ID=1。

在启动了的Solaris下,利用 "$ps -p 1" 能够查看该进程,输出如下:

PID TTY TIME CMD
1 ? 0:01 init

接下来init进程读取/etc/inittab文档,来决定下一步如何做。

inittab是以行为单位的描述性(非执行性)文本,每一个指令行都具备以下格式:

id:runlevel:action:process 其中id为入口标识符,runlevel为运行级别,action为动作代号,process为具体的执行程式。

id一般需要4个字符以内,runlevelinit所处于的运行级别的标识,一般使用0-6连同S或s(S或s表示单用户模式)。

action字段则告诉init进程,如何对待process字段指定的进程:当inittab中各行的runlevel值和当前运行级别匹配时,指定的action才被执行。

但有几个特别的action:

initdefault是个特别的action值,用于标识缺省的启动级别;当init由核心激活以后,他将首先读取inittab中的initdefault项,取得其中的runlevel,并作为当前的运行级别。

sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的runlevel,即不管当前运行级别是什么,他都执行,并且是优先执行。其余的action(不含initdefault)都和某个runlevel相关。

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!