红联Linux门户
Linux帮助

对Linux内核的总结认识

发布时间:2015-04-17 21:31:11来源:linux网站作者:linux人

第一次接触Linux是高三那时,后来在大学里舍友T总的影响便开始了对Linux的不断追求与学习。读万卷书不如行万里路,以前在学校里看了那么多书还不如工作中在代码中煅练,当然关键是找到个入门口点,我选择驱动程序开始!下面总结和谈下学习驱动后对其的理解与认识。


1.内核是怎样实现其管理的职能?

以前在学校时一直不能理解内核是怎么做管理?比如内核如何知道在什么时候对各个进程做调度,又在什么时候知道缺页从而执行内存比如内核如何知道在什么时候对各个进程做调度,又在什么时候知道缺页从而执行内存管理的代码,内核为什么在会需要它管理的时候被唤醒,进程在用户态跑的好好的,又是谁唤醒内核转入核心态去check一下?内核管理职能的实现一方面可以是通过中断触发,这些中断包括系统调用,硬件中断;另一方面有一些内核线程长期运行在内核态。因为系统调用往往触发相应驱动程序代码的执行,而驱动程序本身包含大量可以导致进程休眠的代码,例如一个系统调用read把CPU从用户态转到核心态,经过虚拟文件系统VFS传入到设备相关的驱动程序中file_operations的真正实现函数read,这个系统调用可能引发内核中很多管理模块的执行,假如驱动中发现设备没有可用的数据时对于阻塞型的调用,驱动程序会把与其相关联的用户执行线程挂起休眠,从而让出了CPU实现了进程调度器的调度。另外,一般来说CPU定时器会周期性地唤醒内核让其check一下,看一下有没有东西需要它处理的(主要是引发了软中断的产生,进而可能执行一些软中断的任务,稍后解释什么是软中断)。这就是内核的“心跳”,一般PC上会把这个心跳值HZ(每秒定时器中断次数)设置为100~1000,jiffies是开机累计心跳值。同样,CPU外部的器件会产生大量的中断,这样使得内核也知道了它将要干什么。最后,内核线程kernel thread在内核态周期性地执行,例如磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等。


2.代码执行空间及其可见度

以前看一本书操作系统的书搞得好头痛,x86上又是什么GDT,又是什么LDT,MMU的存在搞得内存这块非常的难懂。现在看来在区分用户态和核心态的32位系统上,用户进程所能看到的空间仅仅分配给它的4G虚拟地址空间,用户进程要想通往内核的唯一途径是内核事先定义的有限数目的“系统调用”。系统调用的实质是该进程把数据(包括中断号,系统调用的参数)放进 CPU特定寄存器并向CPU发送一个异常。通往内核并不意味着用户进程就能看到并且干预内核空间,它能且仅能通过系统调用给定的接口参数向内核申请服务,这样内核会接手和代表用户进程并借用该用户进程的上下文继续执行(英文context的翻译,是指程序计数器、栈和一组寄存器等),CPU转入核心态,内核代码就执行该系统调用在内核中的“实现部分”以满足用户进程对数据操作的需要,就执行路线上说,此时的内核执行处于“该用户进程上下文” 中,内核能够看到该用户进程的空间并且该执行路线是与用户进程相关,内核访问用户空间的代码随体系结构不同而有不同的实现,而且需要经过地址转换。待内核处理完系统调用返回后,则转回用户态,用户态进程就在该系统调用返回继续前行。在这种多任务的操作系统中,内核代码存在着多条执行路线,所以内核代码非常讲究代码可重入性设计。在内核中,中断不与任何一个用户进程相关,包括定时器中断或者外围硬件中断和甚至“软中断”,即不是运行在任何一个进程的上下文中而是运行在中断上下文拥有自己的程序计数器、栈和一组寄存器;常常硬件中断的发生并且被响应必定意味着切换CPU上下文。中断上下文不代表用户进程,所以中断执行例程不能访问用户空间的一切内容。


3.驱动程序是什么?及其与内核的关系

