当前位置:文档之家› Linux内核的等待队列

Linux内核的等待队列

Linux内核的等待队列
Linux内核的等待队列

Linux内核的等待队列

Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。在Linux2.4.21中,等待队列在源代码树include/linux/wait.h中,这是一个通过list_head连接的典型双循环链表,如下图所示。

在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为"连接件"。由于我们只需要对队列进行添加和删除操作,并不会修改其中的对象(等待队列项),因此,我们只需要提供一把保护整个基础设施和所有对象的锁,这把锁保存在等待队列头中,为wq_lock_t类型。在实现中,可以支持读写锁(rwlock)或自旋锁(spinlock)两种类型,通过一个宏定义来切换。如果使用读写锁,将wq_lock_t定义为rwlock_t类型;如果是自旋锁,将wq_lock_t 定义为spinlock_t类型。无论哪种情况,分别相应设置wq_read_lock、

wq_read_unlock、wq_read_lock_irqsave、wq_read_unlock_irqrestore、wq_write_lock_irq、wq_write_unlock、wq_write_lock_irqsave和

wq_write_unlock_irqrestore等宏。

等待队列头

struct __wait_queue_head {

wq_lock_t lock;

struct list_head task_list;

};

typedef struct __wait_queue_head wait_queue_head_t;

前面已经说过,等待队列的主体是进程,这反映在每个等待队列项中,是一个任务结构指针(struct task_struct * task)。flags为该进程的等待标志,当前只支持互斥。

等待队列项

struct __wait_queue {

unsigned int flags;

#define WQ_FLAG_EXCLUSIVE 0x01

struct task_struct * task;

struct list_head task_list;

};

typedef struct __wait_queue wait_queue_t;

声明和初始化

#define DECLARE_WAITQUEUE(name, tsk) \

wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

#define __WAITQUEUE_INITIALIZER(name, tsk) { \

task: tsk, \

task_list: { NULL, NULL }, \

__WAITQUEUE_DEBUG_INIT(name)}

通过DECLARE_WAITQUEUE宏将等待队列项初始化成对应的任务结构,并且用于连接的相关指针均设置为空。其中加入了调试相关代码。

#define DECLARE_WAIT_QUEUE_HEAD(name) \

wait_queue_head_t name =

__WAIT_QUEUE_HEAD_INITIALIZER(name)

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \

lock: WAITQUEUE_RW_LOCK_UNLOCKED, \

task_list: { &(name).task_list, &(name).task_list }, \

__WAITQUEUE_HEAD_DEBUG_INIT(name)}

通过DECLARE_WAIT_QUEUE_HEAD宏初始化一个等待队列头,使得其所在链表为空,并设置链表为"未上锁"状态。其中加入了调试相关代码。

static inline void init_waitqueue_head(wait_queue_head_t *q) 该函数初始化一个已经存在的等待队列头,它将整个队列设置为"未上锁"状态,并将链表指针prev和next指向它自身。

{

q->lock = WAITQUEUE_RW_LOCK_UNLOCKED;

INIT_LIST_HEAD(&q->task_list);

}

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)

该函数初始化一个已经存在的等待队列项,它设置对应的任务结构,同时将标志位清0。

{

q->flags = 0;

q->task = p;

}

static inline int waitqueue_active(wait_queue_head_t *q)

该函数检查等待队列是否为空。

{

return !list_empty(&q->task_list);

}

static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)

将指定的等待队列项new添加到等待队列头head所在的链表头部,该函数假设已

经获得锁。

{

list_add(&new->task_list, &head->task_list);

}

static inline void __add_wait_queue_tail(wait_queue_head_t

*head, wait_queue_t *new)

将指定的等待队列项new添加到等待队列头head所在的链表尾部,该函数假设已经获得锁。

{

list_add_tail(&new->task_list, &head->task_list);

}

static inline void __remove_wait_queue(wait_queue_head_t

*head, wait_queue_t *old)

将函数从等待队列头head所在的链表中删除指定等待队列项old,该函数假设已经获得锁,并且old在head所在链表中。

{

list_del(&old->task_list);

}

睡眠和唤醒操作

对等待队列的操作包括睡眠和唤醒(相关函数保存在源代码树的/kernel/sched.c 和include/linux/sched.h中)。思想是更改当前进程(CURRENT)的任务状态,并要求重新调度,因为这时这个进程的状态已经改变,不再在调度表的就绪队列中,因此无法再获得执行机会,进入"睡眠"状态,直至被"唤醒",即其任务状态重新被修改回就绪态。

常用的睡眠操作有interruptible_sleep_on和sleep_on。两个函数类似,只不过前者将进程的状态从就绪态(TASK_RUNNING)设置为

TASK_INTERRUPTIBLE,允许通过发送signal唤醒它(即可中断的睡眠状态);而后者将进程的状态设置为TASK_UNINTERRUPTIBLE,在这种状态下,不接收任何singal。以interruptible_sleep_on为例,其展开后的代码是:

void interruptible_sleep_on(wait_queue_head_t *q)

