深入理解Linux内核(第一章 绪论一)

操作系统基本概念

操作系统必须要完成的两个目标:

  • 与硬件部分交互,为包含在硬件平台上的所有低层可编程部件提供服务。
  • 为运行在计算机系统上的应用程序(即所谓用户程序)提供执行环境。
    硬件为CPU引入了至少两种不同的执行模式:用户程序的非特权模式和内核的特权模式。Unix把他们分别称为用户态(User Mode)和内核态(Kernel Mode)。

多用户系统

多用户系统(multiuser system)就是一台能并发和独立地执行分别属于两个或多个用户的若干应用程序的计算机。“并发”(concurrently)意味着几个应用程序能同时处于活动状态并竞争各种资源,如CPU、内存、硬盘等等。“独立”(independently)意味着每个应用程序能执行自己的任务,而无需考虑其他用户的应用程序在干些什么。
在这里插入图片描述

用户和组

在多用户系统中,每个用户在机器上都有私用空间。操作系统必须保证用户空间的私有部分仅仅对其拥有者是可见的。
所有的用户由一个唯一的数字来标识,这个数字叫用户标识符(User ID,UID)。
为了和其他用户有选择地共享资料,每个用户是一个或多个用户组的一名成员,组由唯一的用户组标识符(user group ID)标识。每个文件也恰好与一个组相对应。例如,可以设置这样的访问权限,拥有文件的用户具有对文件的读写权限,同组用户仅有只读权限,而系统中的其他用户没有对文件的任何访问权限。

进程

一个进程可以定义为:“程序执行时的一个实例”,或者一个运行程序的“执行上下文”。
现代操作系统允许具有多个执行流的进程,也就是说,在相同的地址空间可执行多个指令序列。
允许进程并发活动的系统称为多道程序系统(multiprogramming)或多处理系统(multiprocessing)。
区分程序和进程是非常重要的:几个进程能并发执行同一程序,而同一个进程能顺利地执行几个程序。
操作系统中叫做调度程序(scheduler)的部分决定哪个进程能执行。多用户系统中的进程必须是抢占式的(preemptable),操作系统记录下每个进程占有的CPU时间,并周期性地激活调度程序。
只要进程发出系统调用(即对内核提出要求),硬件就会把特权模式由用户态变成内核态,然后进程以非常有限的目的开始一个内核过程的执行。这样,操作系统在进程的执行上下文中起作用,以满足进程的请求。一旦这个请求完全得到满足,内核过程将迫使硬件返回到用户态,然后进程从系统调用的下一条指令继续执行。

内核体系结构

大部分Unix内核是单块结构:每一个内核层都被集成到整个内核程序中,并代表当前进程在内核态下运行。相反,微内核(microkernel)操作系统只需要内核有一个很小的函数集,通常包括几个同步原语、一个简单的调度程序和进程间通信机制。
微内核比单块内核效率低,因为操作系统不同层次之间显式的消息传递要花费一定的代价。
微内核迫使程序员采用模块化的方法,因为任何操作系统层都是一个相对独立的程序,这种程序必须通过定义明确而清晰的软件接口与其他层交互。
已有的微内核操作系统可以很容易地移植到其他的体系结构上,因为所有与硬件相关的部分都被封装进微内核代码中。
微内核操作系统比单块内核更加充分地利用了RAM,因为暂且不需要执行的系统进程可以被调出或撤销。

Unix文件系统概述

硬链接和软链接

包含在目录中的文件名就是一个文件的硬链接(hard link),或简称软链接(Link)。在同一目录或不同的目录中,同一文件可以有几个链接,因此对应几个文件名。
硬链接的限制:

  • 不允许用户给目录创建硬链接。
  • 只有在同一文件系统中的文件之间才能创建链接。
    为了克服这些限制,引入了软链接(soft link),也称符号链接(symbolic link)。符号链接是短文件,这些文件包含有另一个文件的任意一个路径名。路径名可以指向位于任意一个文件系统的任意文件或目录,甚至可以指向一个不存在的文件。

