主页 > 知识库 > 服务器 > Linux/BSD >

破解Linux操作系统的工作奥秘

来源:中国IT实验室 作者:佚名 发表于:2013-07-04 14:16  点击:
总结 Linux操作系统能够正常工作是建立在:存储程 序计算机、函数调用堆栈机制和中断机制这三个基础之上的。而对操作系统的讨论可以归结到对进程运行情况的讨论,Linux操作系统中进程主要分为:内核线 程和普通进程。其中内核线程只工作在内核态,主要负责操
总结
  Linux操作系统能够正常工作是建立在:存储程 序计算机、函数调用堆栈机制和中断机制这三个基础之上的。而对操作系统的讨论可以归结到对进程运行情况的讨论,Linux操作系统中进程主要分为:内核线 程和普通进程。其中内核线程只工作在内核态,主要负责操作系统的初始化和一些周期性管理任务,常见的有0号进程(idle进程)和1号进程(init进程 );而普通进程既可以运行在内核态,也可以运行在用户态。
  程序运行的过程中其实就是顺序从存储器 里读取指令,然后利用函数调用堆栈机制不断入栈出栈(详见这里)。当一个进程在执行的过程中,可能会遇到各种事件,使其被挂起,内核调度CPU资源给进程 队列中其他的进程,使其执行。Linux操作系统在宏观上来看,基本上就是这样:进程执行——>中断——>进程切换——>进程执行,这 样循环往复进行工作。
  从微观上来看,相对复杂:进程队列中的某个进程A正在执行,由于某种事件(系统调用、时间片用完等)产生中断,从用户态转向内核态,在内核态 中,首先进行SAVE_ALL,然后执行中断处理程序,在中断处理过程中或其结束后,由于某种原因(比如时间片用完等),内核可能会进行进程调度,将当前 进程切换为进程队列中的另一个进程B,然后RESTORE_ALL、iret,再由内核态转向用户态。若发生了进程切换,则必然会用到switch_to 宏,将涉及到内核堆栈状态的变化,详见附录。
  附录
  存储程序计算机
  存储程序和程序控制原理的要点是,程序输入到计算机中,存储在内存储器中(存储原理),在运行时,控制器按地址顺序取出存放在内存储器中的指令 (按地址顺序访问指令),然后分析指令,执行指令的功能,遇到转移指令时,则转移到转移地址,再按地址顺序访问指令(程序控制)。
  函数调用堆栈机制
  详见实验一:计算机是怎样工作的
  中断机制
  操作系统之所以具备如此强大的功能,很重要的一个原因是其具备中断处理这个机制。中断提供了一种特殊的方式,使处理器转而去运行正常控制流之外 的代码。当一个中断信号到达时,CPU必须停止它当前正在做的事情,并且切换到一个新的活动。为了做到这一点,就要在内核态堆栈保存程序计数器的当前值 (即eip和cs寄存器的内容),并把与中断类型相关的一个地址放进程序计数器中。
  进程切换
  为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换。进程切换可能只发生在精心定义的点:schedule()函数;从本质上说,每个进程切换由两步组成:
  1.切换页全局目录以安装一个新的地址空间;
  2.切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息,包含CPU和寄存器。
  每个进程可以拥有属于自己的地址空间,但所有进程必须共享CPU寄存器。因此,恢复一个进程的执行之前,内核必须确保每个寄存器装入了挂起进程时的值。
  进程切换的第二步由switch_to宏执行,该宏有三个参数,它们是prev,next,和last,其中,prev指向被替换进程的描述符,而next指向被激活进程的描述符,last则用来记录该进程是由哪个进程切换过来的。
  贴出switch_to宏的内核代码(linux-3.2.1/arch/x86/include/asm/system.h),以便更好的分析:
/*
 * Saving eflags is important. It switches not only IOPL between tasks,
 * it also protects other tasks from NT leaking through sysenter etc.
 */
#define switch_to(prev, next, last)     \
do {         \
 /*        \
  * Context-switching clobbers all registers, so we clobber \
  * them explicitly, via unused output variables.  \
  * (EAX and EBP is not listed because EBP is saved/restored \
  * explicitly for wchan access and EAX is the return value of \
  * __switch_to())      \
  */        \
 unsigned long ebx, ecx, edx, esi, edi;    \
         \
 asm volatile("pushfl\n\t"  /* save    flags */ \
       "pushl %%ebp\n\t"  /* save    EBP   */ \
       "movl %%esp,%[prev_sp]\n\t" /* save    ESP   */ \
       "movl %[next_sp],%%esp\n\t" /* restore ESP   */ \
       "movl $1f,%[prev_ip]\n\t" /* save    EIP   */ \
       "pushl %[next_ip]\n\t" /* restore EIP   */ \
       __switch_canary     \
       "jmp __switch_to\n" /* regparm call  */ \
       "1:\t"      \
       "popl %%ebp\n\t"  /* restore EBP   */ \
       "popfl\n"   /* restore flags */ \
         \
       /* output parameters */    \
       : [prev_sp] "=m" (prev->thread.sp),  \
         [prev_ip] "=m" (prev->thread.ip),  \
         "=a" (last),     \
         \
         /* clobbered output registers: */  \
         "=b" (ebx), "=c" (ecx), "=d" (edx),  \
         "=S" (esi), "=D" (edi)    \
                \
         __switch_canary_oparam    \
         \
         /* input parameters: */    \
       : [next_sp]  "m" (next->thread.sp),  \
         [next_ip]  "m" (next->thread.ip),  \
                \
         /* regparm parameters for __switch_to(): */ \
         [prev]     "a" (prev),    \
         [next]     "d" (next)    \
         \
         __switch_canary_iparam    \
         \
       : /* reloaded segment registers */   \
   "memory");     \
} while (0)

有帮助
(0)
0%
没帮助
(0)
0%