{

unsigned long flags;

wait_queue_t wait;

/* 构造当前进程对应的等待队列项*/

init_waitqueue_entry(&wait, current);

/* 将当前进程的状态从TASK_RUNNING改为TASK_INTERRUPTIBLE */ current->state = TASK_INTERRUPTIBLE;

/* 将等待队列项添加到指定链表中*/

wq_write_lock_irqsave(&q->lock,flags);

__add_wait_queue(q, &wait);

wq_write_unlock(&q->lock);

/* 进程重新调度,放弃执行权*/

schedule();

/* 本进程被唤醒,重新获得执行权,首要之事是将等待队列项从链表中删除*/ wq_write_lock_irq(&q->lock);

__remove_wait_queue(q, &wait);

wq_write_unlock_irqrestore(&q->lock,flags);

/* 至此,等待过程结束,本进程可以正常执行下面的逻辑*/

}

对应的唤醒操作包括wake_up_interruptible和wake_up。wake_up函数不仅可以唤醒状态为TASK_UNINTERRUPTIBLE的进程,而且可以唤醒状态为TASK_INTERRUPTIBLE的进程。wake_up_interruptible只负责唤醒状态为TASK_INTERRUPTIBLE的进程。这两个宏的定义如下:

#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1)

#define

wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE, 1)

__wake_up函数主要是获取队列操作的锁,具体工作是调用

__wake_up_common完成的。

void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr) {

if (q) {

unsigned long flags;

wq_read_lock_irqsave(&q->lock, flags);

__wake_up_common(q, mode, nr, 0);

wq_read_unlock_irqrestore(&q->lock, flags);

}

}

/* The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve number) then we wake all the non-exclusive tasks and one exclusive task.

There are circumstances in which we can try to wake a task which has already started to run but is not in state

TASK_RUNNING. try_to_wake_up() returns zero in this (rare) case, and we handle it by contonuing to scan the queue. */

static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, int nr_exclusive, const int sync)

参数q表示要操作的等待队列,mode表示要唤醒任务的状态,如

TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE等。nr_exclusive是要唤醒的互斥进程数目,在这之前遇到的非互斥进程将被无条件唤醒。sync表示???

{

struct list_head *tmp;

struct task_struct *p;

CHECK_MAGIC_WQHEAD(q);

WQ_CHECK_LIST_HEAD(&q->task_list);

/* 遍历等待队列*/

list_for_each(tmp,&q->task_list) {

unsigned int state;

/* 获得当前等待队列项*/

wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);

CHECK_MAGIC(curr->__magic);

/* 获得对应的进程*/

p = curr->task;

state = p->state;

/* 如果我们需要处理这种状态的进程*/

if (state & mode) {

WQ_NOTE_WAKER(curr);

if (try_to_wake_up(p, sync) &&

(curr->flags&WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)

break;

}

}

}

/* 唤醒一个进程,将它放到运行队列中,如果它还不在运行队列的话。"当前"进程总是在运行队列中的(except when the actual re-schedule is in progress),and as such you're allowed to do the simpler "current->state =

TASK_RUNNING" to mark yourself runnable without the overhead of this. */

static inline int try_to_wake_up(struct task_struct * p, int synchronous) {

unsigned long flags;

int success = 0;

/* 由于我们需要操作运行队列,必须获得对应的锁*/

spin_lock_irqsave(&runqueue_lock, flags);

/* 将进程状态设置为TASK_RUNNING */

p->state = TASK_RUNNING;

/* 如果进程已经在运行队列中,释放锁退出*/

if (task_on_runqueue(p))

goto out;

/* 否则将进程添加到运行队列中*/

add_to_runqueue(p);

/* 如果设置了同步标志*/

if (!synchronous || !(p->cpus_allowed & (1UL <<

smp_processor_id())))

reschedule_idle(p);

/* 唤醒成功,释放锁退出*/

success = 1;

out:

spin_unlock_irqrestore(&runqueue_lock, flags);

return success;

}

等待队列应用模式

等待队列的的应用涉及两个进程,假设为A和B。A是资源的消费者,B是资源的生产者。A在消费的时候必须确保资源已经生产出来,为此定义一个资源等待队列。这个队列同时要被进程A和进程B使用,我们可以将它定义为一个全局变量。DECLARE_WAIT_QUEUE_HEAD(rsc_queue); /* 全局变量*/

在进程A中,执行逻辑如下:

while (resource is unavaiable) {

interruptible_sleep_on( &wq );

}

consume_resource();

在进程B中,执行逻辑如下:

produce_resource();

wake_up_interruptible( &wq );

探究linux内核,超详细解析子系统

探究linux内核,超详细解析子系统 Perface 前面已经写过一篇《嵌入式linux内核的五个子系统》,概括性比较强,也比较简略,现在对其进行补充说明。 仅留此笔记,待日后查看及补充!Linux内核的子系统 内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。 Linux内核把不同功能分成不同的子系统的方法,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。系统调用接口用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用。系统调用是Linux内核提供的,用户空间无法直接使用系统调用。在用户进程使用系统调用必须跨越应用程序和内核的界限。Linux内核向用户提供了统一的系统调用接口,但是在不同处理器上系统调用的方法

