嵌入式Linux系统

更新时间:2024-11-14 10:51:12 阅读: 评论:0


2022年8月1日发
(作者:价格法)

10-1嵌入式Linux系统概述

嵌入式系统是以应用为中心,以计算机技术为基础,软硬件可裁剪,适用于应用系统,

对功能、可靠性、成本、体积、功耗等方面有特殊要求的专用计算机系统。

Linux在所有的操作系统中,Linux是一个发展最快、应用最为广泛的操作系统。

所谓嵌入式Linux,是指Linux在嵌入式系统中应用,而不是什么嵌入式功能。实际上,

嵌入式Linux和Linux是同一件事。

10-2Linux启动过程综述

一.Bootloader

二.Kernel引导入口

三.核心数据结构初始化--内核引导第一部分

四.外设初始化--内核引导第二部分

五.init进程和inittab引导指令

六.rc启动脚本

七.getty和login

八.bash

附:XDM方式登录

Bootloader

简单地说,BootLoader就是在操作系统内核运行之前运行的一段小程序。通过这段小

程序,我们可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一

个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并

没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因

此整个系统的加载启动任务就完全由BootLoader来完成。

在Alpha/AXP平台上引导Linux通常有两种方法,一种是由MILO及其他类似的引导程序引

导,另一种是由Firmware直接引导。MILO功能与i386平台的LILO相近,但内置有基本的

磁盘驱动程序(如IDE、SCSI等),以及常见的文件系统驱动程序(如ext2,iso9660等),

firmware有ARC、SRM两种形式,ARC具有类BIOS界面,甚至还有多重引导的设置;而SRM

则具有功能强大的命令行界面,用户可以在控制台上使用boot等命令引导系统。ARC有分

区(Partition)的概念,因此可以访问到分区的首扇区;而SRM只能将控制转给磁盘的首扇

区。两种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环境),调用load()将核心代码加载到START_ADDR(见

“include/asm-alpha/system.h”),再将Firmware中的核心引导参数加载到ZERO_PAGE(0)

中,最后调用runkernel()将控制转给0x100000的kernel,bootloader部分结束。

Bootloader中使用的所有“srm_”函数在“arch/alpha/lib/”中定义。

对于I386平台

i386系统中一般都有BIOS做最初的引导工作,那就是将四个主分区表中的第一个可引

导分区的第一个扇区加载到实模式地址0x7c00上,然后将控制转交给它。

在“arch/i386/boot”目录下,bootsect.S是生成引导扇区的汇编源码,它首先将自

己拷贝到0x90000上,然后将紧接其后的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中的代码包含进来,检测和设置显示器和显示模式。

最后,setup.S将系统转换到保护模式,并跳转到0x100000(对于bzImage格式的大内核是

0x100000,对于zImage格式的是0x1000)的内核引导代码,Bootloader过程结束。

对于2.4.x版内核

没有什么变化。

Kernel引导入口

Kernel操作系统内核操作系统内核是指大多数操作系统的核心部分。它由操作系统中用

于管理存储器、文件、外设和系统资源的那些部分组成。操作系统内核通常运行进程,并

提供进程间的通信。核心功能:1.事件的调度和同步。2.进程间的通信(消息传递)。3.

存储器管理。4.进程管理。目前比较常用的Linux内核版本有2.4和2.6&3.0(为最新

版本)。

Linux2.4内核源码目录:

arch包括所有和体系结构相关的核心代码。

include包括编译内核所需要的大部分头文件

init包含内核的初始化代码(不是系统的引导代码),有main.c和

Version.c两个文件

mm包含所有的内存管理代码

drivers包含系统中所有的设备驱动程序

ipc包含核心进程间的通信代码

fs存放Linux支持的文件系统代码

kernel包含内核管理的核心代码

net内核的网络部分代码,其每个子目录对应于网络的一个方面

lib包含核心的库代码

