课程学习总结报告

请您根据本课程所学内容总结梳理出一个精简的Linux系统概念模型,最大程度统摄整顿本课程及相关的知识信息,模型应该是逻辑上可以运转的、自洽的,并举例某一两个具体例子(比如读写文件、分配内存、使用I/O驱动某个硬件等)纳入模型中验证模型。,

谈谈您对课程的心得体会,改进建议等。

 

1.一个精简的Linux系统概念模型

Linux内核实现了操作系统的三大核心功能,即进程管理、内存管理和文件系统。其中,操作系统内核中最核心的功能是进程管理,下面首先来看一下进程管理。

1.1 进程管理

在Linux内核中用一个数据结构struct task_struct来描述进程,这个数据结构非常庞大,其中state是进程状态, stack是堆栈等,因为涉及的内容过于庞杂,我们可以通过如图所示的进程描述符的结构示意图从总体上看清structtask_struct的结构关系,⽐如进程的状态、进程双向链表的管理,以及控制台tty、⽂件系统fs的描述、
进程打开⽂件的⽂件描述符files、内存管理的描述mm,还有进程间通信的信号signal的描述等。

 

1.1.1 进程的创建

在Linux中,0号进程的初始化是通过硬编码方式固定下来的,除此之外,所有其他进程的初始化都是通过do_fork复制父进程的方式初始化的。那进程是如何创建的呢?用户态创建一个子进程时调用fork()函数,发生fork系统调用,由用户态转到内核态,通过追踪fork系统调用,发现是通过_do_fork函数创建了一个新进程,具体创建过程大概是调用copy_process()复制父进程,把当前进程的描述符等相关进程资源复制一份,并根据子进程的需要对复制的进程描述符做一些修改。并获得自己的pid,然后通过wake_up_new_task()函数将子进程添加到就绪队列。至此,进程完成了创建。创建过程如下图所示。

1.1.2 进程的调度

首先,知道进程在Linux内核中的状态与操作系统原理中不同,在Linux内核中,就绪态和运行态都用TASK_RUNNING,新进程创建完之后,其状态是TASK_RUNNING,此时它处于就绪态;当调度器选择这个新创建的进程运行时,它的状态还是TASK_RUNNING。

来看一下进程调度。

Linux内核通过schedule函数实现进程调度,schedule函数负责在运行队列中选择一个进程,然后把它切换到CPU上执行。调用schedule函数的时机主要分为两类:

(1)中断处理过程中的进程调度时机,中断处理过程中会在适当的时机检测need_resched标记,决定是否调用schedule()函数。比如在系统调用内核处理函数执行完成后且系统调用返回之前就会检测need_resched标记决定是否调用schedule()函数。

(2)内核线程主动调用schedule(),如内核线程等待外设或主动睡眠等情形下,或者在适当的时机检测need_resched标记,决定是否主动调用schedule函数。

了解了进程调度的时机,下面来看一下进程的切换。

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复执行以前挂起的某个进程。这种行为被称为进程切换、任务切换或进程上下文切换。在进程切换时,需要保存当前进程的所有信息,如用户地址空间、控制信息、进程的CPU上下文,和相关寄存器的值。那进程是怎么切换的呢?

schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换。context_switch()首先调用switch_mm切换地址空间,然后调用switch_to()进行CPU上下文切换。整个过程如下图所示。

 1.2 系统调用

上文中多次提到系统调用,系统调用是一种特殊的中断,一般来说,从用户态进入内核态是由中断触发的,可能是硬件中断,在用户态进程执行时,硬件中断信号到来,进入内核态,就会执行这个中断对应的中断服务例程。也可是用户态程序执行过程中,调用了一个系统调用,陷入了内核态,因此说,系统调用是一种特殊的中断。下面来看一下系统调用是怎么进行的。

当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行system_call汇编代码,其中根据系统调用号调用对应的内核处理函数。以pread函数为例,查看系统调用过程。

pread函数触发系统调用__x64_sys_pread64,其功能由ksys_pread64()实现;汇编指令触发系统调用,找到函数中断入口,执行entry_SYSCALL_64(),通过swapgs指令保存现场,触发do_syscall_64。通过do_syscall_64函数得到了系统调用号,执行系统调用内容。然后程序跳到read_write.c文件,触发了ksys_pread64()函数,执行完毕后,又跳回了do_syscall_64中的syscall_return_slowpath(),准备进行现场恢复操作,并准备跳回用户态。最后回到entry_SYSCALL_64()执行popq,完成堆栈切换。

设置断点之后,继续执行,监听。

程序跳到read_write.c文件,触发了ksys_pread64()函数

触发  entry_SYSCALL_64() 函数

继续执行,使用swapgs指令恢复现场。

 单步执行直到完成。

 

1.3 文件管理

文件是,具有符号名的、在逻辑上具有完整意义的一组相关信息项的有序序列。

文件系统,就是操作系统中实现文件统一管理的一组软件、被管理的文件以及为实施文件管理所需要的一些数据结构的总称。

要实现操作系统对其他各种不同文件系统的支持,就要将对各种不同文件系统的操作和管理纳入到一个统一的框架中。对用户程序隐去各种不同文件系统的实现细节,为用户程序提供一个统一的、抽象的、虚拟的文件系统界面,这就是所谓的虚拟文件系统VFS。通常,虚拟文件系统分为三个层次,如下图所示。

第一层为文件系统接口层,如open/write/close等系统调用接口

第二层为VFS接口层。该层有两个接口:一个是与用户的接口;一个是与特定文件系统的接口。VFS与用户的接口将所有对文件的操作定向到相应的特定文件系统函数上。VFS与特定文件系统的接口主要是通过VFS-operations实现。

第三层是具体文件系统层,提供具体文件系统的结构和实现,包括网络文件系统,如NFS

VFS的作用是向上提供接口,向下通过file结构达到兼容。

读写文件之前,必须先打开文件。在文件被打开的过程中,要根据给定的文件路径名搜索目录结构。一旦被找到,文件的FCB被复制到内存里的系统打开文件表里。接着,在进程打开文件表里创建一个指针字段,指向系统打开文件表里相应的表项。在UNIX系统,打开文件系统调用返回的是文件描述符。Linux文件系统逻辑结构如下图所示。

 

 

 VFS把每个目录看做一个文件;每一个文件除了有一个inode数据结构外,还有一个dentry数据结构与之关联,该结构中的d_inode指针指向相应的inode结构;dentry数据结构可以加快对文件的快速定位,改进文件系统的效率。

 2.心得体会

通过本课程的学习,对Linux有了更深一步的了解,从前并不了解Linux系统里究竟是如何工作运转的,孟老师讲的内核部分,让我了解了Linux内核,了解了系统调用、进程切换等等;李老师让我了解了文件系统的知识,之前对于文件系统局限在表面,而通过李老师的课程,知道了VFS是如何工作的,是如何起作用的。在这门课程中学习到了很多。

posted @ 2020-07-08 12:45  ljj18  阅读(125)  评论(0编辑  收藏  举报