深刻理解Linux内核(1)---基本概念

 1.Linux与其余著名的商用UNIX比较node

单块结构的内核(monolithic kernel 程序员

它是一个庞大的,复杂的自我完善(do-it-yourself)程序,由几个逻辑上独立的成分构成。这点上是至关传统的,大多商用UNIX也是单块结构,但另外的是appleMac Os XGNUHurd,用的微内核的方法算法

编译并静态连接的传统Unix内核 数组

能够动态安装和卸载部份内核代码(好比驱动)缓存

内核线程 bash

内核线程是一个能被独立调度的执行环境(context),线程之间切换比普通进程的上下文切换话费代价少量多,由于线程在同一个地址空间执行。内核线程能够跟应用程序有关,也能够无关。网络

支持多线程 session

Linux定义了本身的轻量级进程版本,商用unix变体都基于内核线程,而Linux吧轻量级进程看成基本的执行上下文,经过非标准的clone()系统调用来处理它们。数据结构

抢占式(preemptive)内核 多线程

多处理器支持

Linux 2.6以几乎最优化的方式使用SMP

文件系统

支持Ext2,Ext3jfs

STREAMS

大部分Unix内核包含了SRV4引入的STREAM I/O子系统,而且已变成编写驱动,网络协议的首选接口,但Linux没有此类子系统

2.Linux与商业Unix竞争的优点

Linux是免费的

Linux的全部成分均可以充分的定制

Linux能够运行在低档、便宜的意见平台上

Linux是强大的

Linux开发者都是很是出色的程序员

Linux内核很是小,并且紧凑

Linux与不少通用操做系统高度兼容

Linux有很好的技术支持

3.基本概念

操做系统必须完成两个主要目标:管理全部硬件,为全部app提供运行环境。Linux分用户态(User Mode)和内核态(Kernel Mode,以免用户直接操做硬件。

⑴多用户系统(multiusers system

多用户系统就是一台能并发(concurrently)和独立(independently)运行若干app的计算机。

并发:多个app能够竞争各类资源,如CPU,内存,硬盘等

独立:应用执行本身的任务,与其余应用无关。

多用户系统必须包含如下特色:

核实用户身份的认知机制

防止有错误的app妨碍其余app在系统运行的保护机制

防止有恶意的app干涉或窥视其余用户的活动的保护机制

限制分配给每一个用户的资源数的计账机制

Unix是实行系统资源硬件保护的多用户系统

(2)用户和组

在多用户系统中,每一个用户在机器上都有私用空间,私有的权限。特别是必须保证,没有用户可以开发一个侵犯其余用户私有空间的系统app.

每一个用户都有惟一的用户标志符(User ID,UID),隶属于一个或多个组的一名成员,组由惟一的用户组标志符(user group ID)标识,每一个文件都属于特定的用户和组,能够设置其相应权限。

Root用户拥有一切权利,几乎无所不能,能够访问任何文件,能够干涉任何正在执行的用户程序活动。

(3)进程

资源分配的基本单位,多道程序系统(multiprogramming)或多处理系统(multiprocessing)同时执行的进程数是有限的,由调度程序(scheduler)决定那个进程执行。

多用户系统必须是可抢占式的(preemptable),操做系统记录下每一个进程占有CPU时间,并周期性地激活调度程序。

Unix是具备抢占式的多处理操做系统,即便没有用户登陆,没有程序运行,也有几个系统进程在监视外围设备,好比监听终端等待用户登陆。

Unix操做系统采用进程/内核模式,每一个进程都自认为是系统中惟一的进程,能够独占OS所提供的服务,只要进程发出系统调用,硬件就会把特权模式由用户态变成内核态,而后进程以很是有限的目的开始一个内核过程的执行,这样,操做系统在进程的执行上下文中做用,以知足进程请求,一旦这个请求获得知足,内核过程将迫使硬件返回用户态,而后进程在系统调用的下一条指令继续执行。

(4)内核体系结构

大部分Unix内核是单块结构:每个内核层都被集成到整个内核程序中,并表明当前进程的内核态下运行。相反,微内核(microkernel)只须要内核有一个很小的函数集,它要求程序员采用模块化的方法,由于任何操做系统层都是一个相对独立的程序,这种程序必须经过清晰明确的软件接口与其余层交互,容易移植,且能比单块内核更充分利用RAM,由于暂且不须要执行的系统进程能够被调出或撤销,但微内核通常比单块内核效率低,由于操做系统不一样层次之间显示的消息传递要花费必定的代价。

为了达到微内核理论上的不少优势又不影响性能,Linux内核提供了模块(module)。模块是一个目标文件,其代码能够运行时链接到内核或卸载,一般用来实现文件系统,驱动程序或其余内核上层功能。与微内核操做系统的外层不一样,模块不是做为一个特殊的进程执行的,相反,与任何其余静态连接的内核函数同样,它表明当前进程在内核态下执行。

使用模块化主要优势包括:

①模块化方法:能够在运行时动态加载或卸载,所以系统程序员必须提供良好定义的软件接口以访问由模块处理的数据结构,这使得开发新模块变得容易。

②平台无关性:即便模块依赖于某些特殊的硬件特色,但它不依赖于某个固定的硬件平台

③节省内存使用:当须要模块功能时,把它连接到运行的内核中,不然将该模块解除,这种机制对于小型嵌入式系统很是有用

④无性能损失:模块的目标代码一旦被连接到内核,其做用与静态连接的内核的目标代码彻底等价,所以当模块函数被调用时,无需显示地进行消息传递。

(5)文件

Unix文件是以字节序列组成的信息载体(container),内核不解释文件的内容,不少库函数实现了更高级的抽象,例如由字段构成的记录,从用户观点来看,文件被组织在一个树结构的命名空间中,

除了叶节点外,树的全部节点都表示目录名,目录节点包含它下面文件及目录的全部信息。文件和目录名由处”/”和空字符”\0”以外的任意ASCII字符序列组成,大多数文件系统对文件名长度都有限制,一般不超过255字符。

Unix的每一个进程都有一个当前工做目录,它属于进程执行上下文(execution context),标志出进程所用的当前目录。分绝对路径和相对路径。

(6)硬连接和软链接

包含在目录中的文件名就是一个文件的硬连接(hard link),或简称连接(link),在同一目录或不一样目录中,同一文件能够有几个连接,所以对应几个文件名。

$ ln p1 p2

用来建立一个新的硬连接,即为路径p1标识的文件建立一个路径为p2的硬连接。

硬连接有两个限制:

①不容许给目录建立硬连接,防止把目录树变为环形图

②只有在同一个文件系统的文件之间才能建立连接。

 

这带来很大限制,由于现代Unix系统可能包含多种文件系统,这些文件系统位于不一样的磁盘或分区,用户没法知道他们之间的物理划分,为了克服这些限制,所以引入了软链接(soft link)也叫符号连接(symbol link),符号连接是短文件,能够指向任何路径名,甚至指向不存在文件。

$ ln –s p1 p2

建立一个路径名为P2的新软连接,指向p1

(7)文件类型

Unix文件能够是下列类型之一:

普通文件(regular file)

目录

符号连接

面向快的设备文件(block-oriented device file)

面向字符的设备文件(character-oriented device file)

管道(pipe)和命名管道(named pipe)(也叫FIFO

套接字(socket)

前三个是Unix文件系统的基本类型,设备文件与驱动程序相关,访问文件时即直接访问I/O设备,管道和套接字是用于进程间通讯的特殊文件。

 

(8)文件描述符与索引节点

Unix对文件的内容描述和描述文件的信息给出了清楚的区分,除了设备文件和特殊文件外,每一个文件都有字符序列组成,文件内容不包含任何控制信息,如文件长度,权限等。

文件系统处理文件须要的全部信息包含在一个名为索引节点(inode)的数据结构中,每一个文件都有本身的索引节点,文件系统用索引节点来标志文件。

虽然文件系统及内核函数对索引节点的处理可能随Unixixt不一样有所不一样,但通常都必须至少提供在POSIX标准中指定的以下属性:

文件类型

与文件相关的硬连接个数

以字节为单位的文件长度

设备标志符

在文件系统中标志文件的索引节点号

文件拥有者的UID

文件的用户组ID

几个时间戳,表示索引节点改变时间、最后访问时间及最后修改时间

访问权限和文件模式

(9)访问权限和文件模式

文件的潜在用户分为三种类型

文件拥有者用户

同组用户,不包括全部者

其余用户

有三种访问权限读、写及执行。还有三种附加的标记

Suid:(set User ID)进程执行一个文件时一般保持进程拥有者UID,若是设置了可执行文件的suid标志位,进程就得到了该文件拥有者的UID

Sgid(Set Group ID):进程执行一个文件时保持进程组的组ID,若是设置了可执行文件的sgid标志位,进程就得到了该文件用户组的ID

Sticky:

当文件由一个进程建立时,文件拥有者ID就是该进程UID,而其组用户ID能够是进程建立者的ID,也能够是父目录的ID,取决于副目录的sgid标志位

(10)文件操做的系统调用

当用户访问一个普通文件或目录内容时,他其实是访问存储在硬件块设备上的一些数据,从这个意义上说,文件系统是硬盘分区物理组织的用户及视图。

①打开文件 fd= open(path,flag,mode)

这个系统调用建立一个“打开文件”对象,并返回文件描述符(file descriptor).

这个对象包括:

文件操做的一些数据结构,如打开方式,偏移等/

进程能够调用的一些内核函数指针

文件描述符表示进程与打开文件之间的交互,而打开文件对象包含了与这种交互相关的数据。同一个文件对象也许由几个文件描述符标志,而且Unix文件系统在同一文件上发出的I/O操做之间不提供任何形式的同步机制。

②访问文件

Lseek,read,write,close(fd)

③改名及删除

Res = rename(oldpath,newpath),改变了文件连接的名字

Res=unlink(pathname),减小文件链接数,删除了相应目录项,当链接数为0时,文件才被真正删除

 

11)进程/内核模式

CPU既能够运行在用户态,也能够运行在内核态下,实际上一些CPU能够运行两种以上执行状态,如Intel80x86微处理器有四种不一样的执行状态,ARM有其中执行状态,但全部标准的Unix内核都仅仅利用了内核态和用户态。

进程是动态的尸体,在系统内一般只有有限的生命期。

内核自己并非一个进程,而是进程的管理者。

 

除用户进程外,Unix系统还包括几个内核线程(kernel thread)的特权进程,它们有如下特色:

它们之内核态运行在内核地址空间

他们不与用户直接交互,所以不须要终端设备

他们一般在系统启动时建立,而后一直处于活跃状态知道系统关闭

Unix内核的工做远不止处理系统调用,实际有4中方式激活内核例程

①进程调用系统调用

CPU发出异常(exception)信号

③外围向CPU发送中断(interrupt)信号

④内核线程被执行。

 

为了让内核管理进程,每一个进程由一个进程描述符(process description)表示,这个进程描述符包含有关进程当前状态的信息。

当内核暂停一个进程执行时,就把几个相关寄存器内容保存进进程描述符(PC,SP,通用寄存器,浮点寄存器,处理器状态字),当内核决定恢复执行一个进程时,用进程描述符中合适的字段来装载CPU寄存器。

(12)可重入内核

全部的Unix内核都是可重入的(reentrant,这意味着若干进程能够同时在内核下执行,提供可重入的一种方式是,编写内核函数只改变局部变量不改变全局结构,尽管如此(一些实时内核就是如此实现),可重入内核能够包括非重入函数,而且利用鎖机制保证一次只有一个进程执行的非重入函数。

内核控制路径(kernel control path):表示内核处理系统调用、异常或中断所执行的质量序列。

当下属事件之一发生时,CPU交错执行内核控制路径:

①进程调用一个系统调用,相应内核控制路径证明该请求没法当即知足,内核控制路径调用调度程序选择一个新的进程投入运行,结果进程切换发生,第一个内核控制路径没完成,而CPU又从新开始执行其余内核控制路径,这种状况下,两条控制路径表明两个不一样的进程在执行。

②当运行一个内核控制路径时,CPU检测到一个异常

③当CPU正在运行一个启动了中断的内核控制路径,硬件中断发生

④在支持抢占式调度的内核中,CPU正在运行,而一个更高优先级的进程加入就绪队列,则中断发生。

 

(13)进程地址空间

每一个进程运行在它的私有地址空间,在用户态下运行的进程设计有私有栈,数据区和代码区,当在内核态运行时,进程访问内核的数据区和代码区,可是用另外私有的栈。

由于内核是可重入的,所以几个内核控制路径(对应不一样进程相关)能够轮流执行,这种状况下,每一个内核控制路径都引用它本身的私有内核栈。

尽管看起来每一个进程访问一个私有地址空间,但有时进程之间也共享部分地址空间。

①用户空间的进程间内存共享。

②同一个程序由几个用户同时使用,则这个程序只被装入内存一次,其指令由全部须要它的用户共享。

mmap()系统调用,容许块设备上文件映射到进程的部分地址空间。

 

14)同步和临界区

当调度多个进程访问共享数据时,存在一种竞争条件(race condition),实现可重入内核须要利用同步机制。

①非抢占式内核

在寻找完全、简单地解决同步问题的方案中,大多数传统的Unix内核都是非抢占式的。

若是内核支持抢占,那么在应用同步机制时,确保进入临界区前禁止抢占,退出临界区时开启抢占。

②禁止中断

单处理器系统上,在进入临界区时,禁止全部硬件中断,离开临界区时再重启中断。

但在多处理器系统中禁止本地CPU中断时不够的,全部仍是要采用其余的同步技术。

③信号量

普遍使用的一种机制是信号量(semaphore),它在单处理器,多处理器系统中都有效。能够把信号量看做一个对象,它包括:

一个整数变量

一个等待进程的链表

两个原子方法:down()和up()

每一个要保护的数据结构都有它本身的信号量,其初始值为1.down()方法对信号量值减1,up()对信号量值加1,若信号量值小于0,则加入到等待链表中,且当前进程挂起,若大于0,则正常访问数据。

④自旋锁

在多处理系统中,信号量并不老是解决同步的最好办法。自旋锁与信号量相似,但没有链表,当一个进程发现锁被另外一个进程持有时,它不停的“旋转”,执行一个紧凑的循环指令制导锁打开。

在单处理器环境下,自旋锁是无效的,由于当一个进程等待锁时,永远得不到锁释放,系统将挂起。

⑤避免死锁

只要涉及到内核设计,当所用内核信号量数量较多时,死锁就成为一个突出问题。Linux经过按规定的循序请求信号量来避免死锁,例如哲学家就餐问题。

(15)信号与进程间通讯

Unix信号(signal)提供了把系统事件报告给进程的一种机制,有两种系统事件:

异步通告---例如当用户在终端按下中断建(CTRL+C,即向前台发送一个中断信号SIGINT

同步错误或异常---例如当进程访问非法内存地址时,内核向这个进程发送一个SIGSEGV信号

通常来讲,进程能够有两种方式对信号作出反应:忽略该信号,异步地执行一个指定过程(信号处理函数)

POSIX标准定义了大约20种不一样信号,其中有2个是用户自定义的。

 

若是进程不指定选择何种方式,内核就根据信号的编号执行一个默认操做,5种可能的默认操做是:

①终止进程

②将执行上下文或进程地址空间内容写入一个文件(核心转储,core dump)并终止进程

③忽略信号

④挂起进程

⑤若是进程曾被暂停,则恢复它的执行。

AT&TUnix System V引入了在用户态下进程间通讯机制:信号量,消息队列及共享内存,统称为System V IPC。共享内存是进程间交换和共享数据的最快方式。

16)进程管理

Unix在进程和正在执行的程序之间作出了清晰划分。Fork()_exit()分别用来建立和终止一个进程,而exec()类系统调用则是装入一个新程序,该新程序替换了当前进程的正文、数据、堆和栈段,进程ID不变。

实现fork()是将父进程数据和代码彻底复制,这会至关费时,当前以来硬件分页单元的内核采用写时复制(Copy-On-Write)技术,及把页的复制延迟到最后一刻(子进程须要时才写进页)

_exit()系统调用终止一个进程,内核对这个系统调用处理是释放内核拥有的资源并向父进程发送SIGCHILD信号(默认操做为忽略)

17)僵尸进程(zombie precess)

父进程调用wait4()等待其中一个子进程结束,它返回已终止子进程的进程标志符(Precess IDPID),若是没有子进程退出,该进程就设置成等待状态,一直到子进程结束。

若在父进程调用wait4()以前,子进程就死掉了,则成为僵死进程。

解决方法是init进程,它在系统初始化时建立,当一个进程终止时,内核改变其全部现有子进程的进程描述符指针,收养全部这些进程为init的子进程。Init监控全部子进程的执行,而且按常规发布wait4(),这样能够除掉全部僵尸进程。

18)进程组和登录会话

现代Unix引入进程组(process group)的概念,以表示一种做业(job)的抽象。

$ls |sort |more

Shell支持进程组,例如bash,为三个相应进程ls,sort,more建立一个进程组,每一个进程组有一个领头进程(其PID与进程组ID相同的进程),新建立的进程最初被插入到父进程的进程组中。

登录会话(login session):一个登录会话包含指定终端已经开始工做会话的那个进程的全部后代进程。进程组中的全部进程必须在同一个登录会话中。只有一个进程组一直处于前台,它能够访问中断,其余活动着的进程组在后台,后台进程访问中断时,将受到SIGTTINSIGTTOUT信号,能够用bgfg把一个进程放在后台或前台。

19)内存管理

