linus进程模型的分析与学习
康帝 2018-05-17 来源 : 阅读 33 评论 0

摘要:学习linus进程的加深了对进程这一抽象事物的理解更好地对抽象事物进行认知,以下做具体介绍。

 

学习linus进程的加深了对进程这一抽象事物的理解更好地对抽象事物进行认知,以下做具体介绍。

      1.进程与线程

 1.0什么是进程

?计算机上所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程,简称进程。

?一个进程就是一个正在执行程序的实例,包括程序计数器,寄存器和变量的当前值。从概念上说,每个进程都拥有它自己的虚拟cpu.

 1.1什么是线程

?在传统操作系统中,每个进程有一个地址空间和一个控制线程

?线程从体量上看表现为轻量级的进程

 1.2进程与线程的关系

?从需求上看,在许多应用中同时发生着多种活动。其中某些活动随着时间的推移会被堵塞。通过将这些应用程序分解成可以准并行运行的多个顺序线程,程序设计模型会变得更简单。

?从实现上因为线程比进程更加轻量所以创建简单,撤除也很快速。

 

      2.0linus对进程的定义

2.0 linus中进程的表示

?在linux系统中,进程由一个叫task_struct的结构体描述,也就是说linux中的每个进程对应一个task_struct结构体。该结构体记录了进程的一切。

 

 

struct task_struct

 

 

{

 

 

 //这个是进程的运行状态,-1代表不可运行,0代表可运行,>0代表已经停止。

 

 

    volatile long state;

  /*        flags是进程当前的状态标志,具体如下:

 

 

            0x0000 0002表示进程正在被创建

 

 

            0x0000  0004表示进程正准备退出

 

 

            0x0000  0040表示此进程被fork出,但是并没有执行exec

 

 

            0x0000   0400表示此进程由于其他进程发送相关信号而被杀死

 

 

    */

 

 

    unsigned int flags;

 

 

    //表示此进程的运行优先级 

 

    unsigned     int   rt_priority

 

 

    //该结构体记录了进程内存使用的相关情况

 

 

    struct   mm_struct *mm;

 

 

    //进程号,是进程的唯一标识 

 

    pid_t   pid;

 

 

   //进程组号 

 

    pid_t  tgid;

 

 

    //real_parent是该进程的"亲生父亲",不管其是否被"寄养"

 

 

    struct  task_struct  *real_parent;

 

 

    //parent是该进程现在的父进程,有可能是"继父"

 

 

    struct   task_struct  *parent;

 

 

    //这里children指的是该进程孩子的链表,可以得到所有孩子的进程描述符

 

 

    struct   list_head    children;

 

 

    //同理,sibling该进程兄弟的链表,也就是其父进程的所有孩子的链表

 

 

    struct    list_head    sibling;

 

 

   //这个是主线程的进程描述符,也许你会奇怪,为什么线程用进程描叙符表示,因为linux并没有单独实现线程的相关结构体,只用一            个进程来代替线程,然后对其做一些特殊的处理。

 

 

    struct   task_struct  *group_leader;

 

 

   //这个是该进程所有线程的链表

 

 

    struct   list_head   thread_group;

 

 

    //这个是该进程使用cpu时间的信息,utime是在用户态下执行的时间,stime 是在内核态下执行的时间 

 

    cputime_t   utime,stime;

 

 

     //comm是保存该进程名字的字符数组,长度最长为15,因为TASK_COMM_LEN为16

 

 

    char   comm[TASK_COMM_LEN];

 

 

    //打开的文件相关信息结构体

 

 

    struct  files_struct  *files;

 

 

    //信号相关信息的句柄

 

 

    struct   signal_struct  *signal;

 

 

    struct   sigband_struct  *sighand;

 

 

};

 

 

?进程号(pid),就像我们的身份证ID一样,每个人的都不一样。进程ID也是,是其唯一标示。

 

?进程的状态,标识进程是处于运行态,等待态,停止态,还是死亡态

 

 A.运行态:此时进程 或者正在运行,或者准备运行

 

 B.等待态:此时进程在等待一个事件发生或某种系统资源

 

 C.停止态:此时进程被终止

 

 D.死亡态:这是一个已终止的进程,但还在进程向量数组中,占有一个task_struct结构。

 

 ?进程的优先级和时间片。不同有优先的进程,被调度运行的次序不一样,一般是高优先级的进程先运行。时间片标识一个进程将被处理器运行的时间

 

 ?虚拟内存    大多数进程有一些虚拟内存(内核线程和守护进程没有) ,并且Linux必须跟踪内存如何映射到系统物理内存。

 

 ?处理器相关上下文,一个进程可以被认为是系统当前状态的总和。每当一个进程运行时,它要使用处理器的寄存器、栈等,这是进程的上下文(context)。并且,每当一个进程被暂停时,所有的CPU相关上下文必须保存在该进程的task_struct中。当进程被调度器重新启动时其上下文将从这里恢复

 3.0linus进程的生命周期

3.0进程创建:

?通过函数产生如clone(),fork(),vfork()

?其中fork()与vfork()都是系统调用clone()实现的,clone()函数原型:
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);其中flags 规定了父进程与子进程的关系

 

3.1进程切换:

?进程切换统一发生在schedule()函数中,其中,switch_to()是一个宏,其定义如下:

