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函数有配合作用。
计算机的启动过程概述
- 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语言编程操作的水平。