scripts包含用于配置核心的脚本文件

Documentation一些文档,是对每个目录作用的具体说明

查看Linux内核版本命令:uname-a或者cat/proc/version

内核编译命令:

makemenuconfig配置编译选项

makedep提供变量依赖关系信息

makeclean删除生成的模块和目标文件

makezImage编译内核生成压缩的映像

makemodules编译模块

makemodules_install安装编译完成的模块

对于I386平台

在i386体系结构中,因为i386本身的问题,在"arch/alpha/kernel/head.S"中需要更

多的设置,但最终也是通过callSYMBOL_AME(start_kernel)转到start_kernel()这个体

系结构无关的函数中去执行了。

所不同的是,在i386系统中,当内核以bzImage的形式压缩,即大内核方式

(__BIG_KEREL__)压缩时就需要预先处理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"中定义的gunzip()将内核解压到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()等

待调度,init())

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

对于I386平台

i386平台上的内核启动过程与此基本相同,所不同的主要是实现方式。

对于2.4.x版内核

2.4.x中变化比较大,但基本过程没变,变动的是各个数据结构的具体实现,比如Cache。

外设初始化--内核引导第二部分

init()函数作为核心线程,首先锁定内核(仅对SMP机器有效),然后调用

do_basic_setup()完成外设及其驱动程序的加载和初始化。过程如下:

总线初始化(比如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())

至此do_basic_setup()函数返回init(),在释放启动内存段(free_initmem())并给内

核解锁以后,init()打开/dev/console设备,重定向stdin、stdout和stderr到控制台,

最后,搜索文件系统中的init程序(或者由init=命令行参数指定的程序),并使用execve()

系统调用加载执行init程序。

init()函数到此结束,内核的引导部分也到此结束了,这个由start_kernel()创建的

第一个线程已经成为一个用户模式下的进程了。此时系统中存在着六个运行实体:

start_kernel()本身所在的执行体,这其实是一个"手工"创建的线程,它在创建了

init()线程以后就进入cpu_idle()循环了,它不会在进程(线程)列表中出现

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进程,它是内核启动的第一个用户级进程。init有许多很重要的任务,比如

象启动getty(用于用户登录)、实现运行级别、以及处理孤立进程。

对于Linux系统的运行来说,init程序是最基本的程序之一。但你仍可以大部分的忽

略它。一个好的Linux发行版本通常随带有一个init的配置,这个配置适合于绝大多

数系统的工作,在这样一些系统上不需要对init做任何事。通常,只有你在碰到诸如

串行终端挂住了、拨入(不是拨出)调制解调器、或者你希望改变缺省的运行级别时

你才需要关心。init进程是系统所有进程的起点,内核在完成核内引导以后,即在本线程

(进程)空间内加载init程序,它的进程号是1。inittab为linux初始化文件系统时init

初始化程序用到的配置文件。这个文件负责设置init初始化程序初始化脚本在哪里;

每个运行级初始化时运行的命令;开机、关机、重启对应的命令;各运行级登陆时所

运行的命令。

init程序需要读取/etc/inittab文件作为其行为指针,inittab是以行为单位的描述

性(非执行性)文本,每一个指令行都具有以下格式:

id:runlevel:action:process其中id为入口标识符,runlevel为运行级别,action

为动作代号,process为具体的执行程序。

id一般要求4个字符以内,对于getty或其他login程序项,要求id与tty的编号相

同,否则getty程序将不能正常工作。

runlevel是init所处于的运行级别的标识,一般使用0-6以及S或s。0、1、6运行

级别被系统保留,0作为shutdown动作,1作为重启至单用户模式,6为重启;S和s意义相

同,表示单用户模式,且无需inittab文件,因此也不在inittab中出现,实际上,进入单

用户模式时,init直接在控制台(/dev/console)上运行/sbin/sulogin。

在一般的系统实现中,都使用了2、3、4、5几个级别,在Redhat系统中,2表示无FS