各不相同。Linux内核提供了大量的系统调用,现在从系统 调用的基本原理出发探究Linux系统调用的方法。这是在一个用户进程中通过GNU C库进行的系统调用示意图,系 统调用通过同一个入口点传入内核。以i386体系结构为例,约定使用EAX寄存器标记系统调用。 当加载了系统C库调用的索引和参数时,就会调用0x80软件中断,它将执行system_call函数,这个函数按照EAX 寄存器内容的标示处理所有的系统调用。经过几个单元测试,会使用EAX寄存器的内容的索引查system_call_table表得到系统调用的入口,然后执行系统调用。从系统调用返回后,最终执行system_exit,并调用resume_userspace函数返回用户空间。 linux内核系统调用的核心是系统多路分解表。最终通过EAX寄存器的系统调用标识和索引值从对应的系统调用表 中查出对应系统调用的入口地址,然后执行系统调用。 linux系统调用并不单层的调用关系,有的系统调用会由

简析linux内核的内核执行流程图

简析linux核的执行流程 ----从bootsect.s到main.c(核版本0.11)Linux启动的第一阶段(从开机到main.c) 3个任务: A、启动BIOS,准备实模式下的中断向量表和中断服务程序。 B、从启动盘加载操作系统程序到存。 C、为执行32的main函数做过渡准备。 存变化如下: ①、0xFE000到0xFFFFF是BIOS启动块,其中上电后第一条指令在0xFFFF0。 ②、而后0x00000到0x003FF总共1KB存放中断向量表,而接下去的地址到0x004FF共256B存放BIOS数据,从0x0E05B 开始的约8KB的存中存放中断服务程序。 ③、利用BIOS中断0x19h把硬盘的第一扇区bootsect.s的代码加载到存中,即0x07c00处,后转到该处执行。 ④、将bootsect.s的代码复制到0x90000处。 ⑤、利用中断0x13h将setup.s程序加载到存0x90200处。 ⑥、再将剩余的约240个扇区的容加载到0x10000~0x2EFFF 处。 ⑦、开始转到setup.s处执行,第一件事就利用BIOS提供的中断服务程序从设备上获取核运行的所需系统数据并存在0x90000的地址处,这时将原来bootsect.s的代码覆盖得只剩2Byte的空间。

