基于Linux进程模型分析

Posted jmu1079

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Linux进程模型分析相关的知识,希望对你有一定的参考价值。

  • 操作系统是怎么组织进程的  

  1. 什么是进程

      进程是处于执行期的程序以及它所包含的所有资源的总称,包括虚拟处理器,虚拟空间,寄存器,堆栈,全局数据段等。

      在Linux中,每个进程在创建时都会被分配一个数据结构,称为进程控制块。进程控制块中包含了很多重要的信息,供系统调度和进程本身执行使用。而进程的用户空间主要存储代码和数据。

  2. 进程的创建

    进程是通过调用::fork(),::vfork()和::clone()系统调用创建新进程,而且都是通过调用do_fork来实现进程的创建。

      三者的区别如下

    • ::fork():父进程的所有数据结构都会复制一份给子进程。
    • ::vfork():只复制task_struct和内核堆栈,所以生成的只是父进程的一个线程。
    • ::clone():::clone()可以让你有选择性的继承父进程的资源,既可以选择像::vfork()一样和父进程共享一个虚拟空间,从而使创造的是线程,你也可以不和父进程共享,你甚  至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。
  3. 进程的退出

   进程通过调用exit()退出执行,这个函数会终结进程并释放所有的资源。父进程可以通过wait4()查询子进程是否终结。进程退出执行后处于僵死状态,直到它的父进程调用wait()或者waitpid()为止。父进程退出时,内核会指定线程组的其他进程或者init进程作为其子进程的新父进程。 

  • 进程状态如何转换(给出进程状态转换图)

  1. 三种基本状态 :

    运行态:进程占用CPU,并在CPU上运行;

      就绪态:进程已经具备运行条件,但是CPU还没有分配过来;

        阻塞态:进程因等待某件事发生而暂时不能运行;

   状态转换图:

 

进程之间的转换过程:

        运行---》就绪:这是有调度引起的,主要是进程占用CPU的时间过长
        就绪---》运行:运行的进程的时间片用完,调度就转到就绪队列中选择合适的进程分配CPU
        运行---》阻塞:发生了I/O请求或等待某件事的发生
        阻塞---》就绪:进程所等待的事件发生,就进入就绪队列
         阻塞--》运行:即使给阻塞进程分配CPU,也无法执行,操作系统載进行调度时不会載阻塞队列进行挑选,其调度的选择对象为就绪队列:
        就绪--》阻塞:因为就绪态根本就没有执行

  • 进程是如何调度的

  1. 进程的优先级

    进程提供了两种优先级,一种是普通的进程优先级,第二个是实时优先级,前者使用SCHEED_NORMAL调度策略,后者可选SCHED_FIFO或SCHED_rr调度。任何时候,实时进程的优先级都高于普通进程,实时进程只会被更高级的实时进程抢占,同时实时进程之间是按照FIFO(一次机会做完)或者RR(多次轮转)规则调度的。

  2.  主调度器schedule

    schedule就是主调度器的工作函数, 在内核中的许多地方, 如果要将CPU分配给与当前活动进程不同的另一个进程, 都会直接调用主调度器函数schedule或者其子函数__schedule.

asmlinkage void __sched schedule(void)
{
    struct task_struct *tsk = current;
    sched_submit_work(tsk);
    __schedule();
}

  3.进程上下文的切换

  context_switch其实是一个分配器, 他会调用所需的特定体系结构的方法

  • 调用switch_mm(), 把虚拟内存从一个进程映射切换到新进程中

    switch_mm更换通过task_struct->mm描述的内存管理上下文, 该工作的细节取决于处理器, 主要包括加载页表, 刷出地址转换后备缓冲器(部分或者全部), 向内存管理单元(MMU)提供新的信息

  • 调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息

static void __sched __schedule(void)  
{  
    struct task_struct *prev, *next;  
    unsigned long *switch_count;  
    struct rq *rq;  
    int cpu;  
    cpu = smp_processor_id();  
    rq = cpu_rq(cpu);  
    rcu_note_context_switch(cpu);  
    prev = rq->curr;            //当前队列的当前进程  
    schedule_debug(prev);  
  ...
    if (unlikely(!rq->nr_running))//当前rq无进程运行时,从其他cpu上取一个task执行  
        idle_balance(cpu, rq);  
    //选择下个可调度进程  
    next = pick_next_task(rq);  
  ...
    //上下文切换  
    context_switch(rq, prev, next); 
  ...
}  

  4.Linux的现代调度算法:CFS(完全公平调度) 

   CFS是完全公平调度算法,将所有的进程都统一对待。其中的schedule() 函数它会先抢占当前运行任务,当它开始确定下一个要调度的任务时,它会调用 pick_next_task 函数,通过调度器类调用 CFS 调度器,返回相关 的sched_entity。

static inline struct task_struct *  
pick_next_task(struct rq *rq)  
{  
    const struct sched_class *class;  
    struct task_struct *p;  

  /*使用的数据结构*/
    if (likely(rq->nr_running == rq->cfs.nr_running)) { 
      //如果nr_running==cfs.nr_running,则说明当前rq队列中是没有rt任务的,  
      //rt任务不在cfs队列中,它的优先级的设置和cfs不一样。 
 
        p = fair_sched_class.pick_next_task(rq); //在cfs队列中挑选即将被切换进去的进程
        if (likely(p))  
            return p;  
    }  
    for_each_class(class) {   
        p = class->pick_next_task(rq);  
        if (p)  
            return p;  
    } 
}  
  • 对Linux操作系统的看法

Linux系统同时可以支持多个用户,每个用户对自己的文件设备有特殊的权利,能够保证各用户之间互不干扰。并且具有比window是更好的稳定性以及高效性。且Linux的操作与windows的操作相比更加简洁,在命令行和脚本上面的操作,只需要简单的几行命令就能完成鼠标需要点击很多次的操作功能。

  • 参考资料

https://blog.csdn.net/jnu_simba/article/details/11724277

https://www.cnblogs.com/sky-heaven/p/8118110.html

 

 

 

 

   

 

以上是关于基于Linux进程模型分析的主要内容,如果未能解决你的问题,请参考以下文章

第一次作业:基于Linux0.01深入源码分析进程模型

基于Linux进程模型分析

第一次作业:基于Linux2.6内核源码进程模型分析

第一次作业:基于Linux2.6.30进程模型分析

第一次作业:基于Linux0.11操作系统的进程模型分析

基于Linux Kernel Version 4.13.0-36-generic的源码分析进程模型