《Linux内核设计与实现》读书札记

《Linux内核设计与实现》 Robert love著

<!--[if !supportLists]-->第一章   <!--[endif]-->Linux内核简介

<!--[if !supportLists]-->1.       <!--[endif]-->处理器活动范围为:

运行于内核空间,处于进程上下文

运行于内核空间,处于中断上下文

运行于用户空间,执行用户进程。

<!--[if !supportLists]-->2.       <!--[endif]-->单内核与微内核

Windows NT和Mac OS X的Mach都是微内核,Linux是单内核,但是吸取了微内核的精华:模块化设计,抢占式内核,支持内核线程及动态装载模块。

<!--[if !supportLists]-->第二章   <!--[endif]-->从内核出发

<!--[if !supportLists]-->1.       <!--[endif]-->编译内核时会在内核代码树的根目录下创建一个System.map文件,是一份符号对照表,用以将内核符号和他们的起始地址对应起来。

<!--[if !supportLists]-->2.       <!--[endif]-->内联函数需要在使用前定义好,例如static inline void foo()。

<!--[if !supportLists]-->3.       <!--[endif]-->Gcc中可以使用likely()和unlikely()分别标记出发生频率较高和较低的分支,以优化程序执行效率。

<!--[if !supportLists]-->4.       <!--[endif]-->不要在内核使用浮点数。

<!--[if !supportLists]-->第三章   <!--[endif]-->进程管理

<!--[if !supportLists]-->1.       <!--[endif]-->进程是Unix操作系统最基本的抽象之一,另一个是文件。

<!--[if !supportLists]-->2.       <!--[endif]-->每个线程有一个独立的程序计数器,进程栈和一组进程寄存器,内核调度对象是线程,不是进程。

<!--[if !supportLists]-->3.       <!--[endif]-->进程存放在叫做任务队列的双向循环链表中,链表每一项都是task_struct类型,称为进程描述符。

<!--[if !supportLists]-->4.       <!--[endif]-->在进程上下文中,current宏获得当前进程是有效的。

<!--[if !supportLists]-->5.       <!--[endif]-->写时拷贝(COW)是一种推迟甚至免除在fork调用时所需要的数据拷贝操作。内核不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝,只有在需要写入时,所涉及到的页才被复制。因此在fork之后子进程如果马上调用exec,系统开销就只是复制父进程的页表以及给子进程创建唯一的进程描述符。

<!--[if !supportLists]-->6.       <!--[endif]-->Clone是一个实现fork的系统调用,通过一系列参数来指明父,子进程之间需要共享的资源。

<!--[if !supportLists]-->7.       <!--[endif]-->在创建子进程后,内核有意首先执行子进程。

<!--[if !supportLists]-->8.       <!--[endif]-->Vfork与fork相同,唯一不同在于vfork只拷贝父进程页表,不拷贝页表项,他们共享数据,父进程被阻塞直到子进程退出或执行execl。

<!--[if !supportLists]-->9.       <!--[endif]-->Linux中线程同样被当作进程,只是他们之间共享资源。

<!--[if !supportLists]-->10.   <!--[endif]-->内核线程只在内核空间运行,但是与普通进程一样可以被调度,被抢占。

<!--[if !supportLists]-->第四章   <!--[endif]-->进程调度

<!--[if !supportLists]-->1.       <!--[endif]-->进程调度来决定什么时候停止一个进程的运行以便其他进程能够得到执行的机会,这个强制的停止,挂起动作叫做抢占。

<!--[if !supportLists]-->2.       <!--[endif]-->进程可以被分为I/O消耗型和处理器消耗型。I/O消耗型应该多被调度,而处理器消耗型应该降低其运行频率,延长运行时间。

<!--[if !supportLists]-->3.       <!--[endif]-->基于进程优先级的调度,Linux实现了基于动态优先级的调度方法:一开始进程设置基本的优先级,然后根据执行情况来加,减优先级。常常处于等待I/O的I/O消耗型增加,常常迅速用完处理器时间片的处理器消耗型减少优先级。

<!--[if !supportLists]-->4.       <!--[endif]-->Linux使用-20~+19的nice值,以及0~99的实时优先级来决定一个进程的优先级。

<!--[if !supportLists]-->5.       <!--[endif]-->当一个进程时间片耗尽时,就不会再运行,除非其他所有进程都耗尽了时间片,那是,所有进程的时间片被重新计算。

<!--[if !supportLists]-->6.       <!--[endif]-->一个进程进入TASK_RUNNING状态时,内核会检查其优先级是否高于当前进程优先级,如果高于,调度进程被唤醒,抢占发生;或者当进程时间片为0时,同样会唤醒调度进程,将其抢占。

<!--[if !supportLists]-->7.       <!--[endif]-->调度程序最基本的是运行队列,每个处理器一个,表示该处理器上可执行进程的链表。

<!--[if !supportLists]-->8.       <!--[endif]-->Cpu_rq(processor)宏用于返回指定CPU上的运行队列,this_rq()返回当前CPU的。

<!--[if !supportLists]-->9.       <!--[endif]-->在对可执行队列操作前需要用task_rq_lock等函数对其加锁。

<!--[if !supportLists]-->10.   <!--[endif]-->每个运行队列都有2个优先级数组,一个活跃的,一个过期的。每个数组都有一个优先级位图,可以提高查找当前系统内最高优先级的可执行进程的效率。每个数组还都有一个list_head队列,包含有多个链表,每个链表与一个给定的优先级对应,即一个链表表示一个优先级上的所有可执行进程,这样能快速找到某个优先级上的进程。

相关推荐