文件类型

文件描述符与索引节点

文件系统处理文件需要的所有信息包含在一个名为索引节点(inode)的数据结构中。每个文件都有自己的索引节点,文件系统用索引节点来标识文件。

访问权限和文件模式

文件访问权限的组合就用九种不同的二进制来标记。还有三种附加的标记:
suid:进程执行一个文件时通常保持进程拥有者的UID。然而,如果设置了可执行文件suid的标志位,进程就获得了该文件拥有者的UID。
sgid:进程执行一个文件时保持进程组的用户组ID。然而,如果设置了可执行文件sgid的标志位,进程就获得了该文件用户组的ID。
sticky:设置了sticky标志位的可执行文件相当于向内核发出一个请求,当程序执行结束后,依然将它保留在内存。
当文件由一个进程创建时,文件拥有者的ID就是该进程的UID。而其用户组ID可以是进程创建者的ID,也可以是父目录的ID,这取决于父目录sgid标志位的值。

文件操作的系统调用

当用户访问一个普通文件或目录文件的内容时,他实际上是访问存储在硬件块设备上的一些数据。从这个意义上说,文件系统是硬盘分区物理组织的用户级试图。因为处于用户态的进程不能直接与低层硬件交互,所以每个实际的文件操作必须在内核态下进行。
打开文件
fd=open(path, flag, mode)
flag 指定文件的方式。它也指定是否应当创建一个不存在的文件。
mode 指定新创建文件的访问权限
一个打开文件对象包括:

  • 文件操作的一些数据结构,如指定文件打开方式的一组标志;表示文件当前位置的offset字段,从这个位置开始将进行下一个操作(即所谓的文件指针),等等。
  • 进程可以调用的一些内核函数指针。这组允许调用的函数集合由参数flag的值决定。
    为了创建新的文件,进程也可以调用creat()系统调用。

访问打开的文件
对普通Unix文件,可以顺序地访问,也可以随机地访问,而对设备文件和命名管道文件,通常只能顺序地访问。
内核把文件指针存放在打开文件对象中,也就是说,当前位置就是下一次进行读或写操作的位置。
顺序访问是文件的默认访问方式,即read() 和write() 系统调用总是从文件指针的当前位置开始读或写。为了修改文件指针的值,必须在程序中显式地调用lseek() 系统调用。当打开文件时,内核让文件指针指向文件的第一个字节(偏移量为0)。
newoffset=lseek(fd, offset, whence);
fd:表示打开文件的文件描述符。
offset:指定一个有符号整数值,用来计算文件指针的新位置。
whence:指定文件指针新位置的计算方式:可以是offset 加0,表示文件指针从文件头移动;也可以是offset加文件指针的当前位置,表示文件指针从当前位置移动;还可以是offset加文件最后一个字节的位置,表示文件指针从文件末尾开始移动。
nread= read(fd, buf, count);
关闭文件
res=close(fd);
更名及删除文件
res= rename(oldpath, newpath); // 改变了文件链接的名字
res= unlink(pathname); // 减少了文件链接数,删除了相应的目录项。只有当链接数为0时,文件才被真正删除。

Unix内核概述

内核必须实现一组服务及相应的接口。应用程序使用这些接口,而且通常不会与硬件资源直接交互。

进程/内核模式

进程是动态的实体,在系统内通常只有有限的生存期。创建、撤销及同步现有进程的任务都委托给内核中的一组例程来完成。】
内核本身并不是一个进程,而是进程的管理者。
进程/内核模式假定:请求内核服务的进程使用所谓系统调用(system call)的特殊编程机制。每个系统调用都设置了一组识别进程请求的参数,然后执行与硬件相关的CPU指令完成从用户态到内核态的转换。
除用户进程之外,Unix系统还包括几个所谓内核线程(kernel thread)的特权进程(被赋予特殊权限的进程),它们具有以下特点:

  • 它们以内核态运行在内核地址空间
  • 他们不与用户态直接交互,因此不需要终端设备。
  • 它们通常在系统启动时创建,然后一直处于活跃状态直到系统关闭。

