嵌入式Linux概念学习笔记

来源:互联网 发布:mac下载的系统在哪 编辑:程序博客网 时间:2024/06/10 16:22

POSIX (Portable Operating System Interfaceof Unix)表示可移植操作系统接口. 由IEEE(Institute of Electricaland Electronic Engineering——电气和电子工程师协会)开发,由ANSI(ANSI——美国国家标准学会)和ISO(InternationalStandard Organized)标准化。

Decomposition of Linux System

1.User Application

The set of applications in use on a particular Linuxsystem will be different depending on what system is used for.

Typical examples include a telnet, ftp, and so on.

2.OS Services

These are services that are typical considered part ofthe operating system.

Typical examples include a command shell.

3.Linux Kernel

This is the mean area of Linux system

The kernel abstracts and mediated access to the hardwareresource and processor(CPU).

4.Hardware

This subsystem is comprised of all the possible physicaldevices in Linux installation

Processor, memory, network

 

Kernel

Kernel操作系统内核,是指大多数操作系统的核心部分。它由操作系统中用于管理存储器、文件、外设和系统资源的 那些部分组成。对于提供保护机制的现代操作来说,内核独立于普通应用程序,它一般处于系统态,拥有受保护的内存空间和访问硬件设备的所有权限,这种系统态和被保护起来的内存空间,统称为内核空间。相对的,应用程序在用户空间执行,它们只能看到允许它们使用的部分系统资源,并且不能使用某些特定的系统功能, 不能直接访问硬件,还有其他一些使用限制。当内核运行的时候,系统才进入内核空间。

应用程序通过系统调用和内核通信来运行。应用程序通常调用库函数----比如C库函数-----再由库函数通过系统调用界面让内核代其完成各种不同任务。在这种情况下,应用程序被称为通过系统调用在内核空间运行,而内核被称为运行于进程上下文中。

Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。

1.进程调度(SCHED):控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序选择最值得运行的进程。可运行进程实际上是仅等待CPU资源的进程,如果某个进程在等待其它资源,则该进程是不可运行进程。Linux使用了比较简单的基于优先级的进程调度算法选择新的进程。

2.内存管理(MM)允许多个进程安全的共享主内存区域。Linux 的内存管理支持虚拟内存,即在计算机中运行的程序,其代码,数据,堆栈的总量可以超过实际内存的大小,操作系统只是把当前使用的程序块保留在内存中,其余的程序块则保留在磁盘中。必要时,操作系统负责在磁盘和内存间交换程序块。内存管理从逻辑上分为硬件无关部分和硬件有关部分。硬件无关部分提供了进程的映射和逻辑内存的对换;硬件相关的部分为内存管理硬件提供了虚拟接口。

3.虚拟文件系统:隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口,VFS提供了多达数十种不同的文件系统。虚拟文件系统可以分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。

4.网络接口(NET):提供了对各种网络标准的存取和各种网络硬件的支持。网络接口可分为网络协议和网络驱动程序。网络协议部分负责实现每一种可能的网络传输协议。网络设备驱动程序负责与硬件设备通讯,每一种可能的硬件设备都有相应的设备驱动程序。

5.进程间通讯(IPC):支持进程间各种通信机制。

 

Process

内核把进程存放在叫做任务队列(task list)的双向循环链表中,链表中的每一项都是类型为task_struct,名称叫做进程描述符(process descriptor)的结构,该结构定义在include/linux/sched.h文件中,它包含了一个具体进程的所有信息。内核使用内存描述符结构表示进程的地址空间,由结构体mm_struct结构体表示,定义在linux/sched.h中

linux操作系统采用虚拟内存技术,所有进 程之间以虚拟方式共享内存。进程地址空间由每个进程中的线性地址区组成,而且更为重要的特点是内核允许进程使用该空间中的地址。通常情况下,每个进程都有唯一的地址空间,而且进程地址空间之间彼此互不相干。但是进程之间 也可以选择共享地址空间,这样的进程就叫做线程(这个可以通过CLONE_VM标志来实现)。