驱动程序本身就是内核的一部分,从www.kernel.org下载回来的内核代码,单从代码量来说驱动程序超过占65%,当然因为内核配置和条件编译的关系,并不是所有的驱动都会被选上。内核中除掉驱动程序以外的代码是一个框架,该框架提供一个公有的属性,不随CPU体系架构和具体硬件型号而变化,也是一个高度抽象模块集,更是内核的“基础设施”,这个基础设施向驱动程序提供一些接口和定义一些标准。驱动程序的设计要遵循内核给定给驱动程序的标准并使用内核和接口,这样就把操作具体硬件相关的工作交给了驱动程序,抽象出更一般的模块,提高了操作系统的可移植性,可扩展性,和标准化。就像做填空题那样,驱动程序在内核给定的空格上填好这个固定的空以使整个表达式是正确的。严格来说驱动程序包括:CPU体系架构驱动,各种各样的硬件设施驱动。一般的驱动开发者其实工作的重点是除CPU外的设备的适配。基于CPU体系架构的实现依赖于具体的平台如ARM,X86,SPARC,PPC,MIPS等,所以具体到平台的实现部分有些是用汇编语言写的,这些汇编因不同的体系结构而不同(某种原因是C语言实现不了的操作,某些是基于采用汇编实现效率更高更安全)。比如在关中断开断的实现又或切换进程上下文等操作采用汇编。除CPU外的设备一般C语言就完成了,因为大多数是与操作寄存器和数据读写有关。这里应该指明,现在的嵌入式芯片应该称之为SOC(System On Chip)更适合,而本文的CPU指的是处理器内核比如ARM926EJ-S。设备驱动程序是跟设备操作相关的系统调用的内核实现版本。就是说对设备的操作(比如ioctl(fd,…))其在内核的实现是由驱动完成。所以驱动要做的事情就是实现file_operations结构的函数指针,安排中断例程。当然驱动也有分层的,有些模块纯粹是一种软件抽象。比如USB驱动分层。驱动程序是直接面对硬件,内核通过驱动与硬件交互。对内核的核心来说驱动屏蔽了硬件的实现细节。驱动对硬件的操作集中表现在对寄存器的访问及数据读写。


4.内核线程,用户线程,NPTL

以前的用户态多线程的实现通过用户态线程库为其做调度与切换。想象一下,那时的多线程进程,在内核里只把该进程看成一个执行线程。多线间的切换调度是通达线程库实现,这样效率是比较差。现在的2.6内核NPTL线程库其实是在1:1,就是说在用户态进程的线程在内核里有对应的代表线程。在“内核”中 Linux并不区别线程与进程,也就是说线程和进程都用task_struct结构表示,我想这句话一定会让困惑于线程与进程的朋友大为吃惊,不错,在当代2.6的Linux内核中的确如此,只不过,内核可以让某一些线程共享资源从而让这些线程组给用户空间看起来就像是一个进程包含多个线程,这些共享的资源可以是打开的文件描述符,地址空间,信号处理程序和命名空间等等的任意组合。周期性运行的内核线程可以理解成在内核中运行的特殊进程,它有自己的“进程上下文”(借用调用它的用户进程的上下文),所以同样被进程调度程序调度,也可以睡眠——它和用户进程属性何其相似,不同之处就在于内核线程运行于内核空间,可访问内核数据,运行期间不能被抢占。传统的Unix系统把一些重要的任务委托给周期性执行的进程,这些任务包括刷新磁盘高速缓存,交换出不用的页面,维护网络链接等等。事实上,以严格线性的方式执行这些任务的确效率不高,如果把他们放在后台调度,不管是对它们的函数还是对终端用户进程都能得到较好地响应。因为一些系统进程只运行在内核态,现代操作系统把它们的函数委托给内核线程(Kernel Thread),内核线程不受不必要的用户态上下文的拖累。在Linux中使用ps -eFT命令可以查看到所有内核线程和用户线程,所有有中括号[]的都是内核线程,线程名后面跟着的数字代表该线程在哪个CPU上跑着,如果你的机器是SMP的话。