可以有几种方式激活内核例程:

  • 进程调用系统调用
  • 正在执行进程的CPU发出一个异常(exception)信号,异常是一些反常情况,例如一个无效的指令。内核代表产生异常的进程处理异常。
  • 外围设备向CPU发出一个中断(interrupt)信号以通知一个事件的发生,如一个要求注意的请求、一个状态的变化或一个I/O操作已经完成等。每个中断信号都是由内核中的中断处理程序(interrupt handler)来处理的。因为外围设备与CPU异步操作,因此,中断在不可预知的时间发生。
  • 内核线程被执行。因为内核线程运行在内核态,因此必须认为其相应程序是内核的一部分。

进程实现

为了让内核管理进程,每个进程由一个进程描述符(process descriptor)表示,这个描述符包含有关进程当前状态的信息。
当内核暂停一个进程的执行时,就把几个相关处理器寄存器的内容保存在进程描述符中。这些寄存器包括:

  • 程序计数器(PC)和栈指针(SP)寄存器
  • 通用寄存器
  • 浮点寄存器
  • 包含CPU状态信息的处理器控制寄存器(处理器状态字,Processor Status Word)
  • 用来跟踪进程对RAM访问的内存管理寄存器

当内核决定恢复执行一个进程时,它用进程描述符中合适的字段来装载CPU寄存器。因为程序计数器中所存的值指向下一条将要执行的指令,所以进程从它停止的地方恢复执行。
当一个进程不在CPU上执行时,它正在等待某一事件。Unix内核可以区分很多等待状态,这些等待状态通常由进程描述符队列实现。每个(可能为空)队列对应一组等待特定事件的进程。