在linux中,并没有线程这个概念,linux中所有的线程都当作进程来处理,换句话说就是在内核中并没有什么特殊的结构和算法来表示线 程,线程仅仅是一个使用共享资源的进程。每个线程都拥有一个隶属于自己的task_struct(还有程序计数器、堆栈和寄存器上下文等,靠这些东西 cpu也才能在调度进程/线程).所以说线程本质上还是进程,只不过该进程可以和其他一些进程共享某些资源信息。


内核调度的对象是线程,而不是进程。linux系统中的线程很特别,它对线程和进程并不做特别区分。进程的另外一个名字叫任务(task).用户空间运行的程序叫做进程,把内核中运行的程序叫做任务。

Linux 进程

Each process represented by taskstructure

State:As a process executes it changesstate according toits circumstances

Scheduling Information:Thescheduler fairly decide which processin the system most deserves to run

Identifier:Each process has user and groupidentifiers to controlthis processes access to the files and devices in thesystem:

Inter-process Communication:Mechanismsof signals, pipes and semaphores

Links:Each process keeps pointers to itsparent process andits own child processes

Time and Timer:Keeps track ofa process creation time aswell as the task life time

File System:Pointers to descriptors for each openfile as well aspointers to VFS inode

Processor Specific Context:Processor‟s registers, stackand so on.

分为三种类型:

内核线程:

它 的创建和撤消是由内核的内部需求来决定的,用来负责执行一个指定的函数,一个内核线程不需要和一个用户进程联系起来。它共享内核的正文段核全局数据,具有自己的内核堆栈。它能够单独的被调度并且使用标准的内核同步机制,可以被单独的分配到一个处理器上运行。内核线程的调度由于不需要经过态的转换并进行地址 空间的重新映射,因此在内核线程间做上下文切换比在进程间做上下文切换快得多。

对于内核而言,内核线程没有进程地址空间,也没有相关的内存描述符,内核线程对应的进程描述符中mm域也为空。但内核线程还是需要使用一些数据的,比如页表,为了避免内核线程为内存描述符和页表浪费内存,也为了当新内核线程运行时,避免浪费处理器周期向新地址空间进行切换,内核线程将直接使用前一个进程的内存描述符。

pid_tkernel_thread(int (*fn)(void *), void *arg, unsignedlong flags)

轻量级进程:
轻 量级进程是内核支持的用户线程,它在一个单独的进程中提供多线程控制。这些轻量级进程被单独的调度,可以在多个处理器上运行,每一个轻量级进程都被绑定在一个内核线程上,而且在它的生命周期这种绑定都是有效的。轻量级进程被独立调度并且共享地址空间和进程中的其它资源,但是每个LWP都应该有自己的程序计数器、寄存器集合、核心栈和用户栈。
intclone(int (*fn)(void * arg), void *stack, int flags, void * arg);

用户线程:
用 户线程是通过线程库实现的。它们可以在没有内核参与下创建、释放和管理。线程库提供了同步和调度的方法。这样进程可以使用大量的线程而不消耗内核资源,而且省去大量的系统开销。用户线程的实现是可能的,因为用户线程的上下文可以在没有内核干预的情况下保存和恢复。每个用户线程都可以有自己的用户堆栈,一块 用来保存用户级寄存器上下文以及如信号屏蔽等状态信息的内存区。库通过保存当前线程的堆栈和寄存器内容载入新调度线程的那些内容来实现用户线程之间的调度和上下文切换。内核仍然负责进程的切换,因为只有内核具有修改内存管理寄存器的权力。用户线程不是真正的调度实体,内核对它们一无所知,而只是调度用户线 程下的进程或者轻量级进程,这些进程再通过线程库函数来调度它们的线程。当一个进程被抢占时,它的所有用户线程都被抢占,当一个用户线程被阻塞时,它会阻塞下面的轻量级进程,如果进程只有一个轻量级进程,则它的所有用户线程都会被阻塞。
intpthread_create(pthread_t *restrict thread, const pthread_attr_t *restrictattr, void *(*start_routine)(void*), void *restrict arg);

