《深入了解Linux内核》学习笔记——第一章

《深入理解Linux内核》学习笔记——第一章
第一章 绪论

本章内容涉及整本书,大致介绍了整本书的编排情况。主要知识点有以下几点:
1.Linux是单块结构内核(Monolithic kernel)因此也引入了模块(module)这个强大的东西,内核可以按需装载或卸载模块。引申开来,在进行Linux移植时会需要自己定制代码,通常采用MenuConfig的方法,选择需要编译进内核的模块,这种设计方法为移植带了方便。
2.抢占式内核,这样就可以满足实时性,通常实时性操作系统都是抢占式内核。
3.“操作系统”是“内核”的同义词。操作系统最基本的两个目标是:一是与硬件部分交互,为包含在硬件平台上的所有底层可编程部件提供服务,二是为运行在计算机系统上的应用程序提供执行环境。用四个字来概括就是:承上启下!
4.Linux的“两态”,即用户程序的非特权模式和内核的特权模式。把他叫做用户态(User Mode)和内核态(Kernel Mode)
这个概念很重要,将会涉及两种状态之间的切换,内存的管理,等等一系列的问题。
5.进程!!!!!超级重要的概念啊!!!有木有??进程的理解方式有很多,但我觉得最好的理解是:“程序执行时的一个实例”或者说运行程序的“执行上下文(上下文这个概念很烦是不是,记得小学语文就有题目根据上下文意思理解这个词语,,但是在程序里面上下文可以理解为两个字:环境!)”,程序和进程的概率很容易混淆(这也是常规面试题啊!),要知道程序是静止的,进程是运动的!对于单处理器来说,只有一个进程能占用CPU,这就要引申另一个很重要的概念就是:宏观并行,微观串行!!而实现这个的就是一个伟大的产物:中断(interrupt)!!进程是个很自私的人,他妄图霸占所有资源(每个进程都认为是系统中唯一的进程,可以独占操作系统所提供的服务),为了公平,就出现了中断,他按照规则让很多进程一个个运行。这个时候就要解释下什么叫做霸占资源了。通常32位的操作系统只能访问4G的内存空间,3G给用户,1G给内核(见上一条两态),进程认为这4g空间都是他的,
进程在哪个空间运行,就是说他在什么状态。当进程发出系统调用时,这是就会从用户态到内核态,请求满足后就会又回到用户态。
6.Linu
x的四大天王:进程管理,内存管理,文件操作(设备属于文件),网络。先说说文件,文件我觉得是Linux设计最巧妙的地方之一,有句话说的好”思从深,行从简“我觉得这是对Linux文件一个很好的描述,文件很复杂,但用一些公共的操作就能让一个复杂的东西简化。文件的种类有以下几种:普通文件,目录,符号链接,字符设备文件,块设备文件,管道(FIFO)和套接字。所有的所有归结为文件操作既是:增,删,读、写、改,查!!
7.继续说这个文件,文件怎么描述?他的信息放在一个索引节点(Inode)的数据结构里。里面包含了一系列重要的内容:如文件类型,文件大小,设备标示符,索引节点号,文件拥有者,时间戳,访问权限等。这里再引申下,由于设备本身就是文件,所以,在进行设备文件操作时就格外要注意其他两样东西Device和Driver,这个当看到设备驱动的时候会详细说一下。
8.回到进程上来,说说内核和进程的关系吧。刚刚说到进程其实就是运行着的程序。有人会说内核的本质也是程序,那可不可以说是进程,其实内核本身并不是进程,而是进程的管理者千万不要把内核和内核线程混淆,内核线程运行在内核地址空间且不与用户直接交互,在系统启动时创建关闭时销毁。现在详细说下”两态“之间的转化,可以这样理解(套用当年吴老师的话),内核是皇宫,而用户是平民,平民怎么能随便拜见皇帝呢,你信访也得有个信访局从中间代劳吧,所以系统调用就是那个信访局,当然还有一条路就是中断(可以包括外部和内部中断,内部中断如定时器中断,外部如设备中断等等),这个中断也是有风险的要是有更高优先级的中断来,你的请求可不一定会被授予啊。。就像市长在你面前一挡,你的请求时没办法递到党*的。当然最后还要一种情况会从用户态到内核,就是产生一个异常(exception),像除法异常之类的。《深入了解Linux内核》学习笔记——第一章
9.既然进程那么重要,那就不能让他出乱子,为了能让内核管理这些进程,每个进程都会由一个进程描述符(process descriptor)表示,这个描述符包含了有关进程当前的状态信息。现在来回顾下进程的三个状态:运行、就绪、阻塞《深入了解Linux内核》学习笔记——第一章
《深入了解Linux内核》学习笔记——第一章
当内核暂停一个执行着的进程(即2或者3)时,就把几个相关的处理器寄存器的内容保存在进程描述符中,包括:程序计数器(PC)和栈指针(SP)寄存器,通用寄存器,浮点寄存器,包含CPU状态的信息的处理器控制寄存器,用来跟踪进程对RAM访问的内存管理寄存器。当内核恢复执行一个进程(即1),他用进程描述符中适当的字段来装填CPU寄存器。那他怎么知道在哪恢复执行呢,是因为程序计数器中的值即是他接下来要执行的指令
10.关于可重入内核(reentrant),说实在的对这个概念理解的并不是很深刻,先说说其定义:可重入内核可允许若干个进程同时在内核态下执行。个人理解为在内核态中可发生嵌套。如图所示《深入了解Linux内核》学习笔记——第一章
这里在内核态中,就发生了一次嵌套。如有理解不对的地方请指正。还有一个概念就做可重入函数:只修改局部变量,不修改全局变量的函数。。。。不能理解啊!!!!????
11.进程地址空间。这个概念相当重要,细讲起来可能要用一章的时间。先说点全局性的知识点,第5条层说过进程很自私,他运行在他自己的私有空间,在用户态下运行的进程涉及到私有栈,数据区和代码区。当在内核态运行时,进程访问内核的数据区和代码区,但使用另外的私有栈。有了私有当然就有公有,称其为共享内存,由进程显式的提出。通过这个共享内存可以实现进程间的通信。
12.进程同步(这里的同步一定要解释下,很多人误解同步就是一起操作的意思,其实事实恰好相反,同步时指多个进程间需要处理同一个数据,为了防止读写数据出现问题,而协调这些进程一个个去处理。是这样一个意思。):这个是经常遇到的问题,不仅面试会考,就是在平常debug的时候也会经常遇到。据我不规范统计,在代码中出现偶然产生的bug,通常就是由进程交错执行发生的。因为进程很不靠谱,你不知道他什么时候执行(这个就让我想到了薛定谔的猫他这个状态不可知)。。。所以当多个进程交错执行,并且涉及到临界区的访问,必然容易出现这个问题。那进程的同步怎么去做呢,一般有以下几个方法:
禁止中断(中断时万恶之源,又是天使,禁止他,就可以保证进程的执行不被打扰)
信号量(semaphore)这个概念很重要,因为不仅这一处会使用,在进程通信也会使用,在进程通信时会比较下这两个。先说说同步进程的这个信号量,信号量由:一个整数变量,一个等待进程链表,两个原子方法down()和up(),down()使信号量值减一,这个值小于0时,就把这个进程加入到信号量列表(可以理解为等待队列),up()对信号量加1,如果这个新值大于等于0,则激活这个信号量链表中的进程。信号量的概念还是可以理解的,这里引申一下,我们知道有一种设计模式叫做单例模式(Singleton Pattern),也是用一个变量来控制实例的创建数量。
三自旋锁(spin lock)自旋锁和信号量很类似,唯一的区别就是自旋锁没有进城链表,当一个进程发现锁被另一个进程锁着,他就不停地“旋转”,执行一个紧凑的循环指令直到锁被打开。自旋锁一般用在多处理器系统中
13.死锁(deadlock)。。。这个其实很简单。。但也很重要。。。因为在出现一些系统bug的时候往往就是他干的,先举个最简单的死锁的例子,进程p1获得访问数据结构a的权限,进程p2获得访问b的权限,但是p1在等待b,而p2在等待a,这样就会产生死锁。Linux通过按规定的顺序请求信号量来避免死锁。这段内容会具体在进程同步代码中叙述。
14.现在开始说信号(信号和进程通信中信号量是两个不同的概念,切不可混淆!!!),说信号之前必须先将明白两个重要的概念——异步与同步,这个概念是多线程操作系统中十分重要的概念之一,很多人都无法准确的理解,我就用最简单的话去概括这两个概念:同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。  异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。可以这样形象的比喻,同步就是打电话,你一句我一句,异步就像发短信,我发了,不知道你什么时候回(如同屌丝发短信给白富美。。。。)那白富美(不对。。是进程)接到信号后一般会有两种态度:一不鸟屌丝(忽略该信号)。。二回一个“呵呵”(执行信号处理程序)
15.终于说到进程通信了。。。。一般采用三种机制来实现进程间通信:1信号量,2,消息队列,3共享内存(当然还有管道,套接字等方法。。到时候会一个个代码重现的),统称为System V IPC
先说信号量:这里的信号量和进程同步的信号量类似,唯一的区别就是他是用在用户态下的。事实上他并不会传递信息。只是起到同步处理数据的效果。。
消息队列是能真正的发出接受信息的利用msgsnd()及msgget()系统调用交换消息。msgsend()表示把消息插入到指定队列,msgget()表示从队列中提取消息。这里值得一提的是POSIX标准定义了一种基于消息队列的IPC机制,他对应用程序提供了一个更简单的基于文件的接口,这样就可以像文件操作一样发送接收消息了。
共享内存这个比较好理解就像全局变量一样,大家都可以用,都可以改。这个主要定义一些比较公共的信息。尤其是状态信息。稍微引申下。。在单片机的操作中时常会用到全局变量来传递参数,但是,我比较建议在单片机中全局变量尽量少用,有时会带来一些问题,尤其是定义了一些寄存器数据的全局变量,尽量采用一个Const指针指向某个寄存器地址,再用局部变量去取值。
16.进程管理:要想理解进程管理先必须了解进程之间的关系。进程之间的关系我们通过以下几个概念一一阐述:
父子进程:一个进程调用fork()创建一个新的进程,则称调用者是父进程,被调用者是子进程,这应该很好理解的,他的名称就很好的理解了父子进程关系。还有一个很重要的问题,如果父进程不幸挂了。。。那子进程岂不是成了孤儿,那怎么办呢,其实所有进程都是有个始祖叫做init进程,这个进程在系统启动时产生(将来说到系统启动时会详细介绍),于是这个init进程成为这些“孤儿”的“父亲”。
进程组(process group),这个也比较好理解,就像做一件事需要许多个动作,每个动作就可以比喻成一个进程,而做的这件事就是进程组。简而言之就是完成同一项任务而相互有关联的进程的集合,这些进程产生的子进程也会被包含在进程组里。
登陆会话(login session):登陆会话的理解可以使这样的,可以把进程组想象成进程,把登陆会话想象成进程组,也就是说登陆会话是许多个进程组的集合,但是只有一个进程组处于前台。
17.内存管理:内存管理相当复杂,占据了整本书三分之一的篇幅。想要理解内存管理先要理解:虚拟内存(virtual memory)!!!这个概念,虚拟内存作为一种逻辑层,处于应用程序的内存请求与硬件内存管理单元(MMU)之间,虚拟内存的主要成分是虚拟地址空间,进程所用的一组内存地址不同于物理内存地址。当进程使用虚拟地址时,内核和MMU协同定位其在内存中的实际物理地址。(这句话是内存管理的核心,一定要默念一百遍!!!)
《深入了解Linux内核》学习笔记——第一章
这个图很形象的解释一个上面那句话,由物理地址到进程所用的地址需要经过这样几个处理。第一先经过MMU硬件单元处理(注意这里是硬件的分段分页)再经过Linux中的地址分配(这里是软件的分段分页)才真正为进程所用。也就是说硬件软件同时作用才形成了现在的内存管理机制!!!(很多参考书上解释的不是很清楚,一定要注意这点,再次强调!!)
18.进程虚拟地址空间处理。内核分配给进程的虚拟地址空间主要由以下几个内存区组成:程序可执行代码,初始化数据,未初始化数据,程序栈,所需共享库的可执行代码和数据,堆(由程序动态请求的内存)这里要说一个内存管理中很重要的特性:写时复制。当一个新进场被创建时,内核仅仅把父进程的页框赋给子进程的地址空间,但是这些页框标记为只读,一旦进场修改这个页中数据,就会产生一个异常,异常处理程序把新页框赋给受影响的进程,并用原来页中的内容初始化新页框。也就是说写时复制是通过异常的方法来实现的。
19,设备驱动(这是个大头,将来会用一章时间细讲)先介绍下farmwork
《深入了解Linux内核》学习笔记——第一章
有图可知,设备和设备驱动是相互对应的关系,不同的设备其驱动也不一样。而内核所做的事是将这些设备抽象为两大块文件,一个是字符设备文件,一个是块设备文件,再通过虚拟文件系统对其再次抽象,为应用提供统一的系统调用接口,这样应用就可以像是有文件一样使用这些设备了(不仅感叹这种设计太巧妙了。不是吗?)。抽象的思想!!

本章内容都是比较浅尝辄止的,接下来会用更多的时间慢慢剖析这些问题。