#define switch_to(prev,next,last) do { \
unsigned long esi,edi; asm volatile("pushfl\n\t"     "pushl %%ebp\n\t"     "movl %%esp,%0\n\t" /* save ESP */     "movl %5,%%esp\n\t" /* restore ESP */     "movl $1f,%1\n\t" /* save EIP */     "pushl %6\n\t" /* restore EIP */     "jmp __switch_to\n"     "1:\t"      "popl %%ebp\n\t"     "popfl"      :"=m" (prev->thread.esp),"=m" (prev->thread.eip),      "=a" (last),"=S" (esi),"=D" (edi)     :"m" (next->thread.esp),"m" (next->thread.eip),      "2" (prev), "d" (next)); } while (0)

 

 

?prev存放的是当前的current进程描述符指针。
next存放的是需要被替换进来的进程的描述符指针。
last比较麻烦,在一次进程被切换出去又切换进来的过程中,一般会涉及到3个进程,进程A,B,C,当进程A运行时,切换到进程B,然后再系统运行一段时间后,系统中运行的进程变为了进程C,然后由进程调度程序调度进程A运行,当调度进程A运行时,进程A使用的是自己的内核栈,里面的prev和next就还是从A切换到B时的prev, next,即prev = A, next=B.然而本次从C切换到A应该将prev改变为C才对,而不是A了

 

4.0linus进程的六种状态

?运行状态(TASK_RUNNING)

?当进程正在被CPU执行,或已经准备就绪随时可由调度程序执行,则称该进程为处于运行状态(running)。进程可以在内核态运行,也可以在用户态运行。当系统资源已经可用时,进程就被唤醒而进入准备运行状态,该状态称为就绪态。这些状态(图中中间一列)在内核中表示方法相同,都被成为处于TASK_RUNNING状态。

 

?可中断睡眠状态(TASK_INTERRUPTIBLE)

?当进程处于可中断等待状态时,系统不会调度该进行执行。当系统产生一个中断或者释放了进程正在等待的资源,或者进程收到一个信号,都可以唤醒进程转换到就绪状态(运行状态)。

 

?暂停状态(TASK_STOPPED)

?当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。

 

?僵死状态(TASK_ZOMBIE)

?当进程已停止运行,但其父进程还没有询问其状态时,则称该进程处于僵死状态。

 

?不可中断睡眠状态(TASK_UNINTERRUPTIBLE)

?与可中断睡眠状态类似。但处于该状态的进程只有被使用wake_up()函数明确唤醒时才能转换到可运行的就绪状态。

 

?睡眠状态(TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE)。

?只有当进程从“内核运行态”转移到“睡眠状态”时,内核才会进行进程切换操作。在内核态下运行的进程不能被其它进程抢占,而且一个进程不能改变另一个进程的状态。为了避免进程切换时造成内核数据错误,内核在执行临界区代码时会禁止一切中断

 linus进程模型的分析与学习

 

5.0linus进程的调度算法

5.1进程转换的过程

 ?进程创建时的状态为不可打断睡眠,在do —fork() 结束前被父进程唤醒后。变为执行状态,处于执行状态的进程被移到run —queue 就绪任务队列中等待调度。适当时候由schedule0 按调度算法选中,获得CPU ,若采用轮转法,即时,由时钟中断触发timer —interrupt() ,其内部调用schedule() ,引起新一轮调度,当前进程的状态仍处于执行状态,因而把当前进程挂蛰Jruil —queue 队尾。获得CPU 且正在运行的进程若申请不到某资源。则调用sleep —on() 或interruptible —sleep —on() 睡眠,其task —struct 进程控制块挂到相应资源的wait —queue 等待队列如果调用sleep —on() 。则其状态变为不可打断睡眠,如果调用interruptible —sleep —on() ,则其状态变为可打断睡眠,Sleep—on() 或interruptible —sleep —on() 将调用schedule() 函数把睡眠进程释放。

5.2进程调度策略

 

 linus进程模型的分析与学习

 

?Linux 系统中,为了高效地调度进程,将进程分威两类:实时进程和普通进程( 又称非实时进程或一般进程) ,实时进程的优先级要高于其他进程,如果一个实时进程处于可执行状态,它将先得到执行.实时进程又有两种策略:时间片轮转和先进先出,在时间片轮转策略中。每个可执行实时进程轮流执行一个时间片,而先进先出策略每个进程按各自在运行队列中的顺序执行且顺序不能变化。

?在Linux 中,进程调度策略共定义了3 种:

Linux 系统中的每个进程用task ,struct 结构来描述,进程调度的依据是task —struct 结构中的policy 、priority 、counter 和rt —priority ,PCB 中设置Policy 数据项,其值用于反映针对不同类型的进程而采用的调度策略。SCHED —RR 和SCHED —FIFO 用于实时进程。分别表示轮转调度策略和先进先出调度策略;SCHED —OTHER 表示普通进程,也按照轮转调度策略处理。这三类调度策略均基于优先级.PCB 中设置Priority 数据项,其值为普通进程的调度优先级.普通进程的可用时间片的初始值即为该值,该值通过系统调用是可以改变的。PCB 中设置rt ~p riority 数据项,其值是实时进程专用的调度优先级,实时进程的可用时间片的初始值即为该值.该优先级也可以用系统调用来修改,PCB 中设置counter 数据项。用于进程可用时间片时值的计数,初始值为rt —priority 或Priority 。进程启动后该值随时钟周期递减。

 

 6.0 linus进程学习分析

?通过学习linus进程的加深了对进程这一抽象事物的理解更好地对抽象事物进行认知

 

 

 

 

   

 

 


本文由 @康帝 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
X
免费获取海同IT培训资料
验证码手机号,获得海同独家IT培训资料
获取验证码
提交

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号