fork:

生成的子进程复 制了父进程的资源(包括内存和task_struct(2个进程的pid不同))。这样得到的子进程独立于父进程,具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,共享内存等机制,但由于现在Linux中是采取了copy-on-write(COW写时复制)技术,为了降低开 销,fork最初并不会真的产生两个不同的拷贝,在写入时才发生真正的数据复制。

vfork

vfork系统调用不同于fork,用vfork创建的子进程与父进程共享地址空间,用 vfork创建子进程后,父进程会被阻塞直到子进程调用exec或exit。

clone

系 统调用fork()和vfork()是无参数的,而clone()则带有参数。fork()是全部复制,vfork()是共享内存,而clone()是则 可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的 clone_flags来决定。另外,clone()返回的是子进程的pid。

fork()是全部复制,vfork()是共享内存,而clone()是则可以将父进程资源有选择地复制给子进程,

无论是fork(),还是vfork(),__clone()最后都根据各自需要的参数标志去调用clone().然后有clone()去调用do_fork()(kernel/fork.c). do_fork()调用copy_process()函数,进程开始运行.内核有意选择子进程先运行。因为一般子进程都会马上调用exec()函数,这样可以避免写时拷贝的额外开销。如果父进程首先执行的话,有可能会开始向地址空间写入。

CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask
CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
CLONE_NEWNS 在新的namespace启动子进程,namespace描述了进程的文件hierarchy
CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
CLONE_PTRACE 若父进程被trace,子进程也被trace
CLONE_VFORK 父进程被挂起,直至子进程释放虚拟内存资源
CLONE_VM 子进程与父进程运行于相同的内存空间
CLONE_PID 子进程在创建时PID与父进程一致
CLONE_THREAD Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群


内存管理

linux的内存管理主要分为两部分:物理地址到虚拟地址的映射,内核内存分配管理(主要基于slab)

Slab 是Linux操作系统的一种内存分配机制。与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab缓存分配器通过对类似大小的对象进行缓 存而提供这种功能,从而避免了常见的碎片问题。slab分配器还支持通用对象的初始化,从而避免了为同一目的而对一个对象重复进行初始化。最后,slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能

物理地址(physical address)
用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。

虚拟内存(virtual memory)
这是对整个内存的抽像描述

逻辑地址(logical address)
Intel为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。

线性地址(linear address)或也叫虚拟地址(virtual address)
跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址

内核把物理页作为内存管理的基本单位。尽管处理器的最小可寻址单位通常是字,但是,内存管理单元MMU通常以页为单位进行处理。因此,从虚拟内存的角度来看,页就是最小单位。内核用struct page(linux/mm.h)结构表示系统中的每个物理页:

 在linux中,内核也不是对所有的页都一视同仁,内核而是把页分为不同的区,使用区来对具有相似特性的页进行分组。Linux必须处理如下两种硬件存在缺陷而引起的内存寻址问题:

一些硬件只能用某些特定的内存地址来执行DMA

一些体系结构其内存的物理寻址范围比虚拟寻址范围大的多。

这样,就有一些内存不能永久地映射在内核空间上。为了解决这些制约条件,Linux使用了三种区:

1.ZONE_DMA:这个区包含的页用来执行DMA操作。

2.ZONE_NOMAL:这个区包含的都是能正常映射的页。

3.ZONE_HIGHEM:这个区包"高端内存",其中的页能不永久地映射到内核地址空间。

