精通Linux设备驱动程序开发第2章-内核一瞥

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第2章内核一瞥在我们开始步入Linux设备驱动的神秘世界之前,让我们先熟悉一些从驱动开发人员应该理解的基本的内核概念。我们将学习到内核定时器、同步机制以及内存分配方法,但是,先让我们从顶层视角开始探索,扫描一下内核发出的启动信息,并在感兴趣的地方设置停下来看一看。启动过程图2.1显示了基于x86计算机Linux系统的启动顺序。第一步是BIOS从启动设备中导入主引导记录(MBR),接下来MBR中的代码查看分区表并从活动分区读取GRUB、LILO或SYSLINUX等bootloader,之后bootloader会加载压缩后的内核映像并将控制权传递给它。内核取得控制权后,会将自身解压缩并投入运转。图2.1基于x86的硬件上Linux的启动过程基于x86的处理器有两种操作模式:实模式和保护模式。在实模式下,用户仅可以使用1MB内存,并且没有任何保护。保护模式则更加复杂,用户可以使用更多的高级功能(如分页)。CPU提供了一条由实模式通向保护模式的道路,但是,这条路只允许单向行驶,用户不能从保护模式再切换回实模式。内核初始化的第一步是执行实模式下的汇编代码,之后执行保护模式下init/main.c文件(上一章我们修改了这个文件)中的start_kernel()函数。start_kernel()函数首先会初始化CPU子系统,之后让内存管理和进程管理系统就位,接下来启动外部总线和I/O设备,昀后的一步是激活所有Linux进程的父亲init。init执行用户空间的脚本以启动必要的内核服务,它昀终派生控制台终端程序并显示登录(login)提示。接下来,每一小节的标题都是图2.2中的一条打印信息,这些信息来源于基于x86的笔记本电脑的Linux启动过程。如果你在启动体系结构上启动Linux,消息以及语义可能会有所改变。如果本节中的一些内容读起来非常晦涩,请不要担心。目前的目的仅是从100英尺的高度给你一个视图,让你初次品尝内核甜点的味道。接下来要提到的许多概念都会在以后的章节中进行更深的论述。图2.2内核启动信息Linuxversion2.6.23.1y(root@localhost.localdomain)(gccversion4.1.120061011(RedHat4.1.1-30))#7SMPPREEMPTThuNov111:39:30IST2007BIOS-providedphysicalRAMmap:BIOS-e820:0000000000000000-000000000009f000(usable)BIOS-e820:000000000009f000-00000000000a0000(reserved)...758MBLOWMEMavailable....Kernelcommandline:roroot=/dev/hda1...Console:colourVGA+80x25...Calibratingdelayusingtimerspecificroutine..1197.46BogoMIPS(lpj=2394935)...CPU:L1Icache:32K,L1Dcache:32KCPU:L2cache:1024K...Checking'hlt'instruction...OK....SettingupstandardPCIresources...NET:Registeredprotocolfamily2IProutecachehashtableentries:32768(order:5,131072bytes)TCPestablishedhashtableentries:131072(order:9,2097152bytes)...checkingifimageisinitramfs...itisFreeinginitrdmemory:387kfreed...ioschedulernoopregisteredioscheduleranticipatoryregistered(default)...00:0a:ttyS0atI/O0x3f8(irq=4)isaNS16550A...UniformMulti-PlatformE-IDEdriverRevision:7.00alpha2ide:Assuming33MHzsystembusspeedforPIOmodes;overridewithidebus=xxICH4:IDEcontrolleratPCIslot0000:00:1f.1ProbingIDEinterfaceide0...hda:HTS541010G9AT00,ATADISKdrivehdc:HL-DT-STCD-RW/DVDDRIVEGCC-4241N,ATAPICD/DVD-ROMdrive...serio:i8042KBDportat0x60,0x64irq1mice:PS/2mousedevicecommonforallmice...SynapticsTouchpad,model:1,fw:5.9,id:0x2c6ab1,caps:0x884793/0x0...agpgart:DetectedanIntel855GMChipset....Intel(R)PRO/1000NetworkDriver-version7.3.20-k2...ehci_hcd0000:00:1d.7:EHCIHostController...Yenta:CardBusbridgefoundat0000:02:00.0[1014:0560]...Non-volatilememorydriverv1.2...kjournaldstarting.Commitinterval5secondsEXT3FSonhda2,internaljournalEXT3-fs:mountedfilesystemwithordereddatamode....INIT:version2.85booting...BIOS-providedphysicalRAMmap内核解析从BIOS中读取到的系统内存映射,并率先将这些信息打印出来:BIOS-providedphysicalRAMmap:BIOS-e820:0000000000000000-000000000009f000(usable)...BIOS-e820:00000000ff800000-0000000100000000(reserved)实模式下的初始化代码通过使用BIOS的int0x15服务并执行0xe820号函数来获得系统的内存映射信息。内存映射信息中包含了预留的和可用的内存,内核将使用这些信息创建其可用的内存池。在附录B《Linux和BIOS》的《实模式调用》一节,我们会对BIOS提供的内存映射问题进行更深入的讲解。758MBLOWMEMAvailable896MB以内的常规的可被寻址的内存区域被称作低端内存。内存分配函数kmalloc()就是从该区域分配内存的。高于896MB被称为高端内存,只有在采用特殊的方式进行映射后才能被访问。在启动过程中,内核会计算并显示这些内存zone内总的页数,在本章的稍后,会对这些内存zone进行更深入的分析。KernelCommandLine:roroot=/dev/hda1Linux的bootloader通常会给内核传递一个命令行。命令行中的参数类似于传递给C程序中main()函数的argv[]列表,唯一的不同是它们是传递给内核的。你可以在bootloader的配置文件中增加命令行参数,当然,也可以在运行过程中对bootloader的提示行进行修改[1]。如果你正在使用GRUB这个bootloader,归因于发行版的不同,其配置文件可能是/boot/grub/grub.conf或者是/boot/grub/menu.lst。如果你正在使用LILO,配置文件为/etc/lilo.conf。下面给出了一个grub.conf文件的例子(增加了一些注释),阅读了紧接着“titlekernel2.6.23”后的一行之后,你会发现前述打印信息的由来。\[1]嵌入式设备上的bootloader通常经过了“瘦身”,并不支持配置文件或类似机制。归因于此,许多非x86体系结构提供了CONFIG_CMDLINE这个内核配置选项,通过它,用户可以在编译内核时提供内核命令行。default0#Bootthe2.6.23kernelbydefaulttimeout5#5secondtoalterbootorderorparameterstitlekernel2.6.23#BootOption1#Thebootimageresidesinthefirstpartitionofthefirstdisk#underthe/boot/directoryandisnamedvmlinuz-2.6.23.'ro'#indicatesthattherootpartitionshouldbemountedread-only.kernel(hd0,0)/boot/vmlinuz-2.6.23roroot=/dev/hda1#LookundersectionFreeinginitrdmemory:387kfreedinitrd(hd0,0)/boot/initrd#...命令行参数将影响启动过程中的代码执行路径。举一个例子,假设某命令行参数为bootmode,如果该参数被设置为1,意味着你希望在启动过程中打印一些调试信息并在启动结束时切换到runlevel的第3级(到我们分析init进程的打印信息时,会学习到runlevel的含义);如果bootmode参数被设置为0,意味着你希望启动过程相对简洁,并且设置runlevel为2。因为你已经熟悉了init/main.c文件,让我们在该文件中增加如下修改:staticunsignedintbootmode=1;staticint__initis_bootmode_setup(char*str){get_option(&str,&bootmode);return1;}/*Handleparameterbootmode=*/__setup(bootmode=,is_bootmode_setup);if(bootmode){/*Printverboseoutput*//*...*/}/*...*//*Ifbootmodeis1,chooseaninitrunlevelof3,elseswitchtoarunlevelof2*/if(bootmode){argv_init[++args]=3;}else{argv_init[++args]=2;}/*...*/请重新编译内核并尝试新的修改。另外,本书第18章《嵌入式Linux》的《内存分布》一节也将对命令行参数进行更多的讲解。CalibratingDelay...1197.46BogoMIPS(lpj=2394935)在启动过程中,内核会计算处理器在一个jiffy时间内运行一个内部的delay循环的次数。jiffy的含义是系统定时器2个连续的节拍之间的间隔。如果你所期待的那样,该计算必须被校准到你的CPU的处理速度。校准的结果被存储在称为loops_per_jiffy的内核变量中。使用loops_per_jiffy的一个场合是某设备驱动希望进行小的微妙级别的延迟的时候。为了理解delay循环校准代码,让我们看一下定义于init/calibrate.c文件中的calibrate_delay()函数。该函数机智地使用整型运算得到了浮点的精度。如下的代码片段(增加了一些注释)显示了该函数的开始部分,这部分用于得到一个粗略的loops_per_jiffy:loops_per_jiffy=(112);/*Initialapproximation=4096*/printk(KERN_DEBUGCalibratingdelayloop...);while((loops_per_jiffy=1)!=0){ticks=jiff

1 / 47
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功