
Linux内核设计与实现
文章平均质量分 75
qinzhonghello
脚踏实地,风雨兼程,生命不息,战斗不止!!!
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
硬时钟和定时器
实时时钟 实时时钟RTC是用来持久存放系统时间的设备,即便系统关闭后它也可以靠主板上的微型电池提供的电力保持系统计时。在PC体系结构中,RTC和CMOS集成在一起,RTC的运行和BIOS的保存设置都是通过同一个电池供电的。 当系统启动时,内核通过读取RTC来初始化墙上时间,该时间存放在xtime变量中。虽然内核通常不会在系统启动后再读取xtime变量,但是有的体系结构如想86会周期原创 2008-12-23 10:59:00 · 1329 阅读 · 0 评论 -
内核同步方法之信号量
Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠。这时处理器能重获自由,从而去执行其他代码。当持有信号量的进程将信号量释放后,处于等待队列中的那个任务将被唤醒,并获得该信号量。 由信号量的睡眠特性得出的结论:由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况。原创 2008-12-20 11:45:00 · 1206 阅读 · 0 评论 -
内核同步方法之原子操作
原子操作可以保证指令以原子的方式执行,执行过程不被打断。 内核提供了两组原子操作接口:一组针对整数进行操作,另一组针对单独的位进行操作。大多数体系结构要么本来就支持简单的原子操作,要么就为单步执行提供了锁内存总线的指令。原子整数操作针对整数的原子操作只能对atomic_t类型的数据进行处理。引入该类型的原因:首先,让原子函数只接收atomic_t类型的操作数,可以确保原子操原创 2008-12-18 16:23:00 · 2777 阅读 · 0 评论 -
GCC内嵌汇编之语法详解
内嵌汇编语法如下: __asm__(汇编语句模板: 输出部分: 输入部分: 破坏描述部分) 共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:”格开,汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,也需要用“:”格开,相应部分内容为空。例如: __asm__ __volatile__("cli": : :转载 2008-12-18 16:18:00 · 1132 阅读 · 2 评论 -
内核同步方法之自旋锁
linux内核中最常见的锁是自旋锁(spin lock)。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被争用的自旋锁,那么该线程就会一直进行忙循环等待锁重新可用。要是锁未被争用,请求锁的执行线程便能立刻得到它,继续执行。在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。 一个被正用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别原创 2008-12-19 11:52:00 · 8651 阅读 · 1 评论 -
内核同步介绍
1.临界区和竞争条件 所谓临界区(critical region)就是访问和操作共享数据的代码段。为了避免在临界区中并发访问,必须保证这些代码是原子地执行,即代码在执行结束前不可被打断,就如同整个临界区是一个不可分割的指令一样。 如果两个执行线程有可能处于同一个临界区中,称为竞争条件(race condition)。 避免并发和防止竞争条件被称为同原创 2008-12-18 09:57:00 · 816 阅读 · 0 评论 -
老的BH机制
BH很古老。所有BH都是静态定义的,最多可以有32个。由于处理函数必须在编译时就被定义好,所以实现模块时不能直接使用BH接口。 每个BH处理程序都有严格地按顺序执行----不允许执行任何两个BH处理程序同时执行,即使它们类型不同。这样做倒是使同步变得简单了,可是却不利于多处理器的可扩展性,也不利于大型SMP的性能。使用BH的驱动程序很难从多个处理器上受益了。除了这些特点,BH原创 2008-12-15 20:48:00 · 1862 阅读 · 0 评论 -
使用tasklet
1.声明自己的tasklet 既可以静态地创建tasklet,也可以动态地创建它。如果静态地创建一个tasklet(直接引用),使用中的两个宏:#define DECLARE_TASKLET(name, func, data) / struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }原创 2008-12-15 17:04:00 · 7550 阅读 · 1 评论 -
使用工作队列
我们先来看一下默认的events任务队列,然后再看看创建新的工作者线程。 1.创建推后的工作 首先要做的是实际创建一些需要推后完成的工作。可以通过DECLARE_WORK在编译时静态地创建该结构体:在中#define DECLARE_WORK(n, f) / struct work_stru原创 2008-12-17 10:30:00 · 8188 阅读 · 0 评论 -
自旋锁与信号量
在中断上下文中只能用自旋锁,而在任务睡眠的时候只能用信号量。 自旋锁与信号量的比较 需求建议的加锁方法低开销加锁优先使用自旋锁短期锁定优先使用自旋锁长期加锁优先使用信号量中断上下文中加锁使用自旋锁持有锁需要睡眠使用信号量 优快云网友lioqio的回答:用处: 自旋锁用于多处理器之间的原创 2008-12-20 15:32:00 · 884 阅读 · 0 评论 -
顺序和屏障
当处理多处理器之间或硬件设备之间的同步问题时,有时需要在程序代码中以指定的顺序发出读内存(读入)和写内存(存储)指令。在和硬件交互时,时常需要确保一个给定的读操作发生在其他读或写操作之前。另外,在多处理上,可能需要按写数据时的顺序读数据(通常确保后来以同样的顺序进行读取)。但是编译器和处理器为了提高效率,可能对读和写重新排序。 所有可能重新排序和写的处理器提供了及其指令来确保原创 2008-12-22 15:56:00 · 1818 阅读 · 0 评论 -
定时器和时间管理
相对于事件驱动而言,内核中有大量的函数都是基于时间驱动的。 请注意相对时间和绝对时间之间的差别。相对时间绝对时间对内核时间管理来说都至关重要。 另外还要注意周期性产生的事件和推迟执行的事件之间的差别。 周期性产生的事件都是有系统定时器驱动的。系统定时器是一种可编程硬件芯片,它能以固定频率产生中断。该中断就是所谓的定时器中断,它所对应的中断处理程序负责更新系统时间,执行原创 2008-12-22 17:24:00 · 1254 阅读 · 0 评论 -
时钟中断处理程序
时钟中断处理程序可以划分为两个部分:体系结构相关部分和体系结构无关部分。 与体系结构相关的例程作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时,它都能够相应地运行。这部分处理程序主要完成:获得xtime_lock锁,以便对访问jiffies_64和墙上时间xtime进行保护需要时应答或重新设置系统时钟周期性地使用墙上时间更新实时时钟调用体系结构无关的原创 2008-12-23 11:33:00 · 8699 阅读 · 0 评论 -
jiffies
全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。 系统运行时间以秒为单位,等于jiffies/Hz。 注意,jiffies类型为无符号长整型(unsigned long),其他任何类型存放它都不正确。将原创 2008-12-23 10:58:00 · 10695 阅读 · 0 评论 -
内存管理[2]
Kmalloc()函数与用户空间的malloc()一族函数非常类似,只是它多了一个flags参数。用它可以获得以字节为单位的一块内核内存。 在中/* * Fallback definitions for an allocator not wanting to provide * its own optimized kmalloc definitions (like原创 2008-12-25 15:38:00 · 1579 阅读 · 0 评论 -
延迟执行
内核代码(尤其是驱动程序)除了使用定时器或下半部机制以外还需要其他方法来推迟执行任务。这种推迟通常发生在等待硬件完成某些工作时,而且等待时间非常短。 内核提供了许多延迟方法处理各种延迟要求。不同的方法有不同的处理特点,有些是在延迟任务时挂起处理器,防止处理器执行任何实际工作;另一些不会挂起处理器,所以也不能确保被延迟的代码能够在指定的延迟时间运行。忙等待 最简单的原创 2008-12-24 16:23:00 · 4990 阅读 · 0 评论 -
Seq锁
Seq锁是在2.6内核版本中引入的一种新型锁。这种锁提供了一种很简单的机制,用于读写共享数据。实现这种锁主要依靠一个序列计数器。当有疑义的数据被写入时,会得到一个锁,并且序列值会增加。在读取数据之前和之后,序列号都被读取。如果序列号值相同,说明读操作进行的过程中没有被写操作打断。如果读取的值是偶数,那么就表明写操作没有发生(锁的初值是0,写锁会使值变成奇数,释放的时候变成偶数)。原创 2008-12-20 17:19:00 · 1626 阅读 · 2 评论 -
BKL(大内核锁)
BKL(大内核锁)是一个全局自旋锁,使用它主要是为了方便实现从Linux最初的SMP过度到细粒度加锁机制。 BKL的特性:持有BKL的任务仍然可以睡眠 。因为当任务无法调度时,所加的锁会自动被抛弃;当任务被调度时,锁又会被重新获得。当然,并不是说,当任务持有BKL时,睡眠是安全的,紧急是可以这样做,因为睡眠不会造成任务死锁。BKL是一种递归锁。一个进程可以多次请求一个锁,并不会像自旋原创 2008-12-20 16:52:00 · 7706 阅读 · 0 评论 -
完成变量
如果在内核中一个任务需要发出信号通知另一个任务发生了某个特定事件,利用完成变量(complection variable)是使两个任务得以同步的简单方法。如果一个任务要执行一些工作时,另一个任务就会在完成变量上等待。当这个任务完成工作后,会使用完成变量去唤醒在等待的任务。在中struct completion { unsigned int done; wai原创 2008-12-20 16:02:00 · 2242 阅读 · 0 评论 -
内核同步方法之读写自旋锁
当对某个数据结构的操作可以像这样被划分为读/写两种类别时,类似读/写锁这样的机制就很有用了。Linux的读写自旋锁为读和写分别提供了不同的锁,一个或多个读任务可以并发的持有读者锁;用于写的锁最多只能被一个写任务持有,而且此时不能有并发的读操作。有时候读写锁叫做共享排斥锁,或者并发排斥锁,因为这种锁以共享(对读者而言)和排斥(对写着而言)的形式获得使用。/* * Rea原创 2008-12-19 17:11:00 · 4020 阅读 · 0 评论 -
工作队列的实现
工作队列(work queue)是另外一种将工作推后执行的形式。工作队列可以把工作推后,交由一个内核线程去执行----这个下半部分总是会在进程上下文执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许重新调度甚至是睡眠。 如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择软中断或tasklet。原创 2008-12-16 21:11:00 · 2637 阅读 · 0 评论 -
tasklet的实现【2】
2.调度tasklet 已触发的tasklet存放在两个单处理器结构中:tasklet_vec(普通tasklet)和tasklet_hi_vec(高优先级的tasklet)。这两个数据结构都是有tasklet_struct结构体构成的链表,链表中的每个tasklet_struct代表一个不同的tasklet。 tasklet由tasklet_schedule()和tas原创 2008-12-16 09:32:00 · 1846 阅读 · 0 评论 -
虚拟文件系统[5]
文件对象表示进程已打开的文件。进程直接处理的是文件,而不是超级块、索引节点或目录项。 文件对象是已打开文件在内存的表示。该对象(不是物理文件)由相应的open()系统调用创建,由close()系统调用销毁,所有这些文件相关的调用实际上都是文件操作表中定义的方法。 因为多个进程可以同时打开和操作同一个文件,所以同一个文件也可能存在多个对应的文件对象。文件对象仅仅在进原创 2009-01-05 16:43:00 · 859 阅读 · 0 评论 -
块IO层[1]
系统中能够随机(无序的)访问固定大小数据片(chunk)的设备被称作块设备,这些数据片就称为块。常见的块设备是磁盘,软盘驱动器,CD_ROM驱动器和闪存等。注意,它们都是以安装文件系统的方式使用的---这也是块设备通常的访问方式。 另一种基本的设备类型是字符设备。字符设备按照字符流的方式被有序访问。像串口和键盘都属于字符设备。 这两种类型的设备的根本区别在于它们是否可以被原创 2009-01-06 14:20:00 · 2529 阅读 · 1 评论 -
虚拟文件系统[6]
和文件系统相关的数据结构 内核使用一些标准数据结构来管理文件系统的其他相关数据。结构体file_system_type,用于描述各种特定的文件系统类型: 在中struct file_system_type { const char *name; int fs_flags; /*该函数从磁盘上读取超级块,并在文件系统被安装时,在内原创 2009-01-05 16:58:00 · 998 阅读 · 0 评论 -
虚拟文件系统[4]
VFS把目录当作文件对待,所以在路径/bin/vi中,bin和vi都属于文件---bin是特殊的目录文件而vi是一个普通文件,路径中的每个组成部分都由一个索引节点对象表示。虽然它们可以统一由索引节点表示,但是VFS经常需要执行目录相关的操作。比如路径名查找等。路径名查找需要解析路径中的每一个组成部分,不但要确保它有效,还要进一步寻址路径中的下一个部分。为了方便查找操作,VFS引入原创 2009-01-05 15:09:00 · 987 阅读 · 0 评论 -
虚拟文件系统[3]
索引节点对象包含了内核在操作文件或目录时需要的全部信息。对于Unix风格的文件系统来说,这些信息可以从磁盘索引节点直接读入。如果一个文件系统没有索引节点,不管这些相关信息在磁盘上是怎么存放的(没有索引节点的文件系统通常将信息作为文件的一部分存储起来,有些现代文件系统使用数据库来存储文件的数据。不管是那种方式索引节点对象必须在内核中创建以便文件系统使用),文件系统都必须从中提取这些信原创 2009-01-03 17:29:00 · 979 阅读 · 0 评论 -
进程管理
一个进程就是处于执行期的程序,但进程不仅仅局限于一段可执行程序代码,它还包括其他资源,如打开的文件,挂起的信号,内核内部数据,处理器状态,地址空间及一个或多个执行线程,用来存放全局变量的数据段等。进程是处于执行期的程序以及它所包含的资源的总称。 线程是在进程中活动的对象。每个线程都有一个独立的程序设计器,进程栈和一组进程寄存器。内核调度的对象是线程,而不是进程。Linux系统的原创 2008-11-08 09:14:00 · 1388 阅读 · 1 评论 -
内存管理[4]
slab分配器扮演了通用数据结构缓存层的角色。 slab分配器的概念首先在Sun Microsystem的SunOS 5.4操作系统中得以实现。Linux数据结构缓存层分享了它的名字和基本设计思想。 slab分配器试图在几个基本原则之间寻求一种平衡:频繁使用的数据结构也会频繁的分配和是否,因此应当适当缓存它们。频繁分配和回收必然会导致内存碎片。为了避免这种现象,原创 2008-12-29 21:03:00 · 922 阅读 · 0 评论 -
虚拟文件系统[2]
VFS对象及其数据结构 VFS其实采用的是面向对象的思想,使用一族数据结构来代表通用文件对象。这些数据结构表现得就像是对象。因为内核纯粹使用C代码实现,没有直接利用面向对象的语义,所以内核中的数据结构都使用C结构体实现,但这些结构体包含数据的同时也包含操作这些数据的函数指针,其中的操作函数由具体文件系统实现。 VFS中有四个主要的对象类型,它们分别是:1. 超级块原创 2008-12-31 17:19:00 · 1036 阅读 · 0 评论 -
进程地址空间[1]
内核除了管理本身的内存外,还必须管理进程的地址空间,即系统中每个用户空间进程所看到的内存。Linux采用虚拟内存技术,系统中的所有进程之间以虚拟方式共享内存。对每个进程来说,它们好像都可以访问整个系统的所有物理内存;即使单独一个进程,它拥有的地址空间也可以远远大于系统物理内存。 进程地址空间由每个进程中的线性地址区组成,而且更为重要的特点是内核允许进程使用该空间中的地址原创 2009-01-07 11:53:00 · 2978 阅读 · 2 评论 -
进程地址空间[2]
内存区域由vm_area_struct结构体描述,内存区域在内核中也被称作虚拟内存区域或VMA。vm_area_struct结构体描述了指定地址空间内连续区间上的一个独立内存范围。内核将每个内存区域作为一个单独的内存对象管理,每个内存区域都有一致的属性和操作: 在Mm.h中/* * Linux kernel virtual memory manager primit原创 2009-01-07 14:07:00 · 1378 阅读 · 0 评论 -
进程地址空间[4]
虽然应用程序操作的对象是映射到物理内存之上的虚拟内存,但是处理器直接操作的却是物理内存。所以当用程序访问一个虚拟地址时,首先必须将虚拟地址转化成物理地址,然后处理器才能解析地址访问请求。地址的转化工作需要通过查询也不才能完成,概括地讲,地址转换需要将虚拟地址分段,使每段虚拟地址都作为一个索引指向页表,而页表则指向下一级别的页表或者指向最终的物理页面。 linux中使用三级页表完成原创 2009-01-08 11:32:00 · 1393 阅读 · 0 评论 -
ksoftirqd内核线程
每个处理器都有一组辅助处理器软中断(和tasklet)的内核线程。当内核中出现大量软中断的时候,这些内核进程就会辅助处理它们。 引入ksoftirq内核线程的原因: 对于软中断,内核会选择在几个特殊时机进行处理。而在中断处理程序返回时处理是最常见的。软中断被触发的频率有时可能很高,更不利的是,处理函数有时还会字形重复触发,那么就会导致用户空间进程原创 2008-12-15 20:45:00 · 27980 阅读 · 7 评论 -
tasklet的实现【1】
tasklet由两类软中断代表:HI_SOFTIRQ,TASKLET_SOFTIRQ。这两者之间的唯一的区别在于前者的软中断优先于后者。1. tasklet结构体tasklet由tasklet_struct结构体表示。每个结构体单独代表一个tasklet,它在中定义:/* Tasklets --- multithreaded analogue of BHs.原创 2008-11-29 15:05:00 · 1165 阅读 · 2 评论 -
spin_lock宏和spin_unlock宏
今天看了第6章《中断与中断处理程序》,看到spin_lock和spin_unlock,于是便深入研究了一番。 spin_lock宏,其定义如下:#define spin_lock(lock) _spin_lock(lock) #define _spin_lock(lock) __LOCK(lock) #define __LOC原创 2008-11-25 10:59:00 · 5017 阅读 · 1 评论 -
中断处理机制的实现
在内核中,中断的旅程开始于预定义入口点,这类似于系统调用通过预定义的异常句柄进入内核。对应每条中断线,处理器都会跳到对应的一个唯一的位置。这样,内核就可以知道所接收中断的IRQ号了。初始入口点只是在栈中保存这个号,并存放当前寄存器的值(这些值属于被中断的任务);然后,内核调用函数do_IRQ()。下面的代码在/arch/i386/kernel/Irq.c中:/* *原创 2008-11-25 16:40:00 · 2095 阅读 · 0 评论 -
进程地址空间[3]
内核常常要判断进程地址空间中的内存区域是否满足某些条件,为了方便执行,内核定义了许多辅助函数,它们都声明在linux/mm.h中。 find_vma() 在mm/mmap.c中 /* Look up the first VMA which satisfies addr struct vm_area_struct * find_vma(struct原创 2009-01-07 16:29:00 · 1532 阅读 · 0 评论 -
页高速缓存和页回写[2]
由于页高速缓存的缓存作用,写操作实际上会被延迟。当页高速缓存中的数据比后台存储的数据更新时,那么该数据就被称为脏数据。在内存中积累起来的页最终必须被写回磁盘。在以下两种情况发生时,脏页被写回磁盘:1. 当空闲的内存低于一个特定的阈值时,内核必须将脏页写回磁盘,以便释放内存。2. 当脏页在内存中驻留时间超过一个特定的阈值时,内核必须将超时的脏页写回磁盘,以确保脏页不会无限期地驻留原创 2009-01-09 11:34:00 · 3276 阅读 · 0 评论 -
模块
Linux是单块内核(monolithic)的操作系统,整个系统内核都运行与一个单独的包含域中。Linux内核是模块化组成的,它允许内核在运行时动态地向其中插入或从中删除代码。这些代码包括相关的子例程,数据,函数入口和函数出口,被一并组合在一个单独的二进制镜像中,即所谓的可装载内核模块中,或简称为模块。 支持模块的好处就是基本内核镜像可以尽可能的小,因为可选的功能和驱动程序可以利用模原创 2009-01-09 17:30:00 · 2198 阅读 · 0 评论