Maison >Tutoriel système >Linux >Analyse approfondie du code source du noyau Linux et exploration de l'essence du système d'exploitation
一、内核源码之我见
Linux内核代码的庞大令不少人“望而生畏”,也正由于这般,促使人们对Linux的了解仅处于泛泛的层次。假如想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径。我们都晓得,想成为优秀的程序员linux内核源码剖析,须要大量的实践和代码的编撰。编程尚且重要,而且常常只编程的人很容易把自己局限在自己的知识领域内。假如要扩充自己知识的广度,我们须要多接触其他人编撰的代码,尤其是水平比我们更高的人编撰的代码。通过这些途径,我们可以跳出自己知识圈的禁锢,步入别人的知识圈,了解更多甚至我们通常短期内难以了解到的信息。Linux内核由无数开源社区的“大神们”精心维护,那些人都可以称得上一顶一的代码高人。透过阅读Linux内核代码的形式,我们学习到的不光是内核相关的知识,在我看来更具价值的是学习和感受它们的编程方法以及对计算机的理解。
我也是通过一个项目接触了Linux内核源码的剖析,从源码的剖析工作中,我获益颇丰。不仅获取相关的内核知识外,也改变了我对内核代码的过往认知:
1.内核源码的剖析并非“高不可攀”。内核源码剖析的难度不在于源码本身,而在于怎样使用更合适的剖析代码的形式和手段。内核的庞大导致我们不能根据剖析通常的demo程序那样从主函数开始按部就班的剖析,我们须要一种从中间介入的手段对内核源码“各个击破”。这些“按需索要”的形式促使我们可以掌握源码的主线,而非过度郁闷于具体的细节。
2.内核的设计是优美的。内核的地位的特殊性决定着内核的执行效率必须足够高才可以响应目前计算机应用的实时性要求,因此Linux内核使用C语言和汇编的混和编程。并且我们都晓得软件执行效率和软件的可维护性好多情况下是背道而驰的。怎样在保证内核高效的前提下提升内核的可维护性,这须要依赖于内核中这些“优美”的设计。
3.神奇的编程方法。在通常的应用软件设计领域,编码的地位可能不被过度的注重,由于开发者更重视软件的良好设计,而编码仅仅是实现手段问题——就像拿斧头劈柴一样,不用太多的思索。并且这在内核中并不创立,好的编码设计带来的不光是可维护性的提升,甚至是代码性能的提高。
每位人对内核的了理解就会有所不同,随着我们对内核理解的不断加深,对其设计和实现的思想会有更多的思索和感受。因而本文更期望于引导更多徘徊在Linux内核房门之外的人步入Linux的世界,去亲自感受内核的神奇与伟大。而我也并非内核源码方面的专家,那么做也只是希望分享我自己的剖析源码的经验和心得,为这些须要的人提供参考和帮助,说的“冠冕堂皇”一点,也算是为计算机这个行业,尤其是在操作系统内核方面贡献自己的一份绵薄之力。闲谈少叙(早已罗嗦了好多了,囧~),下边我就来分享一下自己的Linix内核源码剖析方式。
二、内核源码难不难?
从本质上讲,剖析Linux内核代码和看他人的代码没有哪些两样,由于摆在你面前的通常都不是你自己写下来的代码。我们先举一个简单的事例,一个陌生人随意给你一个程序,并要你看完源码后讲解一下程序的功能的设计,我想好多自我觉得编程能力还可以的人肯定认为这没哪些,只要我耐心的把他的代码从头到尾看完,肯定能找到答案,但是事实确实是这么。这么如今换一个假定,假如这个人是Linus,给你的就是Linux内核的一个模块的代码,你就会认为仍然这么轻松吗?不少人可能会有所迟疑。同样是陌生人(Linus要是认识你的话其实不算,呵呵~)给你的代码,为何给我们的觉得大相径庭呢?我认为有以下缘由:
1.Linux内核代码在“外界”看来多少有些神秘感,但是它很庞大,旋即摆在面前可能觉得难以下手。例如可能来始于一个很细小的诱因——找不到main函数。对于简单的demo程序,我们可以从头至尾的剖析代码的涵义,并且剖析内核代码这招就彻底失效了,由于没有人能把Linux代码从头到尾看上一遍(由于确实没有必要,用到时看就可以了)。
2.不少人也接触过小型软件的代码,但多数属于应用型项目,代码的方式和含意都和自己常接触的业务逻辑相关。而内核代码不同,它处理的信息多数和计算机底层密切相关。例如操作系统、编译器、汇编、体系结构等相关的知识的缺乏,也会让阅读内核代码障碍重重。
3.剖析内核代码的方式不够合理。面对大量的而且复杂的内核代码,假若不从全局的角度入手linux mint,很容易深陷代码细节的泥沼中。内核代码毕竟庞大,而且它也有它的设计原则和构架,否则维护它对任何人来说都是一个恶梦!假如我们理清代码模块的整体设计思路,再去剖析代码的实现,可能剖析源码就是一件轻松快乐的事情了。
针对那些问题,我个人是这样理解的。假如没有接触过小型软件项目,可能剖析Linux内核代码是一个挺好的积累小型项目经验的机会(确实,Linux代码是我目前接触到的最大的项目了!)。假如你对计算机底层了解的不够透彻,这么我们可以选择边剖析边学习的方法去积累底层的知识。可能刚开始剖析代码的进度会稍显迟钝,然而随着知识的不断积累,我们对Linux内核的“业务逻辑”会渐渐明朗上去。最后一点,怎样从全局的角度掌握剖析的源码,这也是我想与你们分享的经验。
三、内核源码剖析方式
第一步:资料收集
从人认识新事物的角度来讲,在探求事物本质之前,必须有一个了解新鲜事物的过程,这个过程是的我们对新鲜事物形成一个初步的概念。例如我们想学习吉他,这么我们须要先了解演奏吉他须要我们学习基本的视唱、简谱、五线谱等基础知识,然后学习二胡演奏的方法和指法,最后才会真正的开始练习吉他。
剖析内核代码也是这么,首先我们须要定位要剖析的代码涉及的内容。是进程同步和调度的代码,是显存管理的代码,还是设备管理的代码,还是系统启动的代码等等。内核的庞大决定着我们不能一次性将内核代码全部剖析完成,因而我们须要给自己一个合理的分工。正如算法设计告诉我们的,要解决一个大问题,首先要解决它所涉及的子问题。
Selepas mencari julat kod untuk dianalisis, kami boleh menggunakan semua sumber yang ada untuk memahami keseluruhan struktur dan fungsi umum bahagian kod ini selengkap mungkin.
Semua sumber yang dinyatakan di sini merujuk kepada Baidu, enjin carian dalam talian kecil Google, buku teks prinsip sistem pengendalian dan buku profesional, atau pengalaman dan maklumat yang diberikan oleh orang lain, atau pun dokumen, ulasan dan kod sumber yang disediakan oleh kod sumber Linux Nama pengecam (jangan memandang rendah penamaan pengecam dalam kod, kadangkala mereka boleh memberikan maklumat penting). Malah, semua sumber di sini merujuk kepada semua sumber yang ada yang boleh anda fikirkan. Sebenarnya, tidak mungkin kita dapat memperoleh semua maklumat yang kita inginkan melalui kaedah pengumpulan maklumat ini. Kita hanya mahu menjadi selengkap mungkin. Kerana lebih komprehensif maklumat dikumpul, lebih banyak maklumat boleh digunakan dalam proses menganalisis kod, dan kesukaran proses analisis akan menjadi lebih kecil.
Berikut ialah contoh balas mudah Katakan kita ingin menganalisis kod yang dilaksanakan oleh mekanisme penukaran frekuensi Linux. Setakat ini kita hanya tahu istilah ini Daripada makna literal, kita boleh membuat kesimpulan secara kasar bahawa ia sepatutnya berkaitan dengan pelarasan frekuensi CPU. Melalui pengumpulan maklumat, kita seharusnya dapat memperoleh maklumat berkaitan berikut:
1. Mekanisme CPUFreq.
2. prestasi, jimat kuasa, ruang pengguna, ondemand, strategi peraturan frekuensi konservatif.
3. /driver/cpufreq/.
4. /documentation/cpufreq.
5. Pstate dan Cstate.
……
Jika anda boleh mengumpul maklumat seperti ini semasa menganalisis kod kernel Linux, anda harus dikatakan sangat "bertuah". Walaupun maklumat tentang kernel Linux sememangnya tidak sekaya .NET dan JQuery, berbanding lebih sepuluh tahun yang lalu, apabila tiada enjin carian yang berkuasa dan tiada bahan penyelidikan yang relevan, ia sepatutnya dipanggil era "penuaian besar"! Melalui "carian" mudah (yang mungkin mengambil masa satu hingga tujuh hari), kami juga menemui direktori fail kod sumber di mana bahagian kod ini terletak, saya perlu mengatakan bahawa maklumat jenis ini adalah "tidak ternilai"!
Langkah 2: Lokasi kod sumber
Daripada pengumpulan data, kami "bertuah" untuk mencari direktori kod sumber yang berkaitan dengan kod sumber. Dan ini tidak bermakna bahawa kami sebenarnya menganalisis kod sumber dalam direktori ini. Kadangkala direktori yang kami temui mungkin bertaburan, dan kadangkala direktori yang kami temui mengandungi banyak kod yang berkaitan dengan mesin tertentu, dan kami lebih mengambil berat tentang mekanisme utama kod untuk dianalisis berbanding kod khusus yang berkaitan dengan mesin ( Ini akan membantu kita lebih memahami sifat inti). Atas sebab ini, kita perlu berhati-hati memilih maklumat yang melibatkan fail kod dalam maklumat. Malah, langkah ini tidak mungkin dilengkapkan sekaligus, dan tiada siapa yang boleh menjamin bahawa semua fail kod sumber yang akan dianalisis boleh dipilih pada satu masa dan tiada satu pun daripadanya akan terlepas. Dan kita tidak perlu takut selagi kita boleh memahami fail sumber teras yang berkaitan dengan kebanyakan modul, kita secara semula jadi boleh mencari semuanya melalui analisis terperinci kod itu kemudian.
回到上述的事例中,我们认真的阅读/documention/cpufreq下的文档说明。目前的Linux源码会把模块相关的文档说明保存在源码目录的documention的文件夹下,假若待剖析的模块没有文档说明,这多少会降低定位关键源码文件的难度,并且不会引起我们找不到我们要剖析的源码。通过阅读文档说明,我们起码能关注到/driver/cpufreq/cpufreq.c这个源文件。通过这个对源文件的文档说明,结合之前搜罗到的调频策略,我们很容易关注到cpufreq_performance.c、cpufreq_powersave.c、cpufreq_userspace.c、cpufreq_ondemand、cpufreq_conservative.c这五个源文件。所有涉及的文件都找完了吗?不用害怕,从它们开始剖析,迟早能找到其他的源文件。假如在windows下使用sourceinsight阅读内核源码的话,我们通过函数的调用和查找符号引用等功能,结合代码的剖析可以很便捷的找到另外的文件freq_table.c、cpufreq_stats.c和/include/linux/cpufreq.h。
根据搜索出的信息流动方向,我们完全可以定位到须要剖析的源码文件。源码定位这一步并非非常关键,由于我们不须要找出所有源码文件,我们可以把部份工作延后到剖析代码的过程中。源码定位也比较关键,找到一部份源码文件是剖析源码的基础。
第三步:简单注释
在已定位好的源码文件中,剖析每位变量、宏、函数、结构体等代码元素的大致涵义和功能。之所以称此为简单注释,并非指这部份的注释工作很简单,而是指这部份的注释可以毋须过于细化,只要大致描述出相关代码元素的涵义即可。相反,这儿的工作虽然是整个剖析流程中最困难的一步。由于这是第一次深入到内核代码的内部,尤其是对于首次剖析内核源码的人来说,大量的生疏GNU的C句型和铺天盖地的宏定义会令人很绝望。此时只要沉下心来,弄清每位关键的难点,能够保证之后遇到类似的难点不会再被逼退。并且,我们对内核相关的其他知识会不断的像树一样扩充开来。
例如在cpufreq.c文件开始都会出现“DEFINE_PER_CPU”宏的使用,我们通过查阅资料可以基本弄清这个宏的涵义和功能。这儿使用的手段和之前收集资料使用的方式基本一致,另外我们也可以使用sourceinsight提供的转入定义等功能查看它的定义,或则使用LKML(LinuxKernelMailList)查阅,实在不行我们还可以到提问寻求解答(想了解哪些是LKML和stackoverflow?收集资料吧!)。其实借助所有可能的手段,我们总能得到这个宏的涵义——为每位CPU定义一个独立使用的变量。
我们也不要强求一次能够把注释描述的很确切(我们甚至都没必要弄清每位函数的具体实现流程,只要弄清大致功能涵义即可),我们结合收集到的资料和上面代码的剖析不断的建立注释的涵义(源码中原有的注释和标示符命名在此很有借助价值)。通过不断的注释,不断的查阅资料,不断的更改注释的含意。
当我们把所有涉及的源码文件简单注释完毕后我们可以达到如下疗效:
1.基本弄清了源码中代码元素存在的含意。
2.找出了该模块所涉及的基本上全部的关键源码文件。
结合之前收集到的信息和资料对该待剖析代码的整体或则构架描述,我们可以将剖析的结果和资料对比,以确定和修正我们对代码的理解。这样,通过一遍的简单注释,我们就可以从整体上掌握了源码模块的主要结构。这也达到了我们简单注释的基本目的。
第四步:详尽注释
完成代码的简单注释后,可以觉得对模块的剖析工作完成了一半了,剩下的内容就是对代码的深入剖析和彻底理解。简单注释总是不能将代码元素的具体含意描述的非常精确,因而详尽注释是非常有必要的。这一步中,我们须要弄清以下内容:
1.变量定义在何时被使用。
2.宏定义的代码何时被使用。
3.函数的参数和返回值的含意。
4.函数的执行流程和调用关系。
5.结构体数组的具体含意和使用条件。
我们甚至可以把这一步称为函数详尽注释,由于函数之外的代码元素的含意基本上在简单注释中早已比较明晰了。而函数本身的执行流程、算法等是这部份注释和剖析的主要任务。
例如cpufreq_ondemand策略的实现算法(函数dbs_check_cpu中)是怎样实现的。我们须要逐渐剖析该函数使用的变量和调用的函数等信息,弄清算法的来龙去脉。最好的结果,我们须要这种复杂函数的执行流程图和函数调用关系图,这是最直观的抒发形式。
通过这一步的注释,我们基本上能完全掌握待剖析代码整体的实现机制了。而所有的剖析工作可以觉得完成了80%。这一步工作尤其关键,我们必须尽量让注释的信息足够的确切,就能更好的理解待剖析代码的内部模块的界定。其实Linux内核中使用了宏句型“module_init”和“module_exit”声明模块文件,并且对模块内部子功能的界定是构建在充分了解模块的功能基础上的。只有正确界定好模块,我们就能弄清模块提供了什么外部函数和变量(使用EXPORT_SYMBOL_GPL或则EXPORT_SYMBOL导入的符号)。能够继续下一步的模块内标示符依赖关系剖析。
第五步:模块内部标示符依赖关系
通过第四步对代码模块的界定,我们就可以很“轻松”地挨个对模块进行剖析。通常的,我们可以从文件顶部的模块出入口函数开始(“module_init”和“module_exit”声明的函数,通常都在文件最后)linux内核源码剖析,按照它们调用的函数(自己定义的或则其他模块的函数)和使用的关键变量(本文件内的全局变量或则其他模块的外部变量)画出“函数-变量-函数”依赖关系图——我们称为标示符依赖关系图。
其实,模块内标示符依赖关系并非是单纯的树状结构,好多情况是纷繁复杂的网路关系。这时侯,我们对代码的详尽注释的作用就彰显下来了。我们按照函数本身的含意,将模块进行子功能界定,抽取出每位子功能的标示符依赖树。
Grâce à l'analyse des dépendances des identifiants, il peut être clairement montré que les fonctions définies par le module appellent ces fonctions, quelles variables sont utilisées et les dépendances entre les sous-fonctions du module - quelles fonctions et variables sont partagées, etc.
Étape 6 : Interdépendances entre modules
Une fois tous les diagrammes de dépendances des identifiants internes du module triés, les dépendances entre les modules peuvent être facilement obtenues en fonction des variables ou des fonctions d'autres modules utilisés par le module.
La relation de dépendance du module du code cpufreq peut être exprimée comme la relation suivante.
Étape 7 : Schéma de l'architecture du module
Grâce au diagramme de dépendances entre les modules, l'état et la fonction du module dans l'ensemble du code à analyser peuvent être clairement exprimés. Sur cette base, nous pouvons classer les modules et trier la relation architecturale du code.
Comme le montre le diagramme de dépendance des modules de cpufreq, nous pouvons clairement voir que tous les modules de stratégie de modulation de fréquence dépendent des modules de base cpufreq, cpufreq_stats et freq_table. Si nous visualisons les trois modules dépendants comme le cadre principal du code, ces modules de stratégie de modulation de fréquence sont construits sur ce cadre et sont responsables de l'interaction avec la couche utilisateur. Le module principal cpufreq fournit des pilotes et autres sockets associés chargés d'interagir avec le système sous-jacent. Par conséquent, nous pouvons obtenir le diagramme d’architecture de module suivant.
En fait, le diagramme d'architecture n'est pas un épissage inorganique de modules. Nous devons également combiner les données que nous consultons pour enrichir le sens du diagramme d'architecture. Par conséquent, les détails du diagramme d'architecture ici varieront en fonction de la compréhension de différentes personnes. Et la signification du corps principal du diagramme d’architecture est fondamentalement la même. À ce stade, nous avons terminé tout le travail d’analyse du code du noyau à analyser.
4.Résumé
Comme mentionné au début de l'article, il nous est impossible d'analyser l'intégralité du code du noyau. Par conséquent, collecter des informations sur le code à analyser, puis analyser l'histoire originale du code selon le processus ci-dessus est un moyen efficace de comprendre l'essence du noyau. Ces méthodes d'analyse du code du noyau selon des besoins spécifiques offrent la possibilité d'entrer rapidement dans le monde du noyau Linux. Grâce à ces méthodes, nous continuons à analyser d'autres modules du noyau, et finalement obtenons de manière globale notre propre compréhension du noyau Linux, atteignant ainsi notre objectif d'apprendre le noyau Linux.
Enfin, je recommande deux ouvrages de référence pour apprendre le noyau. L'un d'eux est "La conception et l'implémentation du noyau Linux", qui fournit aux lecteurs une introduction rapide et concise aux principales fonctions et à l'implémentation du noyau Linux. Mais il ne mènera pas les lecteurs dans l'abîme du code du noyau Linux. C'est un ouvrage de référence particulièrement efficace pour comprendre l'architecture du noyau et démarrer avec le code du noyau Linux. En même temps, ce livre renforcera l'intérêt des lecteurs pour le code du noyau. L'autre livre est "Compréhension approfondie du noyau Linux". Pourquoi devrais-je en dire plus sur le chef-d'œuvre de ce livre. Je suggère simplement que si vous souhaitez mieux apprendre ce livre, il est préférable de le lire en conjonction avec le code du noyau. Étant donné que ce livre décrit le code du noyau de manière très détaillée, sa lecture conjointe avec le code peut nous aider à mieux comprendre le code du noyau. Dans le même temps, lors du processus d'analyse du code du noyau, vous pouvez également trouver des informations avec une valeur de référence dans ce livre. Enfin, j'espère que vous entrerez le plus tôt possible dans le monde du noyau et que vous découvrirez les surprises que Linux nous réserve !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!