支持的多用户模式,3表示完全多用户模式(也是最常用的级别),4保留给用户自定义,5

表示XDM图形登录方式。7-9级别也是可以使用的,传统的Unix系统没有定义这几个级别。

runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel

与当前运行级别匹配成功才会执行。

initdefault是一个特殊的action值,用于标识缺省的启动级别;当init由核心激活

以后,它将读取inittab中的initdefault项,取得其中的runlevel,并作为当前的运行

级别。如果没有inittab文件,或者其中没有initdefault项,init将在控制台上请求输

入runlevel。

sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的

runlevel,其余的action(不含initdefault)都与某个runlevel相关。各个action的定义

在inittab的man手册中有详细的描述。

在Redhat系统中,一般情况下inittab都会有如下几项:

id:3:initdefault:

#表示当前缺省运行级别为3--完全多任务模式;

si::sysinit:/etc/rc.d/t

#启动时自动执行/etc/rc.d/t脚本

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启动脚本

什么是脚本?其实开机脚本并没有很官方的定义,它通常指的是开机运行一个批处理或者

VBS脚本,来解决一些不需要重启就可以生效的问题,来实现一些不需要重启就可以生效的

功能。

在init进程中将启动运行rc脚本,rc脚本具体的工作方法如下。

一般情况下,rc启动脚本都位于/etc/rc.d目录下,t中最常见的动作就是

激活交换分区,检查磁盘,加载硬件模块,这些动作无论哪个运行级别都是需要优先执行的。

仅当t执行完以后init才会执行其他的boot或bootwait动作。

如果没有其他boot、bootwait动作,在运行级别3下,/etc/rc.d/rc将会得到执行,

命令行参数为3,即执行/etc/rc.d/rc3.d/目录下的所有文件。rc3.d下的文件都是指向

/etc/rc.d/init.d/目录下各个Shell脚本的符号连接,而这些脚本一般能接受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/中。

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,以及TERM,PATH等环境变

量以后,进程加载shell,login的任务也就完成了。

bash

运行级别3下的用户login以后,将启动一个用户指定的shell,以下以/bin/bash为

例继续我们的启动过程。

bash是BourneShell的GU扩展,除了继承了sh的所有特点以外,还增加了很多特

性和功能。由login启动的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将作为一个交互式shell执行

~/.bashrc文件(如果存在的话),很多系统中,~/.bashrc都将启动/etc/bashrc作为系统

范围内的配置文件。

当显示出命令行提示符的时候,整个启动过程就结束了。此时的系统,运行着内核,运

行着几个核心线程,运行着init进程,运行着一批由rc启动脚本激活的守护进程(如inetd

等),运行着一个bash作为用户的命令解释器。

附:XDM方式登录

如果缺省运行级别设为5,则系统中不光有1-6个getty监听着文本终端,还有启动了

一个XDM的图形登录窗口。登录过程和文本方式差不多,也需要提供用户名和口令,XDM的

配置文件缺省为/usr/X11R6/lib/X11/xdm/xdm-config文件,其中指定了

/usr/X11R6/lib/X11/xdm/xsession作为XDM的会话描述脚本。登录成功后,XDM将执行这

个脚本以运行一个会话管理器,比如gnome-session等。

除了XDM以外,不同的窗口管理系统(如KDE和GOME)都提供了一个XDM的替代品,如

gdm和kdm,这些程序的功能和XDM都差不多。

参考文献:1.嵌入式系统概述(中嵌教育-嵌入式linux开发课件)

2.百度百科/view/

3/来自道客巴巴

4Linux从开机到登录启动过程综述/linux/tips/2_

5/来自道客巴巴

:///view/百度百科

:///view/百度百科

8/view/百度百科

.


本文发布于:2022-08-01 16:32:48,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/falv/fa/83/50876.html

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

留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 站长QQ:55-9-10-26