区 的实际使用与体系结构是相关的。linux 把系统的页划分区,形成不同的内存池,这样就可以根据用途进行分配了。需要说明的是,区的划分没有任何物理意义,只不过是内核为了管理页而采取的一种逻辑上的分组。尽管某些分配可能需要从特定的区中获得页,但这并不是说,某种用途的内存一定要从对应的区来获取,如果这种可供分配的资源不够用了,内核就会占 用其他可用去的内存。

 

kmalloc()可以保证在物理地址上都是连续的(虚拟地址也是连续的);用于申请较小的、连续的物理内存

1. 以字节为单位进行分配,在<linux/slab.h>中

2. void *kmalloc(size_tsize, int flags) 分配的内存物理地址上连续,虚拟地址上自然连续

3. gfp_mask标志:什么时候使用哪种标志?如下:

———————————————————————————————-
情形                                                                   相应标志
———————————————————————————————-
进程上下文,可以睡眠                                    GFP_KERNEL
进程上下文,不可以睡眠                                GFP_ATOMIC
中断处理程序                                                    GFP_ATOMIC
软中断                                                                GFP_ATOMIC
Tasklet                                                               GFP_ATOMIC
用于DMA的内存,可以睡眠                            GFP_DMA | GFP_KERNEL
用于DMA的内存,不可以睡眠                        GFP_DMA | GFP_ATOMIC
———————————————————————————————-

4. void kfree(const void *ptr) 释放由kmalloc()分配出来的内存块

vmalloc()分配的内存虚拟地址是连续的,而物理地址则无需连续,大小无限制;用于申请较大的内存空间,虚拟内存是连续的

1. 以字节为单位进行分配,在<linux/vmalloc.h>中

2. void*vmalloc(unsigned long size) 分配的内存虚拟地址上连续,物理地址不连续

3. 一般情况下,只有硬件设备才需要物理地址连续的内存,因为硬件设备往往存在于MMU之外,根本不了解虚拟地址;但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用vmalloc(),例如当模块被动态加载到内核当中时,就把模块装载到由vmalloc()分配 的内存上。

4.void vfree(void *addr),这个函数可以睡眠,因此不能从中断上下文调用。

 

malloc(), vmalloc()和kmalloc()区别

[*]kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存

[*]kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续,malloc不保证任何东西(这点是猜测的,不一定正确)

[*]kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相对较大

[*]内存只有在要被DMA访问的时候才需要物理上连续

[*]vmalloc比kmalloc要慢

http://blog.sina.com.cn/s/blog_858820890100ufvy.html


Exception and Interrupt

中断是指CPU对系统发生的某个事件做出的一种反应,CPU暂停正在执行的程序,保留现场后自动地转去执行相应的处理程序,处理完该事件后再返回断点继续执行被“打断”的程序。

Exception: 来自CPU的内部事件或程序执行中的事件引起的过程, CPU本身故障, 程序故障等
Interrupt: 由CPU外部引起的,称作中断,如I/O中断、时钟中断、控制台中断等

中断产生后内核会执行一个叫做中断处理程序或中断处理例程的函数。,中断处理程序是和特定中断相关联的,而不是和设备相关联,一个设备可以产生很多中断,这时该设备的驱动程序也就需要准备多个这样的函数,一个中断处理程序是设备驱动程序的一部分

中断从硬件到内核的路由

在内核中,中断的旅程开始于预定义入口点,这类似于系统调用。对于每条中断线,处理器都会跳到对应的一个唯一的位置。这样,内核就可以知道所接收中断的IRQ号了。初始入口点只是在栈中保存这个号,并存放当前寄 存器的值(这些值属于被中断的任务);然后,内核调用函数do_IRQ().

Linux 内核给我们提供了一组接口能够让我们控制机器上的中断状态,这些接口可以在<asm/system.h>和<asm/irq.h>中找到。一般来说,控制中断系统的原因在于需要提供同步,通过禁止中断,可以确保某个中断处理程序不会抢占当前的代码。此外,禁止中断还可以禁止内核抢占。然而,不管是禁止中断还是禁止内核抢占,都没有提供任何保护机制来防止来自其他处理器的并发访问。

