下载
加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 Linux内核学习起步

Linux内核学习起步.pdf

Linux内核学习起步

三甲居士
2011-12-09 0人阅读 举报 0 0 暂无简介

简介:本文档为《Linux内核学习起步pdf》,可适用于IT/计算机领域

Linux内核学习起步albcamus<albcamusgmailcom>LastUpdate:年月日,编译内核=========================获取内核压缩在http:wwwkernelorgpublinuxkernelv能看到一系列的文件如ChangeLogxx、linuxxxtar{gz|bz}和patchxx{gz|bz}你应该下载linuxtarbz这样的压缩包。找一个旧的config作参考安装内核开发包(以FC为例是kerneldevelfcirpm)则libmodules<version>build目录下会有config文件。#cdlinuxrc#cplibmodulesfcbuildconfig配置、编译和安装配置:#makemenuconfiggconfigxconfigoldconfigdefconfigallyesconfigallmodconfigFYI:一般用menuconfig比较多如果你喜欢GUI风格也可以选择基于Qt的xconfig或基于Gtk的gconfig。FYI:可以用O=<pathtobuild>指定编译生成的文件放在哪个目录举例来说如果我的内核源代码目录是usrsrclinux编译内核时指定了:makeO=homearcbuildlinux那么libmodules下的`source'和`build'这两个符号链接就分别指向了源代码和目标代码目录:#lsllibmodulessourcelrwxrwxrwxrootroot:libmodulessource>usrsrclinux#lsllibmodulesbuildlrwxrwxrwxrootroot:libmodulesbuild>homearcbuildlinux编译:#makeFYI:V=*verbose模式把每一部执行的命令都打印出来。*一个小技巧就是把标准输出重定向到一个文件中*这样以后查找模块之间的依赖关系就很方便了*C=*需要安装sparse程序进行严格的静态C语法检查。*一般开发者会通过它来预防BUG*安装:#makemodulesinstall#makeinstallFYI:正常情况下makeinstall会根据你机器的配置为你定制initrd文件并更新grubconf文件中的内容。但如果重新引导时发现无法启动注意手工mkinitrd。例如:#mkinitrdv–preloadlibatako–with=extbootinitrdrcimgrc其中preload指定的模块会在etcmodprobeconf之前加载而with指定的模块会在这之后加载。文档#makehtmldocs你也可以不用htmldocs指令HTML格式而指定pdfdocs或psdocs#makemandocs为kernelAPI生成man手册#makeinstallmandocs*将kernelAPI的手册页安装到man程序能找到的*目录中这样就可以mancopyfromuser了*FYI:执行makehtmldocspdfdocspsdocs之后在O=指定的目录(如果没使用O=则是源代码目录)的DocumentationDocBook下会生成几份很重要的文档:kernelapi:内核开发的API手册usb:USBhost端的API手册gadget:USBdevice端的API手册kernellocking:内核加锁的HOWTO文档kernelhacking:内核开发的一些注意事项FYI:内核源代码目录的Documentation目录:kernelparametertxt:内核参数加在一个grubentry的kernel指令后面filesystemsvfstxt:Linux虚拟文件系统的深入介绍memorybarriers:关于barriers的文档CodingStyle:内核编码的规范等等图menuconfig图xconfig图gconfig,浏览代码=====================vimctagscscope编译ctags需要的tags文件:#makeARCH=xtagsFYI:ARCH=<arch>的意思是除了指定的体系结构外不索引其他体系结构相关的代码FYI:之前用i指代位的x处理器用x指代位的x处理器。但在中这者即将合并统称为x编译cscope程序需要的cscopeout文件:#makeARCH=xcscope在Vim中用ctags浏览:tsdoforktagselect效果等同于光标停在dofork上按Ctrl。如果想返回原来的位置CtrlT:tntagnext:tptagprevious:tftagfirst:tltaglastFYI:安装Vim的taglist插件浏览更方便。又~vimrc中控制number:“addedbyalbcamusfunction!TlistWrapper()Tlistifwinnr('$')>="winnr()returnthenumberofcurrentwindow"whilewinnr('$')returnsthenumberofallwindowssetnonuelsesetnuendifendfunctioncommand!nargs=ListcallTlistWrapper()map<F>:List<CR>:<CR>则可以按F键来调用taglist。在Vim中用cscope浏览:csaddcscopeout#添加一个connection浏览内核时经常发现cscope链接断开可以调用:csacscopeout:csshow#显示所有connections:cskill<#>#杀死第#号connection第#号就是:csshow显示出来的:csreset#reset所有connections:csfindc|d|e|f|g|i|s|t<name>解释:c查找name被哪些函数调用d查找name调用了哪些函数eegrep句型f查找名为name的文件g查找name的定义i查找#include本name的文件s查找name这个C符号t查找何处对name赋值对Linux内核来说如果既有tags文件又有cscopeout文件则Vim中:setcst意味着使用cscope风格的^也就是:tag命令。于是:如果想用ctags找到doIRQ::tsdoIRQ如果想用cscope找到doIRQ::tadoIRQFYI:可以在~vimrc中自定义命令这样就不必每次都输入符号名:nmap<C>g:csfindg<CR>=expand("<cword>")<CR><CR>nmap<C>c:csfindc<CR>=expand("<cword>")<CR><CR>nmap<C>s:csfinds<CR>=expand("<cword>")<CR><CR>nmap<C>t:csfindt<CR>=expand("<cword>")<CR><CR>nmap<C>e:csfinde<CR>=expand("<cword>")<CR><CR>nmap<C>f:csfindf<CR>=expand("<cfile>")<CR><CR>nmap<C>i:csfindi^<CR>=expand("<cfile>")<CR>$<CR>nmap<C>d:csfindd<CR>=expand("<cword>")<CR><CR>这样当你在Vim中把光标挪到字符串dofork上按Ctrlc(先按Ctrl然后按c)cscope就会列出所有调用了dofork函数的地方。FYI:GNUGlobal程序也不错。对喜爱Emacs编辑器的朋友还可以用etags和xcscope来替代。喜欢SourceInsight的朋友也可以常识一下kscope它的界面和SI很类似。kscope依赖于KDE的库如果你用gnome作窗口管理器也得安装了KDE才能安装kscope。使用kscope之前需要安装ctags、cscope和dot程序(dot在graphviz包中)。kscope抓图:lxrLinuxCrossReferrence我一般都只在网上浏览:http:lxrlinuxnohttp:userssosdgorg~qiyonglxrsource内核相关的图书==================================入门推荐:LKD《Linux内核设计与实现第版》从入门开始介绍了诸如中断、系统调用、虚拟文件系统、同步与互斥、内存管理、进程控制等方面内容比较浅显易懂是入门的好书。优点:适合入门(个人认为没有比LKD更优秀的内核入门图书)缺点:内容不够深入覆盖面不广。(对高手来说估计就像休闲材料)ULK深入理解Linux内核rd一本很全面的Linux内核原理书。书中以为示例版本着重讲述了x平台的Linux内核实现。优点:深入全面缺点:NA我觉得看完ULK就是高手了:)而且由于书中着重介绍了X体系结构也称得上半个x专家了。《Linux内核源代码情景分析》以为例讲解注重代码级别的剖析对中断、内存管理、文件系统、SMP、PCI和USB、IPC的讲解都是代码级别的深入细致。优点:深入缺点:针对的内核版本较旧且每个「情景」都很长不容易坚持读完。FYI:新手不要从《情景分析》开始学习内核这样只会增长你的学习周期。LDDLinux设备驱动程序rdLDD写的很精彩。但如果要学会写具体的驱动程序还是得参照硬件的datasheet读一个内核中现成的驱动程序。FYI:内核中自带的驱动程序skeleton:driversnetpciskeletonc和driversusbusbskeletonc分别是为PCIUSB驱动程序员提供的参考代码。现代体系结构上的Unix系统内核程序员的SMP和Caching技术这本书着重讲解各种体系结构上的Unix实现注意事项egSMP的同步与互斥、Cache一致性问题。优点:作者知识面非常广原理讲得很清楚。缺点:年的书比较旧IntelAMDCPU参考手册最好带着问题有针对性的去读IntelAMD的手册。,第一个模块:HelloKernel============================代码#catMakefileifneq($(KERNELRELEASE),)objm:=hellooelseKBUILD:=libmodules`unamer`buildmodules:makeC$(KBUILD)M=$(shellpwd)modulesclean:rmf*o*~coredepend*cmd*ko*modcrmrftmpversionsendif#cathelloc#include<linuxkernelh>#include<linuxmoduleh>#include<linuxinith>MODULELICENSE("GPL")MODULEDESCRIPTION("Thehelloworlddemomodule")MODULEAUTHOR("albcamus<albcamusgmailcom>")staticintinithelloinit(void){printk("HelloKernel!n")return}staticvoidexithelloexit(void){printk("Bye!n")}moduleinit(helloinit)moduleexit(helloexit)编译、加载和查看编译:#make加载#insmodhelloko查看:#dmesg|tailHelloKernel!卸载:#rmmodhelloFYI:接下来做什么?在Helloworld之后有很多可以练习的小模块可以做例如截获系统调用表(这个最好只拿来玩)写一个dummy字符设备驱动程序添加一个系统调用等等。(TBD:如果有时间就讲interceptexecve模块),内核相关的网络资源==========================================http:wwwkernelorggittreesbugzillaLKML(和其他的mailinglist)FYI:两个新闻组composlinuxdevelopmentsystem(讨论linux内核程序设计),和falinuxkernel(LKML的USENET版)新闻组可以用evolutionthunderbird等订阅也可以在http:groupsgooglecom访问。http:kerneltraporgLinux和BSD内核的技术新闻。如果没时间跟踪LKML那么经常浏览kerneltrap是个好主意。http:lwnnetLinuxweeklynewshttp:wwwlinuxforumnet从年开始积累了大量的高质量内核技术文档资源。http:linuxchinaunixnet积累壮大中。有很多高手活跃如zxwing、puppylove、daemeon等。http:wwwibmcomdeveloperworkscnviewslinuxarticlesjspviewby=searchsearchby=EEABibmdevelopersworks网站有很多关于内核的文档大都写得通俗易懂。http:zhkernelorgmailmanlistinfolinuxkernel由LiYangfreescale维护活跃着很多在Kernel开发领域活跃的华人如HerbertXu,MingmingCao,BryanWu,ShaohuaLi,YanminZhang等都有参与此外还有各个社区的高手如CLF的wheelz、daemeon、zhllg等。该社区有翻译的内核文档还有IntelOTC的LinuxACPI项目。,其他的知识=========================在学习Linux内核时我们经常发现很多内核之外的知识要掌握否则就会成为学习之路上的绊脚石。大体上来说各个领域都有它的特定知识需要掌握例如要学习Linux内核的网络协议栈实现就不可能不学习TCPIP要学习Linux是如何管理物理内存就不可能不学习某个CPU的内存管理机制。等等。这里只说一点:GCC的C扩展和inlineASM。GCC的C扩展:$infogcc'ce'INLINEASM(针对i):《情景分析》第一章讲解inlineASM比较透彻。另外有两篇很好的文档:http:wwwibiblioorggfergldpGCCInlineAssemblyHOWTOhtmlhttp:groupsgooglecomgroupmuclistslinuxkernelbrowsethreadthreadcadddddb针对x的inline汇编和i区别不大。,内核调试=====================printk朴素的printk永远是最受欢迎的。缺点:有时BUG导致直接reboot了printk的内容甚至都看不到。不过没关系我们可以用console=devttyS把console的输出定向到串口在另一端接收也可以用netconsole(详见Documentationnetworkingnetconsoletxt)优点:随时随地可用。FYI:使用GCC或C的可变参数宏包裹kprintkC方式:=======#defineMYDEBUG#ifdefMYDEBUG#definedbgprint(fmt,)printk("sd:"fmt,FILE,LINE,##VAARGS)#else#definedbgprint(fmt,)do{}while()#endifGCC方式:=======#defineMYDEBUG#ifdefMYDEBUG#definedbgprint(fmt,args)printk("sd:"fmt,FILE,LINE,##args)#else#definedbgprint(fmt,args)do{}while()#endif然后就可以使用:dbgprint("Thisisadebugmessge,i=d,andstringisdn",i,str)kdb由SGI公司开发的一系列补丁。http:osssgicomprojectskdb优点:更新很快基本能和官方内核同步。稳定。缺点:只能做到汇编级别的源代码单步不能做到C代码单步。kgdblinsys公司开发的一系列补丁。Linux内核维护者AndrewMorton就使用它。优点:C源代码级别的单步。缺点:BUG较多需要串口两台机器。免费版本支持的内核版本很少。FYI:这个问题可以规避:a),VMWare中可以指定虚拟机的串口为FIFO文件从而两台虚拟机实现"串口"互联b),较新的kgdb已经支持kgdboeOverEthernet只要以太网卡驱动程序支持NETPOLL即可。FYI:kgdb即将被合并到官方内核中不过最早也得是FYI:比linsys和sourceforge要全的一个地方:http:wwwkernelorgpublinuxkernelpeopleagkpatches另AndrewMorton的mmtree中自带kgdb:http:wwweukernelorgpublinuxkernelpeopleakpmpatcheskprobessystemtapkprobes自加入到官方内核中。systemtap是由redhat和Intel等公司开发的调试工具类似于Solaris的DTrace。它基于kprobes。kprobes优点:几乎内核中的任何地方都可以插入监测点而且可以使用它来截获那些不导出的符号(只要在kallsyms中有)缺点:NAsystemtap:下载:gitclonegit:sourcesredhatcomgitsystemtapgit优点:用户态功能强大缺点:需要学习一门新的语言(stap)文档:主页:http:sourcewareorgsystemtapdocumentationhtml使用systemtap调试内核:http:wwwibmcomdeveloperworkscnlinuxlsystemtapLinux下的一个全新的性能测量和调试诊断工具systemtap第部分:http:wwwibmcomdeveloperworkscnlinuxlcnsystemtapVMWareworkstationxVMWareworkstation在VCPU中实现了gdbstub优点:简单(不需要串口、不需要多台机器)C源代码单步缺点:并不针对Linux系统不能针对线程栈做分析。学习意义大于工程意义。文档:http:blogchinaunixnetushowartphpid=图:WS,现代硬件:ARCHDRIVER=========================================================现代XCPUIntelandIACPUManuals:http:wwwintelcomproductsprocessormanualsindexhtmAMDCPUManuals:http:developeramdcomdevguidesjsp一些Linux支持(或需要更好地支持)的现代XCPU的特性:多处理器:SMP,同步和互斥APIC内存管理:PAE,PSE,MTRRPAT,CacheTLB,x,NUMA系统调用:intx,sysentersysexit,syscallsysret电源管理:ACPI>电源管理>MPS>PCIIRQRouting>PNP>E扩展指令:MMX,SSESSESSESSE,DNow!安 全 :NX虚拟机:VMX(IntelVT)SVM(AMDV)外围设备外围设备资料建议下载IntelICH南桥芯片的datasheet:http:wwwintelcomdesignchipsetsdatashtshtm南桥芯片的datasheet会囊括hostcontrollers手册。当然具体到某种设备例如USB或SATA还需要看相关的规范。,的分段机制=====================================为段映射提供的硬件oGDT表LDT表TSS都在内存中。(Linux的实现是:这个东西都是每个CPU有一个)o每个CPU中都有一个gdtr指向GDT表一个ldtr指向LDT表一个tr指向TSS。o每个CPU有个段寄存器:CSDSSSESFSGS。它们都是bit的。其中CS、DS分别指向代码和数据段SS指向堆栈段都是特殊的而ES、FS、GS可以用作普通目的指向哪个datasegment都成。这些段寄存器叫作"SegmentSelector"。亦即:由它的值决定一个地址(位的segmentselector加上位的线性地址)采用GDT或LDT中的哪个entry–每个entry都代表一个"Segment"所以一个entry就是一个"SegmentDescriptor"。o一个segmentselector是这样的:||||INDEX|TI|RPL|RPL:RequestPrivilegeLevel。CS寄存器的:就叫CPLcurrentprivilegelevel。TI:TableIndicator。TI==时表示使用GDT表TI==时使用LDT表。INDEX:在GDT或LDT表中的位置。例如TI==,使用GDT表。INDEX为gdtr==x而GDT表每个entry的大小为字节(见下条)于是会找到:x(*)==x这个地址就是GDT表中相应的segmentdescriptor在内存中的地址。oGDT表的一个entry代表一个segmentdescriptor长度为字节(bit)格式为:(参考ULKPIntel手册VFigure和Linux内核的structdescstruct定义)TableAAccessedAVLAvailabletoSystemprogrammerBBigCConformingDDefaultDPLDescriptorPriviligeLevelEExpansionDirectionGGranularityRReadableLIMITSegmentLimitWWritablePPresentLinux内核对这个entry格式的定义:structdescstruct{ulimitubaseunsignedbase:,type:,s:,dpl:,p:unsignedlimit:,avl:,l:,d:,g:,base:}attribute((packed))注意对Code段来说limit由Limit和G决定对Data段和Stack段来说Limit还依赖于B和E位。但Linux根本不管B和E位。其中代码段描述符、数据段描述符都可以放在GDT或LDT中作为entries但TSS段描述符只能放在GDT表中作一个entryLDT描述符只能放在GDT表中作一个entry。o对应csdsssesfsgs这个segementselectors可以当前指定个segmentdescriptors(在gdt表或ldt表中)。为此xCPU提供了个软件不可见的bit寄存器存储这个当前的segmentdescriptors。这样例如cs和ds已确定那么CPU就把相应的descriptor加载到不可见寄存器中从此不再访问内存中的GDTLDT表读descriptors直到相应的csds等selector被改变。Linux对段映射的实现。ULK说Linux只有在x处理器需要时才在段映射一事上敷衍敷衍它。o作为GDT表中的一个entryKERNELCS的定义:#defineGDTENTRYKERNELCS(GDTENTRYKERNELBASE)#defineKERNELCS(GDTENTRYKERNELCS*)解释:Linux把内核代码段作为GDT表的第项所以其INDEX就是。INDEX在CS的高位低位的TI==因为用的是GDTRPL==因为是RING权限。这样KERNELCS就是<<亦即*。o而USERCS:#defineGDTENTRYDEFAULTUSERCS#defineUSERCS(GDTENTRYDEFAULTUSERCS*)解释:Linux把用户代码段作为GDT表的第项所以其INDEX是。INDEX在CS的高位所以左移位亦即乘上。至于低位因为用的是GDT所以TI==又因为用户态所以RPL==。所以USERCS就是<<亦即*。oKERNELDS是GDT表中第项USERDS是GDT表中第项。好吧我们看看Linux定义的这个segments其baseaddress和limit的设置。GDT==kernelcsGDT==kerneldsGDT==usercsGDT==userds在GDT表中这个entries的baseaddress都是x,其limit都是xfffff。每个etnry的G标志被设为。(注意如果一个entry的G设为表示limit的单位是byte那么limit==xfffff就指定了MB的空间如果G被设置为则limit的单位是字节于是limit==xfffff就表示(^)*(^)==GB的空间)Linux就是这么敷衍的segmentingunit的:个段每个都是flat的GB大小。分段什么都不做。逻辑地址offset==线性地址。(注意intel的"逻辑地址"定义是bit的其中bit是segmentselector)archxkernelcpucommonc:DEFINEPERCPU(structgdtpage,gdtpage)={gdt={GDTENTRYKERNELCS={xffff,xcfa},GDTENTRYKERNELDS={xffff,xcf},GDTENTRYDEFAULTUSERCS={xffff,xcffa},GDTENTRYDEFAULTUSERDS={xffff,xcff},{其他的忽略}}}EXPORTPERCPUSYMBOLGPL(gdtpage)我们来看看KERNELCS的GDTentry赋值为{xffff,xcfa}是什么意思。KERNELCS在GDT表中的entry值:对照着Table知道baseaddress为x,limit为xfffffGruanularity为亦即KBytes为单位。oUPLinux中只有一个GDT表但SMP中每个CPU都有自己的GDT表。(includeasmxdesch)structgdtpage{structdescstructgdtGDTENTRIES}attribute((aligned(PAGESIZE)))DECLAREPERCPU(structgdtpage,gdtpage)每个CPU都有一个GDT表变量名就叫做gdtpage是个structgdtpage结构其中域gdtGDTENTRIES就是GDT表每个entry都是一个structdescstruct结构体变量。oLinux下基本不用LDT。所以只定义了一个默认的LDT放在defaultldt数组中。它包含项但只有两个常用:一个兼容iBCS操作系统的可执行文件一个兼容Solarisx的可执行文件。Wine需要修改ldt于是Linux提供了modifyldt()系统调用。IALinux的level页面映射===================================================================参考:INTEL手册V:Ch,Section:PageTranslationusingbitPhysicalAddressingPARTI:线性地址的划分level:位的线性地址被划分为部分:||||位的Directory|位的Table|位的Offset高位:又叫PageDirectory,PD中间位:PageDirectory,PT低位:页内offset每个进程都有自己的页表用以找到其PD的物理地址称为PGD:PageGlobalDirectory。每个进程都有自己的页表其PGD的物理地址放在CR寄存器中见schedule>contextswitch>switchmm>loadcr(next>pgd)PARTII:MMU分页单元寻找物理地址的过程)MMU根据CR中的pgd值和要访问的那个线性地址的高bit(就是在PD中的offset)在PD中找到相应的的entry──一个PD的entry是一个位的物理地址。其中高bit是PageTable的BaseAddress)MMU根据PageTable的BaseAddress和要访问的那个线性地址的中间bit(:)在PT中找到相应的entry──一个PT的entry(就是传说中的PTE)是一个位的物理地址。其中高bit是Page的BaseAddress找到Page的baseaddress就找到了页面的物理地址。)MMU根据Page的物理地址(which低位都是零因为页首地址是对齐的)和要访问的那个线性地址的低bit找到该地址在该页面中的位置–这就是传说中的「物理地址」和那个要访问的线性地址对应的。线性地址=>物理地址(来自ULK):PARTIII:标志*PD和PT中的entries其格式是一样的。只不过:*PD的entry约束的是一个包含PT的页面而PT的entry约束的是一个包含有效数据的页面。Thankyou!AnyQuestions,编译内核获取内核压缩找一个旧的config作参考配置、编译和安装文档,浏览代码vimctagscscope编译ctags需要的tags文件:编译cscope程序需要的cscopeout文件:在Vim中用ctags浏览在Vim中用cscope浏览lxrLinuxCrossReferrence内核相关的图书入门推荐:LKD《Linux内核设计与实现第版》ULK深入理解Linux内核rd《Linux内核源代码情景分析》LDDLinux设备驱动程序rd现代体系结构上的Unix系统内核程序员的SMP和Caching技术IntelAMDCPU参考手册,第一个模块:HelloKernel,内核相关的网络资源,其他的知识,内核调试printkkdbkgdbkprobessystemtapVMWareworkstationx,现代硬件:ARCHDRIVER现代XCPU外围设备,的分段机制为段映射提供的硬件Linux对段映射的实现。IALinux的level页面映射PARTI:线性地址的划分PARTII:MMU分页单元寻找物理地址的过程PARTIII:标志

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/29

Linux内核学习起步

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利