首页|黄页|VIP|FTP|留言|注册|帮助|  合作之窗|推荐书目|  首页资料下载技术文章 开发板搜索供应求购企业列表嵌入式论坛您的位置:嵌入式首页->技术文章->软件开发->linux内核
搜索文章
文章:
文章类别
硬件设计 [7871]
   ARM文章 [116]
   DSP [587]
   单片机 [1428]
   AVR [30]
   无线开发 [0]
   EDA [581]
   接口电路 [351]
   存储技术 [109]
   显示光电 [64]
   电源技术 [860]
   传感/控制 [635]   模拟技术 [269]
   通信网络 [1144]   无线通信 [621]
   电测仪表 [526]
   消费电子 [284]
   汽车电子 [86]
软件开发 [2103]
   WinCE [29]
   uC/OS [43]
   linux编程 [154]   bootloader [27]   嵌入式开发 [1167]   编程基础 [23]
   vxworks [21]
   网络协议 [22]
   linux使用 [565]   linux内核 [19]
   数据库 [17] 
  发表时间:2005-9-28发布人:administrator阅读次数:775
Linux启动过程综述
亚威linux高级培训linux双证直...
亚威Linux培训全面针对于RHCE认证考试,
使用第4版官方教材,并辅以大量中...
OPEN-LAB(上海)授权LINUX培训考...
OPEN-LAB专业提供LINUX,CISCO,MCSE
业IT认证培训。LINUX课程设置为入...
百度主题推
Linux启动过程综述
l随笔
作者yfx409@2005年05月26日,14:23
内容:
一.Bootloader
二.Kernel引导入口
三.核心数据结构初始化--内核引导第一部分
四.外设初始化--内核引导第二部分
五.init进程和inittab引导指令
六.rc启动脚本
七.getty和login
八.bash
附:XDM方式登录
本文以Redhat6.0Linux2.2.19forAlpha/AXP为平台,描述了从开机到登录的Linux启动
文对i386平台同样适用。
一.Bootloader
在Alpha/AXP平台上引导Linux通常有两种方法,一种是由MILO及其他类似的引导程序引导,
Firmware直接引导。MILO功能与i386平台的LILO相近,但内置有基本的磁盘驱动程序(如IDE等),以及常见的文件系统驱动程序(如ext2,iso9660等),firmware有ARC、SRM两种形式
BIOS界面,甚至还有多重引导的设置;而SRM则具有功能强大的命令行界面,用户可以在控制台上使用boot等命令引导系统。ARC有分区(Partition)的概念,因此可以访问到分区的首扇区;而
制转给磁盘的首扇区。两种firmware都可以通过引导MILO来引导Linux,也可以直接引导Linux
码。
arch/alpha/boot下就是制作LinuxBootloader的文件。head.S文件提供了对OSFPAL/1口,它将被编译后置于引导扇区(ARC的分区首扇区或SRM的磁盘0扇区),得到控制后初始化一些构,再将控制转给main.c中的start_kernel(),start_kernel()向控制台输出一些提示
pal_init()初始化PAL代码,调用openboot()打开引导设备(通过读取Firmware环境),调
心代码加载到START_ADDR(见include/asm-alpha/system.h),再将Firmware中的核心引
到ZERO_PAGE(0)中,最后调用runkernel()将控制转给0x100000的kernel,bootloader部分
arch/alpha/boot/bootp.c以main.c为基础,可代替main.c与head.S生成用于
网络引导的Bootloader。
   linux集 [3]
   c&&c++编程 [5]
程序杂谈 [68]
   程序杂谈-嵌入
式 [40]
   程序杂谈-其它 [28]业界资讯 [633]
   业界-嵌入式 [600]   业界-其它 [33]
移动开发 [182]
   Symbian [161]
   WindowsMobile [1]   J2ME [20]Bootloader中使用的所有srm_函数在arch/alpha/lib/中定义。