5.内核信号量的设计与实现,自旋锁,与休眠队列

暂时我的理解是自旋锁其实就是while(锁){}循环检测锁是否可用。这个过程CPU被死锁在死循环中,可以想像这时除了等待锁被释放外啥也干不了。而内核中信号量的设计里边有自旋锁和休眠方面操作。down(),up()及其变种版本,这两个函数比较深奥,只能理解到这里。呵呵


6.什么是软中断,tasklet, 中断的bottom half(下半部分)

澄清一些概念,软件中断与软中断的区别(因为不同的书籍使用不同的术语可能导致混淆),软件中断大多指向CPU发送一个异常引发从用户态到核心态的转换,而软中断则是Linux的一个非常重要也挺复杂的一个机制那就是“延后执行,但最晚不会晚于下一个定时器中断的到来”,它并非一个真正意义上的中断所以称它为虚拟中断。以前只知道定时器是可以给我们在一个固定的时间后执行定时器中断例程来执行我们的任务,同时,外围硬件中断也有很多,有些是很急也有不急的,像都不能在中断例程里做太多操作与运算,这样中断的处理的时间太长从而影响了下一个中断,但是环境客观上又需要中断例程处理很多数据,比如网卡收到数据包发生中断请求,此时中断要做的事情是把数据复制和解析给用户,这对中断例程来说,他需要做的事情太多,但也不得不做。基于这种情况,内核提供了一种设施,把中断例程要做的事情分成两半,上半部分和下半部分,上半部分只是做一些非常紧急和必要的操作然后退出,把其余的放在下半部在CPU不是那么紧急的或相对缓松的时刻来执行,以致不影响下一个中断。如果中断例程任务不重大可不必分上半和下半。如需要分下半部分,那么下半部分怎样的实现在CPU的一个比较宽松的环境里执行呢?提供这种机制的是软中断!Linux的软件中断概念又是什么一回事?软中断(softirq)不象硬中断那样是由硬件中断信号触发执行的,所以也不同于硬件中断那样时随时都能够被执行,笼统来讲,软中断会在内核处理任务完毕后返回用户级程序前得到处理机会。具体的讲,有三个时刻它将被执行(do_softirq()):硬件中断操作完成后;系统调用返回时;内核调度程序中;(另外,内核线程ksoftirqd周期执行软中断)。从中可以看出软中断会紧随硬中断处理(好象狐假虎威),所以抢占内核任务--至少在定时器中断(时钟中断)后总有机会运行一次。还要记得软中断可以在不同处器上并发执行。在有对称多处理器的机器上,那么两个任务就可以真正的在临界区中同时执行了,这种类型被称为真并发。相对而言在,单处理器上并发其实并不是真的同时发生,而是相互交错执行,是伪并发。但它们都同样会造成竞争条件,而且也需要同样的保护。软中断是很底层的机制,一般除了在网络子系统和SCSI子系统这样对性能要求很高以及要求并发处理的时候,才会选择使用软中断。软中断虽然灵活性高和效率高,但是你自己必须处理复杂的同步处理(因为它可在多处理器上并发),所以通常都不直接使用,而是作为支持tasklet和bottom half的根本。需要说明的是,软中断的执行也处于中断上下文中,所以中断上下文对它的限制是和硬中断一样的。Tasklet与定时器的不同其中之一在于tasklet是没有一个固定的执行延迟时间。


7.内核编程与用户态空间编程的差别

对于从事应用程序开发来说,用户空间的任务调度与同步之间的关系相对简单,无需过多考虑需要同步的原因。这一是因为在用户空间中各个进程都拥有独立的运行空间,进程内部的数据对外不可见,所以各个进程即使并发执行也不会产生对数据访问的竞争。第二是因为用户空间与内核空间独立,所以用户进程不会与内核任务交错执行,因此用户进程不存在与内核任务并发的可能。以上两个原因使得用户同步仅仅需要在进程间通讯和多线程编程时需要考虑。作为驱动程序的一部分,内核代码的可重入设计需求非常迫切。其实还有很多。到时再写。