Linux内核分析期中总结

Linux内核分析期中总结

跟踪分析Linux内核的启动过程

过程分析:

在Linux内核的启动过程中,一共经历了start_kernel,rest_init,kernel_thread等几个函数的执行。其中start_kernel相当于普通C程序的main函数。Trap_init负责初始化中断向量,

mm_init负责内存管理模块初始化,sche_init负责调度模块初始化。

两把宝剑:

1.中断上下文切换

2.进程上下文切换

三大法宝:

1.存储程序计算机

2.函数调用堆栈

3.中断

0号进程

有一个全局变量init_task,是手工创建的PCB,也是最后的idle进程,与rest_init函数有配合作用。

计算机的启动过程概述

  1. x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置。

2.BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。这里一般是把硬盘的第一个扇区MBR和活动

分区的引导程序加载到内存(即加载BootLoader),加载完整后把控制权交给BootLoader。

3.引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录,比如root (hd0,0),kernel (hd0,0)/bzImage

root=/dev/ram init=/bin/ash,initrd (hd0,0)/myinitrd4M.img

4.内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init

5.一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动。

initrd文件的功能主要有两个:

1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules)

2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去

编译内核

1.原因:生成符号表

2.方法:

(1)make config操作简单,耗时

(2)make menuconfig图形化界面

(3)make allnoconfig 全no选择处理

系统调用的工作机制

内核态

执行级别高,可以执行特权指令,访问任意物理地址,在intel X86 CPU的权限分级为0级。

用户态

执行级别低,只能访问0x00000000-0xbfffffff之间的逻辑地址,权限分级为3级。

区分与切换

CS:eip(代码段选择寄存器/偏移量寄存器)中,CS寄存器最低两位表示特权级。状态通过中断来切换,包括硬件中断和系统调用两种方式。

寄存器上下文

从用户态切换到内核态时,int指令会保存用户态的寄存器上下文到内核堆栈中,同时会把当前内核态的一些信息加载,例如cs:eip指向中断处理程序入口。

  • 用户态栈顶地址

  • 当时状态字

  • 当时cs:eip

系统调用三层皮(以API xyz为例)

  • API xyz

  • 中断向量system_call

  • 中断服务程序sys_xyz

系统调用号通过eax寄存器传递,将API xyz和中断服务程序sys_xyz关联起来。

分析Linux内核创建一个新进程的过程

进程的两种虚拟机制:虚拟处理器,虚拟内存

任务队列:链表每一项都是进程描述符结构。

进程描述符描述内容:打开的文件,进程地址空间,挂起信号,进程状态

进程状态:

进程描述符中state域描述了进程的当前状态。

TASK_RUNNING(可执行)

TASK_INTERRUPTIBLE(正被阻塞)

TASK_UNINTERRUPTIBLE(不可中断)

_TASK_TRACED(被其他进程跟踪)

_TASK_STOPPED(进程停止执行)

进程家族

所有进程都是PID为1的init进程的后代,内核在系统启动的最后阶段启动init进程

进程创建

1 fork()通过拷贝当前进程创建一个子进程
2 exec()函数读取可执行文件并将其载入地址空间

线程实现

在linux中,线程仅仅被视作一个与其他进程共享某些资源的进程,它只是一种进程间共享资源的手段

内核线程:

独立运行在内核空间中的标准进程

它与普通进程的区别在于,内科线程没有独立的地址空间,只在内核空间运行。可以被调度或者抢占。

由源代码生成可执行文件

预处理 => 编译 => 汇编 => 链接

gcc -E hello.c -o hello.i
gcc –S hello.i –o hello.s
gcc –c hello.s –o hello.o
gcc hello.o –o hello

若无-o指明,生成可执行文件默认为a.out

目标文件格式

目标文件分为PE(windows)和ELF(linux)。

查看ELF文件头部方法:readelf

静态链接ELF可执行文件默认入口点:0x8048000

动态链接执行方法:

$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32
$ export LD_LIBRARY_PATH=$PWD 
/*将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件copy到默认路径下。*/

-L + 路径 或者 -l + 库名

静态链接和动态链接的区别

静态连接:elf_entry指向可执行文件头部,是新程序执行的起点

动态链接:elf_entry指向ld(动态链接器)的起点,加载load_elf_interp

进程调度的时机

直接调用:中断处理过程(包括时钟中断,I/O中断,系统调用和异常)

返回用户态时调用:根据need_resched标记调用

内核线程可以主动调用也可以被动调用

用户态线程仅能在陷入内核态后,即在中断处理过程中调度

进程上下文

用户地址空间:代码,数据,堆栈等

控制信息:进程描述符,内核堆栈

硬件上下文

pick_next_task(rq,prev);进程调度算法封装

context_switch(rq,prec,next);进程上下文切换

switch_to中prev指向当前进程,next指向被调度进程

总结:

通过半个学期对Linux内核的学习,我对上个学期接触过的操作系统的运行机制有了进一步的了解,同时也对C语言进行底层的系统操作有了初步认识,这可以加深我对于软件工程一些思想的

理解,同时也可以提高我的系统级C语言编程操作的水平。

原文地址:https://www.cnblogs.com/20135319zl/p/5414574.html