Linux支持多处理器,因此,内核代码一般都需要获取某种锁,防止来自其他处理器对共享数据的并发访问,获取这些锁的同时也伴随着禁止本地中断。锁提供保护机制,防止来自其他处理器的并发访问,而禁止中断提供保护机制,则是防止来自其他中断处理程序的并发访问。

用于控制中断的方法列表

注册一个中断处理程序:
int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *,structpt_regs*),unsigned long irqflags,const char * devname,void *dev_id)

中断处理程序:
static irqreturn_t intr_handler(int irq, void *dev_id, struct pt_regs *regs)

 

中断处理是分为两个部分:

部:中断处理程序接收到一个中断,就立即执行,但只做有严格时限的工作

只能通过中断处理程序来完成。中断处理程序会异步执行,并且在最好的情况下它也会锁定当前的中断线,因此将中断处理程序缩短的越小就越好。

对时间敏感,和硬件相关,不想被其他中断打断的任务最好放在中断处理程序中完成。


部:主要是允许能稍后完成的工作,执行与中断处理密切相关但中断处理程序本生身不执行的任务。通常情况下,下半部会在中断处理程序返回时立即执行。

有多种实现方式,最早是“bottom half,后来是taskqueues(softirqs)以及tasklets

linux2.6内核提供了三种不同形式的下半部实现机制:软中断,tasklets和工作对列。


当执行一个中断处理程序或下半部时,内核处于中断上下文(interrupt context)中。对比进程上下文,进程上下文是一种内核所处的操作模式,此时内核代表进程执行,可以通过current宏关联当前进程。此外,因为进程是进程上下文的形式连接到内核中,因此,在进程上下文可以随时休眠,也可以调度程序。但中断上下文却完全不是这样,它可以不可以休眠,因为我们不能从中断上下文中调用函数。如果一个函数睡眠,就不能在中断处理程序中使用它,这也是对什么样的函数能在中断处理程序中使用的限制。还需要说明一点的是,中断处理程序没有自己的栈,相反,它共享被中断进程的内核栈,如果没有正在运行的进程,它就使用idle进程的栈。因为中断程序共享别人的堆栈,所以它们在栈中获取空间时必须非常节省。内核栈在32位体系结构上是8KB,在64位体系结构上是16KB.执行的进程上下文和产生的所有中断都共享内核栈。

///////////////////////////////////////

http://blog.csdn.net/lizhibin1091666592/article/details/6976264

1,中断处理程序中不能使用有睡眠功能的函数,如ioremap,kmalloc,msleep等,理由是中断程序并不是进程,没有进程的概念,因此就没有休眠的概念;

2,中断处理程序中的延时可以用忙等待函数来代替,如ndelay,udelay,mdelay等,这些函数在实现上本质是根据CPU频率进行一定次数的 循环;最好不要使用mdelay,因为毫秒延时对内核来说已经是非常大了。但是在中断处理程序中使用msleep却不行。(见linux设备驱动开发详解 第二版p210页)

3,printk函数在中断处理函数中可以使用,但是会占用较多时间,降低效率。

4,使用for和while等的空循环在中断处理函数中进行延时操作,在实际测试中发现并不能起到延时的功能,linux内核处理这种循环速度很快,并没有延时的效果。

 

1、中断是一种电信号,由硬件设备生成,并直接送入中断控制器的输入引脚上。然后再由中断控制器向处理器发送相应的信号。处理器一经检测到此信号,便中断 自己的当前工作转而处理中断。此后,处理器会通知操作系统已经产生中断,这样,操作系统就可以对这个中断进行适当的处理了。

2、不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识。中断值通常被称为中断请求(IRQ)线。有些中断值是指定的,有些是动态分配的。特定的中断总与特定的设备相关联。