Preface The Audience for This Book Organization of the Material Level of Description Overview of the Book Background Information Conventions in This Book How to Contact Us Safari? Enabled Acknowledgments Chapter 1. Introduction Section 1.1. Linux Versus Other Unix-Like Kernels Section 1.2. Hardware Dependency Section 1.3. Linux Versions Section 1.4. Basic Operating System Concepts Section 1.5. An Overview of the Unix Filesystem Section 1.6. An Overview of Unix Kernels Chapter 2. Memory Addressing Section 2.1. Memory Addresses Section 2.2. Segmentation in Hardware Section 2.3. Segmentation in Linux Section 2.4. Paging in Hardware Section 2.5. Paging in Linux Chapter 3. Processes Section 3.1. Processes, Lightweight Processes, and Threads Section 3.2. Process Descriptor Section 3.3. Process Switch Section 3.4. Creating Processes Section 3.5. Destroying Processes Chapter 4. Interrupts and Exceptions Section 4.1. The Role of Interrupt Signals Section 4.2. Interrupts and Exceptions Section 4.3. Nested Execution of Exception and Interrupt Handlers Section 4.4. Initializing the Interrupt Descriptor Table Section 4.5. Exception Handling Section 4.6. Interrupt Handling Section 4.7. Softirqs and Tasklets Section 4.8. Work Queues Section 4.9. Returning from Interrupts and Exceptions Chapter 5. Kernel Synchronization Section 5.1. How the Kernel Services Requests Section 5.2. Synchronization Primitives Section 5.3. Synchronizing Accesses to Kernel Data Structures Section 5.4. Examples of Race Condition Prevention Chapter 6. Timing Measurements Section 6.1. Clock and Timer Circuits Section 6.2. The Linux Timekeeping Architecture Section 6.3. Updating the Time and Date Section 6.4. Updating System Statistics Section 6.5. Software Timers and Delay Functions Section 6.6. System Calls Related to Timing Measurements Chapter 7. Process Scheduling Section 7.1. Scheduling Policy Section 7.2. The Scheduling Algorithm Section 7.3. Data Structures Used by the Scheduler Section 7.4. Functions Used by the Scheduler Section 7.5. Runqueue Balancing in Multiprocessor Systems Section 7.6. System Calls Related to Scheduling Chapter 8. Memory Management Section 8.1. Page Frame Management Section 8.2. Memory Area Management Section 8.3. Noncontiguous Memory Area Management Chapter 9. Process Address Space Section 9.1. The Processs Address Space Section 9.2. The Memory Descriptor Section 9.3. Memory Regions Section 9.4. Page Fault Exception Handler Section 9.5. Creating and Deleting a Process Address Space Section 9.6. Managing the Heap Chapter 10. System Calls Section 10.1. POSIX APIs and System Calls Section 10.2. System Call Handler and Service Routines Section 10.3. Entering and Exiting a System Call Section 10.4. Parameter Passing Section 10.5. Kernel Wrapper Routines Chapter 11. Signals Section 11.1. The Role of Signals Section 11.2. Generating a Signal Section 11.3. Delivering a Signal Section 11.4. System Calls Related to Signal Handling Chapter 12. The Virtual Filesystem Section 12.1. The Role of the Virtual Filesystem (VFS) Section 12.2. VFS Data Structures Section 12.3. Filesystem Types Section 12.4. Filesystem Handling Section 12.5. Pathname Lookup Section 12.6. Implementations of VFS System Calls Section 12.7. File Locking Chapter 13. I/O Architecture and Device Drivers Section 13.1. I/O Architecture Section 13.2. The Device Driver Model Section 13.3. Device Files Section 13.4. Device Drivers Section 13.5. Character Device Drivers Chapter 14. Block Device Drivers Section 14.1. Block Devices Handling Section 14.2. The Generic Block Layer Section 14.3. The I/O Scheduler Section 14.4. Block Device Drivers Section 14.5. Opening a Block Device File Chapter 15. The Page Cache Section 15.1. The Page Cache Section 15.2. Storing Blocks in the Page Cache Section 15.3. Writing Dirty Pages to Disk Section 15.4. The sync( ), fsync( ), and fdatasync( ) System Calls Chapter 16. Accessing Files Section 16.1. Reading and Writing a File Section 16.2. Memory Mapping Section 16.3. Direct I/O Transfers Section 16.4. Asynchronous I/O Chapter 17. Page Frame Reclaiming Section 17.1. The Page Frame Reclaiming Algorithm Section 17.2. Reverse Mapping Section 17.3. Implementing the PFRA Section 17.4. Swapping Chapter 18. The Ext2 and Ext3 Filesystems Section 18.1. General Characteristics of Ext2 Section 18.2. Ext2 Disk Data Structures Section 18.3. Ext2 Memory Data Structures Section 18.4. Creating the Ext2 Filesystem Section 18.5. Ext2 Methods Section 18.6. Managing Ext2 Disk Space Section 18.7. The Ext3 Filesystem Chapter 19. Process Communication Section 19.1. Pipes Section 19.2. FIFOs Section 19.3. System V IPC Section 19.4. POSIX Message Queues Chapter 20. Program ExZecution Section 20.1. Executable Files Section 20.2. Executable Formats Section 20.3. Execution Domains Section 20.4. The exec Functions Appendix A. System Startup Section A.1. Prehistoric Age: the BIOS Section A.2. Ancient Age: the Boot Loader Section A.3. Middle Ages: the setup( ) Function Section A.4. Renaissance: the startup_32( ) Functions Section A.5. Modern Age: the start_kernel( ) Function Appendix B. Modules Section B.1. To Be (a Module) or Not to Be? Section B.2. Module Implementation Section B.3. Linking and Unlinking Modules Section B.4. Linking Modules on Demand Bibliography Books on Unix Kernels Books on the Linux Kernel Books on PC Architecture and Technical Manuals on Intel Microprocessors Other Online Documentation Sources Research Papers Related to Linux Development About the Authors Colophon Index
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值