⑧、关中断并将系统代码复制到0x00000处,将原来放在这里的中断向量表与BIOS数据区覆盖掉,地址围是 0x00000~0x1EFFF。同时制作两表与两寄存器。 ⑨开地址线A20,寻址空间达到4GB,后对8259重新编程,改变中断号。 ⑩、转到head.s(大小是25K+184B)执行,执行该程序完后是这样的: 0x00000~0x04FFF:页目录与4个页表,每一项是4KB,共20KB;0x05000~0x05400:共1KB的空间是软盘缓冲区; 0x05401~0x054b8:共184B没用; 0x054b9~0x05cb8:共2KB的空间存中断描述符表; 0x05cb9~0x064b8:共2KB的空间存全局描述符表; 之后就是main函数的代码了! 第二阶段、从main.c函数到系统准备完毕阶段。 第一步:创建进程0,并让进程0具备在32位保护模式下载主机中的运算能力。流程是: 复制根设备和硬盘参数表(main.c中的102、110、111行) 物理存规划格局(main.c的112行~126行,其中有 rd_init函数定义在kernel/ramdisk.c中,此函数用于虚拟盘初始化;而mem_init函数是用于存管理结构初始化,定义在mem/memory.c中,该函数页面使用

Linux内核与跟文件系统的关系

Linux内核与根文件系统的关系 开篇题外话:对于Linux初学者来说,这是一个很纠结的问题,但这也是一个很关键的问题!一语破天机:“尽管内核是Linux 的核心,但文件却是用户与操作系统交互所采用的主要工具。这对Linux 来说尤其如此,这是因为在UNIX 传统中,它使用文件I/O 机制管理硬件 设备和数据文件。” 一.什么是文件系统 文件系统指文件存在的物理空间,linux系统中每个分区都是一个文件系统,都有自己的目 录层次结构。 Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件、目录、软连接及文件保护信息等都存储在其 中。这种机制有利于用户和操作系统的交互。 每个实际文件系统从操作系统和系统服务中分离出来,它们之间通过一个接口层:虚拟文件系统或VFS来通讯。VFS使得Linux可以支持多个不同的文件系统,每个表示一个VFS 的通用接口。由于软件将Linux 文件系统的所有细节进行了转换,所以Linux核心的其它部分及系统中运行的程序将看到统一的文件系统。Linux 的虚拟文件系统允许用户同时能透明地安装 许多不同的文件系统。 在Linux文件系统中,EXT2文件系统、虚拟文件系统、/proc文件系统是三个具有代表性的 文件系统。 二.什么是根文件系统 根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。 那么根文件系统在系统启动中到底是什么时候挂载的呢?先将/dev/ram0挂载,而后执行/linuxrc.等其执行完后。切换根目录,再挂载具体的根文件系统.根文件系统执行完之后,也就是到了Start_kernel()函数的最后,执行init的进程,也就第一个用户进程。对系统进行各 种初始化的操作。 根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,既然是根的话,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。它包含系统引导和使其他文件系统得以挂载(mount)所必要的文件。根文件系统包括Linux启动时所必须的目录和关键性的文件,例如Linux启动时都需要有init目录下的相关文件,在Linux挂载分区时Linux 一定会找/etc/fstab这个挂载文件等,根文件系统中还包括了许多的应用程序bin目录等,任何包括这些Linux 系统启动所必须的文件都可以成为根文件系统。Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。在Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂载(mount)。使用mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。根文件系统被挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbin /mnt等,再将其他分区挂接到/mnt 目录上,/mnt目录下就有这个分区的各个目录,文件。

Linux内核与驱动开发实验教材

内核与驱动开发实验教材 中程在线 实验一嵌入式开发环境的建立 实验目的 掌握嵌入式开发环境的构建,熟悉课程实验的开发板 掌握安装交叉编译工具的安装方法 掌握的烧写方法 掌握的编译方法 实验内容 安装交叉编译工具 编译 烧写 生成映像 基础知识 交叉编译工具 嵌入式系统的开发中,开发环境被称为主机。因为嵌入式目标系统的资源局限性,不可能完成构建系统的任务,所以需要主机使用交叉编译工具来构建目标系统。 实验使用交叉编译器,与桌面系统采用的编译器是不同,因为实验开发板采用的是处理器。 编译器将使用下列工具 , 与通常在平台上使用的工具不同,交叉编译工具编译处理的执行文件只能够在平台上运行。 嵌入式系统构建 一个嵌入式系统从软件的角度看通常可以分为四个层次: .引导加载程序()。引导加载程序是系统加电后运行的第一段软件代码。 . 内核。特定于嵌入式板子的定制内核以及内核的启动参数。 . 文件系统。包括根文件系统和建立于内存设备之上文件系统。通常用来作为。 .用户应用程序。特定于用户的应用程序。

主要的功能有: 初始化硬件,初始化, , , , 。 启动,这是最重要的功能,保存内核映像到中,并跳转到内核起始地址。 映像下载,下载内核映像和文件系统到,下载只能通过以太网进行。如命令完成文件下载。 内存控制,如命令可以烧写。 机中的引导加载程序由(其本质就是一段固件程序)和位于硬盘中的(比如,和等)一起组成。在完成硬件检测和资源分配后,将硬盘中的读到系统的中,然后将控制权交给。的主要运行任务就是将内核映象从硬盘上读到中,然后跳转到内核的入口点去运行,也即开始启动操作系统。 而在嵌入式系统中,通常并没有像那样的固件程序(注,有的嵌入式也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由来完成。在实验开发板(基于3C)的嵌入式系统中,系统在上电或复位时都从地址处开始执行,而在这个地址处安排的通常就是系统的程序。 简单地说,就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。 通常,是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的几乎是不可能的。尽管如此,我们仍然可以对归纳出一些通用的概念来,以指导用户特定的设计与实现。 内核是所有系统的中心软件组件。整个系统的能力完全受内核本身能力的限制。 由于内核支持多个架构,由于架构的差异性,每种架构都有不同的团队在维护,所以必须根据架构来选择供应内核的网站。见下表: 架构最合适的内核网站下载方式 等 内核源代码目录树结构说明如下: :包含和硬件体系结构相关的代码,每种平台占一个相应的目录。和位相关的代码存放在目录下,其中比较重要的包括(内核核心部分)、(内存管理)、(浮点单元仿真)、(硬件相关工具函数)、(引导程序)、(总线)和(相关状态)。 :常用加密和散列算法(如、等),还有一些压缩和校验算法。 :关于内核各部分的通用解释和注释。 :设备驱动程序,每个不同的驱动占用一个子目录。 :各种支持的文件系统,如、、等。 :头文件。其中,和系统相关的头文件被放置在子目录下。 :内核初始化代码(注意不是系统引导代码)。 :进程间通信的代码。 :内核的最核心部分,包括进程调度、定时器等,和平台相关的一部分代码放在*目录下。:库文件代码。 :内存管理代码,和平台相关的一部分代码放在*目录下。 :网络相关代码,实现了各种常见的网络协议。

Linux内核源码分析方法

Linux内核源码分析方法 一、内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次。如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径。我们都知道,想成为优秀的程序员,需要大量的实践和代码的编写。编程固然重要,但是往往只编程的人很容易把自己局限在自己的知识领域内。如果要扩展自己知识的广度,我们需要多接触其他人编写的代码,尤其是水平比我们更高的人编写的代码。通过这种途径,我们可以跳出自己知识圈的束缚,进入他人的知识圈,了解更多甚至我们一般短期内无法了解到的信息。Linux内核由无数开源社区的“大神们”精心维护,这些人都可以称得上一顶一的代码高手。透过阅读Linux 内核代码的方式,我们学习到的不光是内核相关的知识,在我看来更具价值的是学习和体会它们的编程技巧以及对计算机的理解。 我也是通过一个项目接触了Linux内核源码的分析,从源码的分析工作中,我受益颇多。除了获取相关的内核知识外,也改变了我对内核代码的过往认知: 1.内核源码的分析并非“高不可攀”。内核源码分析的难度不在于源码本身,而在于如何使用更合适的分析代码的方式和手段。内核的庞大致使我们不能按照分析一般的demo程序那样从主函数开始按部就班的分析,我们需要一种从中间介入的手段对内核源码“各个击破”。这种“按需索取”的方式使得我们可以把握源码的主线,而非过度纠结于具体的细节。 2.内核的设计是优美的。内核的地位的特殊性决定着内核的执行效率必须足够高才可以响应目前计算机应用的实时性要求,为此Linux内核使用C语言和汇编的混合编程。但是我们都 知道软件执行效率和软件的可维护性很多情况下是背道而驰的。如何在保证内核高效的前提下提高内核的可维护性,这需要依赖于内核中那些“优美”的设计。 3.神奇的编程技巧。在一般的应用软件设计领域,编码的地位可能不被过度的重视,因为开发者更注重软件的良好设计,而编码仅仅是实现手段问题——就像拿斧子劈柴一样,不用太多的思考。但是这在内核中并不成立,好的编码设计带来的不光是可维护性的提高,甚至是代码性能的提升。 每个人对内核的了理解都会有所不同,随着我们对内核理解的不断加深,对其设计和实现的思想会有更多的思考和体会。因此本文更期望于引导更多徘徊在Linux内核大门之外的人进入Linux的世界,去亲自体会内核的神奇与伟大。而我也并非内核源码方面的专家,这么做也只是希望分享我自己的分析源码的经验和心得,为那些需要的人提供参考和帮助,说的“冠冕堂皇”一点,也算是为计算机这个行业,尤其是在操作系统内核方面贡献自己的一份绵薄之力。闲话少叙(已经罗嗦了很多了,囧~),下面我就来分享一下自己的Linix内核源码分析方法。 二、内核源码难不难? 从本质上讲,分析Linux内核代码和看别人的代码没有什么两样,因为摆在你面前的一般都不是你自己写出来的代码。我们先举一个简单的例子,一个陌生人随便给你一个程序,并要你看完源码后讲解一下程序的功能的设计,我想很多自我感觉编程能力还可以的人肯定觉得这没什么,只要我耐心的把他的代码从头到尾看完,肯定能找到答案,并且事实确实是如此。那么现在换一个假设,如果这个人是Linus,给你的就是Linux内核的一个模块的代码,你还会觉得依然那么 轻松吗?不少人可能会有所犹豫。同样是陌生人(Linus要是认识你的话当然不算,呵呵~)给 你的代码,为什么给我们的感觉大相径庭呢?我觉得有以下原因:

Linux内核详细介绍

Linux内核详细介绍 现如今,电脑的使用越来越普遍,几乎每家每户都有电脑,而电脑的操作离不开操作系统,在这里,小编就向大家介绍Linux 内核。 很多Linux 爱好者对内核很感兴趣却无从下手,本文旨在介绍一种解读Linux内核源码的入门方法,而不是讲解Linux复杂的内核机制。 1.核心源程序的文件组织 (1)Linux核心源程序通常都安装在/usr/src/Linux下,而且它有一个非常简单的编号约定:任何偶数的核心(中间数字)如:2.0.30都是一个稳定的发行的核心,而任何奇数的核心如:2.1.42都是一个开发中的核心。 本文基于稳定的2.2.5源代码,第二部分的实现平台为Redhat Linux 6.0。 (2)核心源程序的文件按树形结构进行组织,在源程序树的

最上层你会看到这样一些目录: arch:arch子目录包括了所有和体系结构相关的核心代码。它的每一个子目录都代表一种支持的体系结构,例如i386就是关于Intel CPU及与之相兼容体系结构的子目录。PC机一般都基于此目录; include:include子目录包括编译核心所需要的大部分头文件。与平台无关的头文件在include/linux子目录下,与Intel CPU 相关的头文件在include/asm-i386子目录下,而include/scsi目录则是有关SCSI设备的头文件目录; init:这个目录包含核心的初始化代码(注:不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的一个非常好的起点; Mm:这个目录包括所有独立于CPU 体系结构的内存管理代码,如页式存储管理内存的分配和释放等,而和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/i386/mm/Fault.c; Kernel:主要的核心代码,此目录下的文件实现了大多数Linux系统的内核函数,其中最重要的文件当属sched.c,同样,和

Linux内核简要介绍(doc 9页)

Linux内核简要介绍(doc 9页)

更多企业学院: 《中小企业管理全能版》183套讲座+89700份资料《总经理、高层管理》49套讲座+16388份资料《中层管理学院》46套讲座+6020份资料《国学智慧、易经》46套讲座 《人力资源学院》56套讲座+27123份资料《各阶段员工培训学院》77套讲座+ 324份资料《员工管理企业学院》67套讲座+ 8720份资料《工厂生产管理学院》52套讲座+ 13920份资料《财务管理学院》53套讲座+ 17945份资料《销售经理学院》56套讲座+ 14350份资料《销售人员培训学院》72套讲座+ 4879份资料

器提供文件传输机制,而用户可以使用任何客户端程序;命令行的客户端和图形化界面的客户端都存在,并且谁都可以为传输文件写一个新的用户界面。 只要涉及到驱动程序,就会运用这样的功能划分。软盘驱动程序是设备无关的——这不仅表现在磁盘是一个连续读写的字节数组上。如何使用设备是应用程序要做的事:tar要连续地写数据,而mkfs则为要安装的设备做准备工作,mcopy 依赖于设备上存在的特殊数据结构。 在写驱动程序时,程序员应该特别留心这样的基本问题:我们要写内核代码访问硬件,但由于不同用户有不同需要,我们不能强迫用户采用什么样的特定策略。设备驱动程序应该仅仅处理硬件,将如何使用硬件的问题留给应用程序。如果在提供获得硬件能力的同时没有增加限制,我们就说驱动程序是灵活的。不过,有时必须要作一些策略决策。 可以从不同侧面来看你的驱动程序:它是位于应用层和实际设备之间的软件。驱动程序的程序员可以选择这个设备应该怎样实现:不同的驱动程序可以提供不同的能力,甚至相同的设备也可以

《深入理解LINUX内核》阅读笔记全二十章

《深入理解LINUX内核》(Understanding The Linux Kernel)第三版 第一章 - 绪论 第一章是绪论。前三节内容很少,讲的都是一些内核边缘相关的东西,不是真正内核的内容,简单了解就好,不必深究。后三节,“操作系统基本概念”简单描述了几个“使用操作系统”要知道的概念;“Unix文件系统概述”也是从用户的角度讲了几个概念,并没有深入到内核;“Unix内核概述”这一节内容很多,最重要的是它在讲述一些内核的重要概念时引出了很多必须处理的问题,让读者带着疑问到本书的后续章节里去自己探寻答案。 第二章 - 内存寻址 这一章的内容都是很底层的,直接是一些硬件特性或者是内核中处理硬件的一些策略。 内存地址根据其组织特点的不同分为三个层次:逻辑地址(虚拟地址)、线性地址、物理地址。再细一层,有分段和分页两种。对这两种机制,书中分别详细描述了其80X86的硬件特性和Linux内核对应的处理。 其中分页是重点。常规分页机制中,页框是4KB;而扩张分页机制的页框是4MB。Linux 采用了4级分页模型,能适应不同的体系结构。本章还讲述了硬件高速缓存和TLB (Translation Lookaside Buffer),TLB的翻译有很多种:转换后援缓冲器、转换检测缓冲区、旁路转换缓冲、页表缓冲,我觉得直接叫页表缓冲就很好理解了,没有必要纠结于单个单词的意思。 第三章 - 进程 这一章讲进程,但没有涉及任何的算法相关的东西,都是那些跟数据有关的系统调用、函数、数据结构,这跟前一章很像。 进程的静态特性:进程描述符,都是task_struct类型的结构,它的字段包含了与一个进程相关的所有信息。进程描述符是很基础很重要的东西,整个内核都构建在它的基础之上。 进程切换,这一章里“切换”是跟“调度”完全不同的概念。切换只是当进程调度时要做的数据的处理,特别是与进程描述符相关的操作以及硬件上下文、进程上下文相关的数据、字段。 创建进程,最重要的是do_fork()函数和copy_process()函数。 还有最后一节是撤销进程。重点是do_exit()函数和进程删除时的父子进程关系。 第四章 - 中断和异常 中断(interrupt)通常被定义为一个事件,该事件改变处理器执行的指令顺序。 中断分为同步和异步中断,或者称为异常和中断。 第二节中断和异常。中断包括可屏蔽中断和非屏蔽中断,而异常则包括处理器探测异常和编程异常。还讲述了一些关于IRQ线的知识,然后是异常处理程序发送的19种信号。当然,少不了中断描述符表。 第四节“初始化中断描述符表”,在Linux中分为以下几种描述符:中断门、系统门、系统中断门、陷阱门、任务门。 第六节中断处理。这是本章的重点,也是难点。 物理IRQ可以分配给32~238范围内的任何向量。 每个中断向量都有它自己的irq_desc_t描述符。

linux内核编译开题报告

南京邮电大学通达学院毕业设计(论文)开题报告 题目Linux内核网络协议栈分析与改进 学生姓名周旸班级学号07003228 专业软件工程 1. 对指导教师下达的课题任务的学习与理解 Linux是最受欢迎的自由电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。该计划开始于1991年,在计划的早期有一些Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。技术上说Linux是一个内核。“内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。一个内核不是一套完整的操作系统。 本课题要求在Linux操作系统上完成网络协议栈的内核分析、测试网络协议栈的不足,并且采用修改内核的方式来增强Linux内核网络协议栈的安全性。并对其进行功能和性能方面的测试。 以上是基于本课题的大致描述,具体来说,我认为本课题可以分为以下三个阶段: 1.准备阶段:即对Linux内核网络栈相关基础知识的学习准备,理解和掌握拒绝服务攻击DoS 的基本原理。 2.实施阶段:在这个阶段主要是对具体流程和项目的编写和操作,如结合Linux源代码分析网络协议栈TCP连接建立的过程;修改系统内核扩展代码,并且编译内核;使用LKM模块实现对内核的改进,达到加固Linux网络协议栈的要求。 3.测试阶段,当然也是非常关键的阶段。在此阶段需对修改后的Linux内核进行功能和性能方面的测试,达到课题要求。 总之,本课题的要点是分析Linux内核网络栈的优缺点,而后根据其不足之处通过修改内核的方式来增加其安全性。 2. 阅读文献资料进行调研的综述 2.1.网络协议栈简介: 网络协议栈是指网络中各层协议的总和,其形象的反映了一个网络中文传输的过程:由上层协议到底层协议,再由底层协议到上层协议。通常使用的协议为ISO/OSI协议和TCP/IP协议。 ISO/OSI协议为局域网协议栈,其本身并不是一个标准,在制定具体网络协议和标准时,要依据OSI/RM参考模型作为“参照基础”。ISO/OSI协议模型分为七层,由下到上分别是:应用层、数据链路层、网络层、传输层、会话层、表示层和应用层。 TCP/IP协议作为Internet的核心协议,被广泛应用于局域网和广域网中,目前已成为事实上国际标准。TCP/IP包含许多重要的基本特征,这些特征主要表现在5个方面:逻辑编址、路由选择、域名解析、错误检测和流量控制以及对应用程序的支持等。TCP/IP协议模型分为5层,由下到上分别是:物理层、链路层、网际层、传输层和应用层。 这个栈的最底部是物理层。物理层是指计算机的一系列硬件。其上市链路层,链路层是指提供对物理层访问的设备驱动程序,这可以是各种介质,例如串口链路或以太网设备。链路层上面是网络层,它负责将报文定向到目标位置。再上一层称为传输层,负责端到端的通信(例如,在一台主机内部)。尽管网络层负责管理主机之间的通信,但是传输层需要负责管理主机内部各端之间的通信。最后一层是应用层,它通常是一个语义层,能够理解要传输的数据。例如,超文本传输协议(HTTP)就负责传输服务器和客户机之间对Web 内容的请求与响应。 图一具体的表示了各层的内容。下面对其各层进行分析: (1)链路层安全协议:负责提供通过通信链路连接的主机或路由器之间的安全保证。 (2)网络层安全协议:主要解决网络层通信的安全问题,IPSec是目前最主要的网络层安全协议。 (3)传输层安全协议:主要实现传输层的安全通信,只可实现端到端(进程到进程)的加密。

操作系统Linux内核编译实验报告

操作系统课程设计实验报告 实验名称:linux的内核编译 姓名/学号: 一、实验目的 熟悉linux的使用,编译内核 二、实验内容 1.编译linux的新内核 2.将学号添加到新内核启动菜单中 三、实验环境 1.软件环境: Windows 7 旗舰版 VMware Workstation 8 UbuntuKylin 13.04(内核版本Ubundu with Linux 2.硬件环境 Intel core i5-2450M 四、程序设计与实现 1.下载并安装VMware Workstation以及Ubuntukylin13.04 开机先获取root权限 sudo passwd root Enter new UNIX password: (在这输入你的密码) Retype new UNIX password: (确定你输入的密码) passwd: password updated successfully 以后,如果在想获得root权限,只需进行如下的操作: su root Password: (在此输入你上面设置的密码) 接下来的实验都是在获得root权限下操作的。 打开系统查看系统信息 按住ctrl+alt+t打开终端,并输入uname -a以查看内核版本信息 可见此版本是ubuntu 2. 下载内核并编译 (1)我下载好的内核存放在Download文件夹里,为了方便直观,我又新建了一个名为kernel的文件夹(mkdir kernel),并将下载好的内核存放在kernel 文件夹里(cp - /root/Downloads/Linux- /home/wcsbfangou/kernel)。 (2)解压下载好的内核文件,输入xz -d linux-,再输入tar -xvf linux-,然后

linux内核设计与实现的读书笔记

linux内核设计与实现的读书笔记 进程的调度程序是保证进程能有效工作的一个内核子系统。调 度程序负责决定将哪个进程投入运行,何时运行以及运行多少时间。简单的来说,调度程序就是在给一堆就绪的进程分配处理器的时间,调度程序是多任务操作系统的基础。调度程序的原则就是最大限度的使用cpu的资源,也就是说,当系统中只要有可运行的进程,就不能让cpu处于空闲的状态,如果系统中没有就绪的进程时,则cpu会运行一个idle进程。 1.多任务 多任务操作系统就是能够同时并发的交互执行多个进程的操作 系统,需要注意这里是并发,而不是并行。如果你的计算机有两个或者两个以上的cpu那么,你的计算机就可以真正同时、并行的执行多个任务。多任务操作系统可以分为两类:抢占式多任务和非抢占式多任务。 抢占式多任务中,由调度程序来决定什么时候停止一个进程的 执行,这种由调度程序强行停止一个进程执行的动作称为抢占(preemption)。进程在被抢占之前运行的时间是固定的,而且有一个专门的名字,叫做时间片(timeslice)。时间片实际上是分配给每个 进程的处理器时间段。 而非抢占式多任务是由进程自己做出让步,在执行了一段时间 之后,主动地让出cpu。进程主动挂起自己的操作称为让步(yielding),

如果某个进程悬挂起来并且拒不作出让步的话,可能会导致操作系统崩溃。 所以总述上面的两种情况,抢占式多任务就像“法律”,只要时间到了,就把你撤下来。而非抢占式却像“道德”一样,你要是有道德,执行了一会之后,你就自己撤下来,如果有的“人”占着茅坑不拉屎,那其他进程除了用“道德”谴责它,也没有其他的办法了。 2.linux进程调度 linux最初的进程调度程序是非常原始的,很难适应一些众多的可运行进程和多处理器环境。后来从linux2.5开始,对linux的进程调度程序做了大的调整,使用了称为O(1)的调度算法,这个算法引起算法行为而得名。O(1)调度算法虽然在数以十计的多处理器上能表现出近乎完美的特性和可扩展性,但是由于这个算法在调度交互进程的时候并没有表现出很理想的效果。所以在linux2.6的开发初期,提出了CFS算法,即完全公平调度算法。 3.策略 (1)IO消耗型进程和处理器消耗型进程 IO消耗型进程指的是进程的大部分时间是用来等待IO的操作,例如图形用户界面(GUI)程序就属于IO消耗型程序,这个程序需要不断的监听用户的输入。这样的进程经常处于可运行的状态,但是每次运行的时间都很短。 处理器消耗型进程是指进程的大部分时间用在执行代码上,比如大型的计算程序MATLAB就属于处理器消耗型进程。

Linux内核组成和架构

Software World 2007.8.5 51 Linux内核从90年代初由Linus发布第一版开始,经过开源社区十几年的共同努力,现在已经发展到了最新的2.6内核。它已经从Linus作为学习Intel 386架构的一个“玩具”项目发展成一个成熟的可以商用的操作系统。由于Linux是一款开源的操作系统,这就给了我们一个非常难得的机会去学习一个成熟的、商用的操作系统是如何实现的。 进程管理 Linux内核支持所谓的多任务运行(multiprogramming),即可以有多个用户程序同时运行,用户可以一边听音乐一边写文档。为了在一个单CPU的系统中支持多任务,Linux内核使用了进程的概念(内核中用task_struct的结构表示),一个进程就是一个运行中的用户程序。 整个系统运行时,每个进程都会分得一定的时间片(从几十毫秒到几百毫秒),Linux内核的调度器负责选择不同的进程运行。当这个进程的时间片用完后,调度器会选择一个新的进程运行。由于切换的时间和频率非常快,给用户的感觉是有多个程序在同时运行。而实际上,在一个CPU上每一个时间点上其实只有一个进程在运行。 1.Linux的进程 一个用户程序开始运行时,内核会负责把存储在磁盘上的二进制可运行程序调入内存中,同时会分配一个task_struct代表这个要运行的用户程序。task_struct是 一个非常重要的数据结构,所有关于这个正在运行的用户程序信息都会保存在这个结构中,包括用户程序打开的文件,所使用的内存空间等。Linux内核所有对于进程的管理都是通过这个结构进行的。 2.进程调度器 进程调度器是整个系统非常关键的一个子系统,在系统中有多个进程可以运行时,它负责选择哪个进程运行。每个进程都是CPU计算和I/O请求的交替,不同的是,有的进程大部分的时间是浪费在CPU计算上,而有的则把几乎所有的时间用在了I/O等待上。一个好的调度器,既能够让用户交互程序有很快的反应时间(如编辑程序必须能够快速地将用户的敲键快速地反映到屏幕上),也能够让CPU密集型(CPU-bound)的进程有很好的吞吐量(throughput)。 Linux内核组成和架构 □ Intel开源技术中心 刘宏 Linux内核是Linux最核心的部分,Linux操作系统就是在Linux内核上发展壮大起来的,而内核的组成和架构则是任何试图涉及Linux Kernel的开源爱好者共同关心的。 Linux内核调度器是基于优先级的调度器,通过一套非常复杂的算法决定一个进程当前的优先级,同时进程的优先级又是动态调整的:一个很久没有得到运行的进程的优先级会被暂时增加,以增加得到运行的机会;一个花了较多时间睡眠的进程则会被认为是用户交互程序,优先级也会被增加,当这类进程需要运行的时候就会有较大的机会被调度器选用,这样整个系统对用户的输入就会有较快的响应。 早先2.4版本的内核把所有可运行的进程连接在一个链表上,每次调度时都要循环遍历链表上的每一个进程,计算其优先级以决定选择哪一个进程运行。这种方法的扩展性不是很好,调度器选择一个可运行的进程的时间将随着系统内进程数的增加而线性增加。在2.5开发版本中, 图1 2.6内核 O(1)调度器

相关主题
文本预览
相关文档 最新文档