周子轩 原创作品转载注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
笔记:
- 冯诺依曼体系结构的核心思想是存储程序计算机。在计算机中有两种指令,一是用户指令,一是系统调用。
- Linux是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。
- 当内核运行的时候,系统以内核态进入内核空间执行。而执行一个普通用户程序时,系统将以用户态进入以用户空间执行。
- 在系统中运行的应用程序通过系统调用来与内核通信。
- 三个法宝:
存储程序计算机、函数调用堆栈、中断机制
两把宝剑:中断上下文、进程上下文的切换
- 当硬件设备想和系统通信的时候,它首先要发出-个异步的中断信号去打断处理器的执行,继而打断内核的执行。中断通常对应着一个中断号,内核通过这个中断号查找相应的中断服务程序,并调用这个程序响应和处理中断。
- Linux是一个可移植的操作系统。即大部分C代码与体系结构无关,在许多不同体系结构的计算机上都能够编译和执行。
博客目录:
Linux内核分析1
冯诺依曼体系结构的核心思想是存储程序计算机。在计算机中有两种指令,一是用户指令,一是系统调用。
当用户使用计算机时,计算机根据其汇编的指令一步步运行,当使用系统调用完后,再返回用户模式,保证系统的稳定。
- 程序中执行call的堆栈变化
- 汇编基础
通用寄存器
16位 32位
AX eax 累加器
BX ebx 基址寄存器
CX ecx 计数寄存器
DX edx 数据寄存器
BP ebp 堆栈基址指针
SI esi 变址寄存器
DI edi 变址寄存器
SP esp 堆栈顶指针
Linux内核分析2
函数调用约定
函数调用约定 参数传递顺序 负责清理参数占用的堆栈 __pascal 从左到右 调用者 __stdcall 从右到左 被调函数 __cdecl 从右到左 调用者 调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。
Windows中C/C++程序的缺省函数调用约定是__cdecl linux中gcc默认用的规则是__stdcall
三个法宝
存储程序计算机、函数调用堆栈、中断机制
两把宝剑
中断上下文、进程上下文的切换
Linux内核分析3
- 计算机的启动过程概述
- x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置。
- BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。这里一般是把硬盘的第一个扇区MBR和活动分区的引导程序加载到内存(即加载BootLoader),加载完整后把控制权交给BootLoader。
- 引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录,比如root (hd0,0),kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash,initrd (hd0,0)/myinitrd4M.img
- 内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init。
- 一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动。
- initrd文件的功能主要有两个:
- 1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules)
- 2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去
- 计算机的启动过程概述
Linux内核分析4
- system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号
- 一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即__NR_fork)
- 在系统调用号( eax)之外,参数的个数不能超过6个( ebx,ecx, edx, esi, edi, ebp)
- 当用户态进程调用一个系统调用时, CPU切换到内核态并开始执行一个内核函数。
- 在Linux中是通过执行int $0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常
- system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号
Linux内核分析5
- 给MenuOS增加time和time-asm命令(四步操作命令)
系统调用的中断处理过程
- 进程的作用:将信号、进程间通信、内存管理和文件系统联系起来
- 操作系统的三大功能: 进程管理、内存管理、文件系统
- 内核通过唯一的进程标识PID来区别每个进程
- fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建
1号进程是所有用户态进程的祖先,0号进程是所有内核线程的祖先
Linux内核分析7
execve系统调用
Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
- execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
库函数exec*都是execve的封装例程
execve和fork都是特殊一点的系统调用:一般的都是陷入到内核态再返回到用户态。 fork父进程和一般进程调度一样,子进程返回到一个特定的点ret_from_fork,子进程是从ret_from_fork开始执行然后返回到用户态; execve特殊:执行到可执行程序--陷入内核--构造新的可执行文件--覆盖掉原可执行程序--返回到新的可执行程序,作为起点(也就是main函数) ,需要构造他的执行环境;
Linux内核分析8
- 进程切换在内核中实现的时机有以下三个时机:
中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
- 进程切换在内核中实现的时机有以下三个时机:
改变属性的值的方法
获取所有节点的一些方法的属性(有待改善)
JS中修改属性
获取节点的几种小案例
在页面上点击按钮,出现弹出框
SelectionSort,选择排序
BubbleSort冒泡排序
混合app
使用(Unicode字符)让inline水平元素换行