①虚拟内存(virtual memory

虚拟内存有多个有点和用途:

若干个进程能够并发地执行

应用程序所需内存大于可用物理内存时也能够运行

程序只有部分代码装入内存时,进程也能够执行它

容许每一个进程访问可用物理内存的子集

进程能够共享库函数或程序的一个单独内存映像

程序能够重定位,便可以在任何地址执行

程序员能够编写机器无关代码

如今的CPU包含了能自动把虚拟地址转换成物理地址的硬件电路,用页表来指定虚拟地址和物理地址的对应关系。

②随机访问存储器(RAM)的使用

内核把RAM分为两个部分,一部分专门用于放内核映像,其余部分由虚拟内存系统来处理。

虚拟内存系统必须解决的一个主要问题是内存碎片。

③内核内存分配器(Kernel Memory AllocatorKMA

它试图知足系统中全部部分对内存的请求,一个好的KMA应具备下列特色:

必须快

必须把内存的浪费减到最小

必须努力减轻内存的碎片问题(fragmentation

必须能与其余内存管理子系统合做,以便借用和释放页框。

基于各类不一样的算法技术,已经提出了几种KMA,包括

资源分配算法(allocator

2的幂次方空闲链表

McKusick-Karels分配算法

伙伴(Buddy)系统

Mach的区域(Zone)分配算法

Dynix分配算法

SolarisSlab分配算法

④进程虚拟地址空间处理

内核分配给进程的虚拟地址空间由如下内存组成:

程序的可执行代码

程序的初始化数据

程序的未初始化数据

初始程度栈(即用户态栈)

所需共享库的可执行代码和数据

堆(用于程序动态请求内存)

全部现代Unix操做系统采用了请求调页(demand paging)的内存分配策略。

⑤高速缓存

最先在Unix系统实现的一个策略是,尽量地推迟写磁盘的时间,从磁盘读入内存的数据集市进程再也不使用它们,他们也留在RAM中,当一个进程请求访问磁盘时,内核首先检查所请求数据是否在内存中,若在(叫作缓存命中),内核就不用访问磁盘了。

Sync()系统调用把全部“脏”的缓冲区(缓冲与磁盘内容不同),写入磁盘来强制磁盘同步。

20)设备驱动程序

设备驱动包含在内核中,由一个或多个设备的数据结构和函数组成,经过特定的接口,每一个驱动程序与内核的其他部分相互做用。

相关文章
相关标签/搜索