3、异常与中断不同,它在产生时必须考虑与处理器时钟同步。异常也常常称为同步中断。许多处理器体系结构处理异常与中断的方式类似,因此内核对它们的处理也很类似。

4、在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序或中断服务例程。产生中断的每个设备都有一个相应的中断处理程序,如果一个设备可以产生多种不同的中断,那么该设备就可以对应多个中断处理程序。一个设备的中断处理程序是它设备驱动程序的一部分。

5、中断处理程序与其他内核函数的真正区别在于:中断处理程序是被内核调用来响应中断的,而它们运行于我们称之为中断上下文的特殊上下文中。

6、中断处理一般分为两个部分,中断处理程序是上半部-接收到一个中断就立即执行,但只做有严格时限的工作,这些工作都是在所有中断被禁止的情况下完成的。能够被允许稍后完成的工作被推迟到下半部去。通常情况下,下半部会在中断处理程序返回时立即执行。

7、Linux中的中断处理程序是无需重入的。当一个给定的中断处理程序正在执行时,相应的中断线在所有处理器上都会被屏蔽掉,以防止在同一中断线上接收 另一个新的中断。通常情况下,所有其他的中断都是打开的,所以这些不同中断线上的其它中断都能被处理,但当前中断线总是被禁止的。由此可以看出,同一个中断处理程序绝对不会被同时调用以处理嵌套的中断。

8、共享的中断处理程序与非共享的在注册和运行方式上比较类似,但差异主要有以下三处:

注册中断处理程序函数request_irq()的参数flags必须设置SA_SHIRQ标志。

对每个注册的中断处理程序来说,dev_id参数必须唯一。不能给共享的处理程序传递NULL值。

中断处理程序必须能够区分它的设备是否真的产生了中断。否则它根本无法知道是它对应的设备发出了这个中断还是共享这条中断线的其它设备发出了这个中断。

9、当执行一个中断处理程序或下半部时,内核处于中断上下文中。中断上下文和进程并没有什么瓜葛。因为没有进程的背景,所以中断上下文不可以睡眠。因此,不能从中断上下文中调用某些函数。如果一个函数睡眠,就不能在中断处理函数中使用它。中断上下文具有较为严格的时间限制,因为它打断了其他代码。中断上下文中 的代码应当迅速简洁,尽量不要使用循环去处理繁重的工作。尽量把工作从中断处理程序中分离出来,放在下半部执行。中断处理程序并不具有自己的栈。相反,它 共享被中断进程的内核栈。如果没有正在运行的进程,就使用idle进程的栈。中断处理程序共享别人的堆栈,所以它在栈中获取空间时必须非常节省。内核栈本 就很有限,所有的内核代码都应该谨慎利用它。

10、Linux内核提供了一组接口用于操作机器上的中断状态。这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽掉整个机器的一条中断线的能 力,这些例程都是与体系结构相关的,可以在<asm/system.h>和<asm/irq.h>中找到。

11、控制中断系统的原因归根结底是需要提供同步。通过禁止中断,可以确保某个中断处理程序不会抢占当前代码,还可以禁止内核抢占。但它们都没有提供任何保护机制来防止来自其他处理器的并发访问。锁提供保护机制来防止来自其他处理器的并发访问。禁止中断提供保护机制来防止来自其他中断处理程序的并发访问。


System Call

系统调用是用户空间访问内核的唯一手段,它们是内核唯一的合法入口

一般情况下,应用程序通过应用编程接口(API)而 不是直接通过系统调用来编程,而且这种编程接口实际上并不需要和内核提供的系统调用对应。一个API定义了一组应用程序使用的编程接口。它们可以实现成一 个系统调用,也可以通过调用多个系统调用来实现,即使不使用任何系统调用也不存在问题。实际上,API可以在各种不同的操作系统上实现,给应用程序提供完 全相同的接口,而它们本身在这些系统上的实现却可能迥异。