以上这种Boot方式是一种最简单的方式,即不需其他工具就能引导Kernel,前提是按照Makefile
生成bootimage文件,内含以上提到的bootloader以及vmlinux,然后将bootimage写入自磁
的位置中。
当采用MILO这样的引导程序来引导Linux时,不需要上面所说的Bootloader,而只需要vmlinux
vmlinux.gz,引导程序会主动解压加载内核到0x1000(小内核)或0x100000(大内核),并
引导部分,即本文的第二节。
对于I386平台
i386系统中一般都有BIOS做最初的引导工作,那就是将四个主分区表中的第一个可引导分区
加载到实模式地址0x7c00上,然后将控制转交给它。
在arch/i386/boot目录下,bootsect.S是生成引导扇区的汇编源码,它首先将自己拷贝
上,然后将紧接其后的setup部分(第二扇区)拷贝到0x90200,将真正的内核代码拷贝到0x100000这些拷贝动作都是以bootsect.S、setup.S以及vmlinux在磁盘上连续存放为前提的,也就是
bzImage文件或者zImage文件是按照bootsect,setup,vmlinux这样的顺序组织,并存放于始于引
的首扇区的连续磁盘扇区之中。
bootsect.S完成加载动作后,就直接跳转到0x90200,这里正是setup.S的程序入口。setup.S
就是将系统参数(包括内存、磁盘等,由BIOS返回)拷贝到0x90000-0x901FF内存中,这个
bootsect.S存放的地方,这时它将被系统参数覆盖。以后这些参数将由保护模式下的代码来
除此之外,setup.S还将video.S中的代码包含进来,检测和设置显示器和显示模式。最后,
统转换到保护模式,并跳转到0x100000(对于bzImage格式的大内核是0x100000,对于zImage
0x1000)的内核引导代码,Bootloader过程结束。
对于2.4.x版内核
没有什么变化。
二.Kernel引导入口
在arch/alpha/vmlinux.lds的链接脚本控制下,链接程序将vmlinux的入口置于
"arch/alpha/kernel/head.S"中的__start上,因此当Bootloader跳转到0x100000时,__start
始执行。__start的代码很简单,只需要设置一下全局变量,然后就跳转到start_kernel去了
start_kernel()是"init/main.c"中的asmlinkage函数,至此,启动过程转入体系结构无关
中。
对于I386平台
在i386体系结构中,因为i386本身的问题,在"arch/alpha/kernel/head.S"中需要更多的设
是通过callSYMBOL_NAME(start_kernel)转到start_kernel()这个体系结构无关的函数中去
所不同的是,在i386系统中,当内核以bzImage的形式压缩,即大内核方式(__BIG_KERNEL__
需要预先处理bootsect.S和setup.S,按照大核模式使用¥(CPP)处理生成bbootsect.S和bsetup.S
编译生成相应的.o文件,并使用"arch/i386/boot/compressed/build.c"生成的build工具,
(未压缩的,含kernel中的head.S代码)与"arch/i386/boot/compressed"下的head.S和misc.c起,其中的head.S代替了"arch/i386/kernel/head.S"的位置,由Bootloader引导执行(startup_32口),然后它调用misc.c中定义的decompress_kernel()函数,使用"lib/inflate.c"中定义
内核解压到0x100000,再转到其上执行"arch/i386/kernel/head.S"中的startup_32代码。
对于2.4.x版内核
没有变化。
三.核心数据结构初始化--内核引导第一部分
start_kernel()中调用了一系列初始化函数,以完成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())
创建uidtaskcountSLABcache("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()等待调
至此start_kernel()结束,基本的核心环境已经建立起来了。
对于I386平台
i386平台上的内核启动过程与此基本相同,所不同的主要是实现方式。
对于2.4.x版内核
2.4.x中变化比较大,但基本过程没变,变动的是各个数据结构的具体实现,比如Cache。
四.外设初始化--内核引导第二部分
init()函数作为核心线程,首先锁定内核(仅对SMP机器有效),然后调用do_basic_setup()
其驱动程序的加载和初始化。过程如下:
总线初始化(比如pci_init())
网络初始化(初始化网络数据结构,包括sk_init()、skb_init()和proto_init()三部分,在
中,将调用protocols结构中包含的所有协议的初始化过程,sock_init())
创建bdflush核心线程(bdflush()过程常驻核心空间,由核心唤醒来清理被写过的内存缓冲
()由kernel_thread()启动后,它将自己命名为kflushd)
创建kupdate核心线程(kupdate()过程常驻核心空间,由核心按时调度执行,将内存缓冲区
到磁盘中,更新的内容包括超级块和inode表)
设置并启动核心调页线程kswapd(为了防止kswapd启动时将版本信息输出到其他信息中间,
kswapd_setup()设置kswapd运行所要求的环境,然后再创建kswapd核心线程)
创建事件管理核心线程(start_context_thread()函数启动context_thread()过程,并重命名
设备初始化(包括并口parport_init()、字符设备chr_dev_init()、块设备blk_dev_init()
scsi_dev_init()、网络设备net_dev_init()、磁盘初始化及分区检查等等,device_setup()
执行文件格式设置(binfmt_setup())
启动任何使用__initcall标识的函数(方便核心开发者添加启动函数,do_initcalls())
文件系统初始化(filesystem_setup())
安装root文件系统(mount_root())
至此do_basic_setup()函数返回init(),在释放启动内存段(free_initmem())并给内核解
()打开/dev/console设备,重定向stdin、stdout和stderr到控制台,最后,搜索文件系统中的(或者由init=命令行参数指定的程序),并使用execve()系统调用加载执行init程序。
init()函数到此结束,内核的引导部分也到此结束了,这个由start_kernel()创建的第一个
一个用户模式下的进程了。此时系统中存在着六个运行实体:
start_kernel()本身所在的执行体,这其实是一个"手工"创建的线程,它在创建了init()线
cpu_idle()循环了,它不会在进程(线程)列表中出现嵌入式linux培训视频
init线程,由start_kernel()创建,当前处于用户态,加载了init程序
kflushd核心线程,由init线程创建,在核心态运行bdflush()函数
kupdate核心线程,由init线程创建,在核心态运行kupdate()函数
kswapd核心线程,由init线程创建,在核心态运行kswapd()函数
keventd核心线程,由init线程创建,在核心态运行context_thread()函数
对于I386平台
基本相同。
对于2.4.x版内核
这一部分的启动过程在2.4.x内核中简化了不少,缺省的独立初始化过程只剩下网络(sock_init()建事件管理核心线程,而其他所需要的初始化都使用__initcall()宏包含在do_initcalls()
行。
五.init进程和inittab引导指令
init进程是系统所有进程的起点,内核在完成核内引导以后,即在本线程(进程)空间内加
它的进程号是1。
init程序需要读取/etc/inittab文件作为其行为指针,inittab是以行为单位的描述性(非执
每一个指令行都具有以下格式:
id:runlevel:action:process其中id为入口标识符,runlevel为运行级别,action为动作代
具体的执行程序。
id一般要求4个字符以内,对于getty或其他login程序项,要求id与tty的编号相同,否则getty
正常工作。
runlevel是init所处于的运行级别的标识,一般使用0-6以及S或s。0、1、6运行级别被系统
shutdown动作,1作为重启至单用户模式,6为重启;S和s意义相同,表示单用户模式,且无
件,因此也不在inittab中出现,实际上,进入单用户模式时,init直接在控制台(/dev/console行/sbin/sulogin。
在一般的系统实现中,都使用了2、3、4、5几个级别,在Redhat系统中,2表示无NFS支持的多用表示完全多用户模式(也是最常用的级别),4保留给用户自定义,5表示XDM图形登录方式。
可以使用的,传统的Unix系统没有定义这几个级别。runlevel可以是并列的多个值,以匹配多别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行。
initdefault是一个特殊的action值,用于标识缺省的启动级别;当init由核心激活以后,
inittab中的initdefault项,取得其中的runlevel,并作为当前的运行级别。如果没有inittab者其中没有initdefault项,init将在控制台上请求输入runlevel。
sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的runlevel,其余的(不含initdefault)都与某个runlevel相关。各个action的定义在inittab的man手册中有详细
在Redhat系统中,一般情况下inittab都会有如下几项:
id:3:initdefault:
#表示当前缺省运行级别为3--完全多任务模式;
si::sysinit:/etc/rc.d/rc.sysinit
#启动时自动执行/etc/rc.d/rc.sysinit脚本
l3:3:wait:/etc/rc.d/rc3
#当运行级别为3时,以3为参数运行/etc/rc.d/rc脚本,init将等待其返回
0:12345:respawn:/sbin/mingettytty0
#在1-5各个级别上以tty0为参数执行/sbin/mingetty程序,打开tty0终端用于
#用户登录,如果进程退出则再次运行mingetty程序
x:5:respawn:/usr/bin/X11/xdm-nodaemon
#在5级别上运行xdm程序,提供xdm图形方式登录界面,并在退出时重新执行
六.rc启动脚本
上一节已经提到init进程将启动运行rc脚本,这一节将介绍rc脚本具体的工作。
一般情况下,rc启动脚本都位于/etc/rc.d目录下,rc.sysinit中最常见的动作就是激活交换盘,加载硬件模块,这些动作无论哪个运行级别都是需要优先执行的。仅当rc.sysinit执行完以后会执行其他的boot或bootwait动作。
如果没有其他boot、bootwait动作,在运行级别3下,/etc/rc.d/rc将会得到执行,命令行参数
行/etc/rc.d/rc3.d/目录下的所有文件。rc3.d下的文件都是指向/etc/rc.d/init.d/目录下各
的符号连接,而这些脚本一般能接受start、stop、restart、status等参数。rc脚本以start
以S开头的脚本,在此之前,如果相应的脚本也存在K打头的链接,而且已经处于运行态了
(以/var/lock/subsys/下的文件作为标志),则将首先启动K开头的脚本,以stop作为参数
启动了的服务,然后再重新运行。显然,这样做的直接目的就是当init改变运行级别时,所有相都将重启,即使是同一个级别。
rc程序执行完毕后,系统环境已经设置好了,下面就该用户登录系统了。
七.getty和login
在rc返回后,init将得到控制,并启动mingetty(见第五节)。mingetty是getty的简化,不能作。getty的功能一般包括:
打开终端线,并设置模式
输出登录界面及提示,接受用户名的输入
以该用户名作为login的参数,加载login程序
缺省的登录提示记录在/etc/issue文件中,但每次启动,一般都会由rc.local脚本根据系统环
成。
注:用于远程登录的提示信息位于/etc/issue.net中。
login程序在getty的同一个进程空间中运行,接受getty传来的用户名参数作为登录的用户名
如果用户名不是root,且存在/etc/nologin文件,login将输出nologin文件的内容,然后退出
来系统维护时防止非root用户登录。
只有/etc/securetty中登记了的终端才允许root用户登录,如果不存在这个文件,则root可以在任何上登录。/etc/usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制当用户登录通过了这些检查后,login将搜索/etc/passwd文件(必要时搜索/etc/shadow文件
密码、设置主目录和加载shell。如果没有指定主目录,将默认为根目录;如果没有指定shell
为/bin/sh。在将控制转交给shell以前,getty将输出/var/log/lastlog中记录的上次登录
然后检查用户是否有新邮件(/usr/spool/mail/{username})。在设置好shell的uid、gid,
PATH等环境变量以后,进程加载shell,login的任务也就完成了。
八.bash
运行级别3下的用户login以后,将启动一个用户指定的shell,以下以/bin/bash为例继续我
程。
bash是BourneShell的GNU扩展,除了继承了sh的所有特点以外,还增加了很多特性和功能。
的bash是作为一个登录shell启动的,它继承了getty设置的TERM、PATH等环境变量,其中PATH
户为"/bin:/usr/bin:/usr/local/bin",对于root为"/sbin:/bin:/usr/sbin:/usr/bin"。
shell,它将首先寻/etc/profile脚本文件,并执行它;然后如果存在 ̄/.bash_profile,
则执行 ̄/.bash_login,如果该文件也不存在,则执行 ̄/.profile文件。然后bash将作为一
执行 ̄/.bashrc文件(如果存在的话),很多系统中, ̄/.bashrc都将启动/etc/bashrc作为
配置文件。
当显示出命令行提示符的时候,整个启动过程就结束了。此时的系统,运行着内核,运行着
程,运行着init进程,运行着一批由rc启动脚本激活的守护进程(如inetd等),运行着一
户的命令解释器。
附:XDM方式登录
如果缺省运行级别设为5,则系统中不光有1-6个getty监听着文本终端,还有启动了一个XDM
口。登录过程和文本方式差不多,也需要提供用户名和口令,XDM的配置文件缺省
为/usr/X11R6/lib/X11/xdm/xdm-config文件,其中指定了/usr/X11R6/lib/X11/xdm/xsession
会话描述脚本。登录成功后,XDM将执行这个脚本以运行一个会话管理器,比如gnome-session
除了XDM以外,不同的窗口管理系统(如KDE和GNOME)都提供了一个XDM的替代品,如gdm和kdm
的功能和XDM都差不多。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。