原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
作者:严哲璟
说一下我对Linux系统的理解
1.加载Linux内核准备:在加载基本输入输出模块(BIOS)之后,从磁盘的引导扇区读入操作系统的代码文件块到内存中,之后开始整个系统的初始化.
2.main.c的start_kernel函数是整个操作系统的入口,这也与Linux是基于C语言的特性相符,start_kernel具体做的动作很多,包括死锁检测,指定多核CPU的ID,栈溢出保护,关闭中断,初始化内存页,以及一些重要模块,如mm_struct,trap_init,schedule_init,以及set(init_task)强制性手动创建一个0号进程即IDLE进程,当只有一个进程的时候,不断循环.
3.rest_init创建一个新的进程(用户进程),pid=1,进程的启动需要 sche_init模块
这些是操作系统最基本的核心,然后我们还有其他的功能,如进程管理,内存管理,文件管理.其中最重要当属进程管理.
进程管理最核心的是进程的切换和中断机制,中断有很多种,如软中断,即系统调用,陷阱,系统异常等等,还有硬中断,如IO中断
中断方式上面硬中断是通过相关的硬件中断处理器,如我们点击键盘触发相关的硬件中断,处理器进入相关的中断状态,在此略去.
而软中断主要是通过相应的系统指令(主要是int)指令去触发,软中断可以是程序本身的行为(系统调用),也可能是CPU为了保护计算机而做出的一种反应(异常).
系统调用主要分为3层:第一是相关的API,即应用程序接口,如read(),write(),getpid()等等,第二层是这些高级接口对应的中断向量表中的偏移,即syscall_table中的序号,对应一个中断服务程序.第三层是system_call保存当前上下文进入中断服务程序进行处理.处理完成之后iret,返回用户程序,在这之前,(应该说从内核态转为用户态的时候)检查进程调度标识,是否需要调度.
进程的创建和切换:通过系统调用fork(),创建一个与父进程完全一样的子进程,包括代码段完全一样,但是子进程的返回值为零,而且子进程的返回用户态的堆栈地址被改变了,其实这也很好理解,你一个子进程要做其他的事情,需要自己的用户空间和内核空间.不仅如此,当父进程执行了,子进程被调度执行的时候,子进程开始执行的地方是ret_from_fork,其下一句就是iret,而通过稍微修改在子进程的内核堆栈中保存的那些数据,iret就可以还原一个与父进程稍微不一样的子进程了.
进程的切换:关键函数是shcedule()和switch_to(),根据不同的进程优先级算法,如LRU,CLOCK,FIFO等,在就绪(3状态为就绪,阻塞,运行)的进程队列里挑选一个出来执行,这其中的关键部分是schedule(),当检测到一个进程需要调度的时候,从用户态切换到内核态执行内核代码(3G以上的区域),维护一个结构,此结构中保存当前被调度准备执行的进程在上一次被调度出去就绪或阻塞的时候执行的下一句代码,并且保存进程的用户堆栈,由于此代码是内核代码,对所有进程可见并通用,因此执行到此处都是一样的.通过switch_to,进程得以回复原来的用户堆栈的sp,以及eip,得以继续执行.之后schedule()返回,重新回到用户态,但是eip和esp都与上一个进程不一样了.
进程加载可执行文件:假设我们在运行shell进程,要执行cd /home这个命令,我们需要加载cd这个可执行文件,在linux上面,终端命令基本都被作为可执行文件,保存在/usr/bin中,在执行的时候,需要加载,并且传入一定参数.首先我们fork出一个新进程,但是在fork的新子进程中,我们需要调用exec类函数来加载一个可执行文件.加载这个可执行文件的具体过程是:调用启动加载器,加载器删除子进程现有的虚拟存储器段(后面会说)并创建一组新的代码,数据,堆,和栈,新的栈和堆被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页,新的代码和数据段被初始化为可执行文件的内容.最后加载器跳转到_start地址,最终从可执行目标文件中调用相应的main()函数,因此子进程就变成一个新的进程,其功能是可执行目标文件的功能,如上述所说,打开/home这个目录,将当前目录置为/home.C语言的编译和链接分为静态和动态链接,其中静态链接是将所有的头文件一起编译链接,动态链接分为共享库动态链接和运行时动态链接.共享库是/usr/lib中的一些动态链接库,其作用是只在加载的时候链接而不需要重新编译,这样链接非常方便,修改也可以独立修改.而运行时动态链接需要加载加载器,其本身也是一个共享库.
下面是个人理解的Linux32位的进程映像(即课件说的进程地址空间,是虚拟存储器)
内核虚拟存储器3G以上
用户栈(运行时创建),下面的这条线代表ESP,栈往下增长
共享库的存储器区域
运行时堆(malloc)往上增长
读写段,或者叫数据段,在可重定位目标文件中是data和bss字段
只读段或者是代码段(与上面的段都是从obj可执行文件中加载,下面的线是0x08048000)
总结部分:我对这门课的评价非常高,这门课让我对计算机的系统原理和系统实现有了非常好的认识,孟宁老师先从linux32位机器码说起,阐述了程序的机器级代码的实现,以及一些ASM的C汇编,让我们对机器代码有了一个比较好的认识,其次,老师讲述了进程,程序的映像以及在栈中程序的行为,之后老师再讲到进程的切换以及中断,最后课程讲授链接和加载的过程,让我们学会制作一些简单的共享库,理解了大型程序是怎么构造,以及理解其他重要的系统概念.
学完这门课之后,最大的收获是初步认识了操作系统的本质,以及当我们机器出现问题了,应该找到什么样的问题去尝试解决.
最大的遗憾是,由于时间实在太少,没能非常仔细的把全部内容熟悉,可能还有一些地方有疏漏
Linux系统理解以及Linux系统学习心得