系统调用号:在linux中, 每个系统调用都赋予一个系统调用号,通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候,这个系统调用号就被用来指明到底要执行哪个系统调用;进程不会提及系统调用的名称。系统调用号一旦分配就不能再有任何变更(否则编译好的应用程序就会崩溃),如果一个系统调用被删除,它 所占用的系统调用号也不允许被回收利用。Linux有一个"未使用"系统调用sys_ni_syscall(),它除了返回-ENOSYS外不做任何其他 工作,这个错误号就是专门针对无效的系统调用而设的。虽然很罕见,但如果有一个系统调用被删除,这个函数就要负责“填补空位”。

用 户空间的程序无法直接执行内核代码。它们不能直接调用内核空间的函数,因为内核驻留在受保护的地址空间上,应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,系统切换到内核态,这样内核就可以代表应用程序来执行该系统调用了。这种通知内核的机制是通过软中断实现的。x86系统上的软中断由int$0x80指令产生.这条指令会触发一个异常导致系统切换到内核态并执行第128号异常处理程序,而该程序正是系统调用处理程序,名字叫system_call().它与硬件体系结构紧密相关,通常在entry.s文件中通过汇编语言编写。

  所有的系统调用陷入内核的方式都是一样的,所以仅仅是陷入内核空间是不够的。因此必须把系统调用号一并传给内核。在x86上,这个传递动作是通过在触发软中断前把调用号装入eax寄存器实现的。这样系统调用处理程序一旦运行,就可以从eax中得到数据。

系 统调用必须仔细检查它们所有的参数是否合法有效。系统调用在内核空间执行。如果任由用户将不合法的输入传递给内核,那么系统的安全和稳定将面临极大的考验。最重要的一种检查就是检查用户提供的指针是否有效,内核在接收一个用户空间的指针之前,内核必须要保证:指针指向的内存区域属于用户空间;指针指向的内存区域在进程的地址空间里;如果是读,读内存应该标记为可读。如果是写,该内存应该标记为可写。


Virtual File System

简单地说,就是在各种格式的文件系统基础上用虚拟文件的格式进行一次封装,把具体文件系统的差异用虚拟文件系统隔离开来,从而使用户面对的是一个统一的虚拟文件系统界面。

 由上可知,虚拟文件系统既没有文件,也不直接管理文件,它只是用户与实际文件系统之间的接口。因此,它并不需要保存在永久存储介质中,而只是在需要时由内核在内存中创建起来的一个文件系统,所以叫做虚拟文件系统。

VFS在内核中与其他的内核模块的协同关系


这个VFS抽象层之所以能衔接各种各样的文件系统,是因为它定义了所有文件系统都支持的基本抽象接口和数据结构,同时实际系统也将自身的诸如“如何打开文件”,“目录是什么”等概念在形式上与VFS的定义保持一致。

Unix使用了四种和文件系统相关的传统抽象概念,如下:

1.文件:就是一个有序字节串。(linux/fs.h)
2.
目录项:文件是放在目录中,目录又可以层层嵌套,形成文件路径。路径中的每一项就叫做目录项。目录是文件,这个文件列出了该目录下的所有文件.( linux/dcache.h中)
3. 索引节点:一个文件其实是由两部分组成:相关信息和文件本身。这里的相关信息指的是访问控制权限,大小,拥有者,创建时间等。文件相关信息也叫做元数据,被存储在一个单独的数据结构中,这个结构就叫做索引点(index node,简写inode)。(定义在linux/fs.h中。)
4.安装点(挂载点):文件系统被安装在一个特定的安装点上,该安装点在全局层次结构中被称为命名空间,所有的已安装文件系统都作为根文件树的树叶出现在系统中。
5.超级块:是一种包含文件系统信息的数据结构,里边是文件系统的控制信息。(定义在linux/fs.h中。)


 

参考博客:

http://www.cnblogs.com/hanyan225/
http://blog.csdn.net/sandflee/article/details/5189312

 

 


原创粉丝点击