第一次作业:深入源码分析进程模型(linux)

一.什么是进程

  计算机上有许多可以运行的软件,其中也包括操作系统,这些软件运行时,就产生了一个或多个进程。

二.Linux系统中进程的组织与转换

1>Linux中进程的描述符(即用来描述一个进程的结构体)

struct task_struct {

    ......

    volatile long state;                   // 描述进程的运行状态

    void *stack;                           // 指向内核栈

    struct list_head tasks;                // 用于加入进程链表
    ......

    struct mm_struct *mm, *active_mm;      // 指向该进程的内存区描述符

    ......

    pid_t pid;                             // 进程ID

    pid_t tgid;                            // 线程组ID

    struct pid_link pids[PIDTYPE_MAX];     // 用于连接到PID、TGID、PGRP、SESSION哈希表

    ........

    struct task_struct __rcu *real_parent; // 指向创建其的父进程,如果其父进程不存在,则指向init进程
  
    struct task_struct __rcu *parent;      // 指向当前的父进程,通常与real_parent一致 
    
    struct list_head children;             // 子进程链表

    struct list_head sibling;              // 兄弟进程链表

    struct task_struct *group_leader;      // 线程组领头线程指针

    struct thread_struct thread;           // 在进程切换时保存硬件上下文

    struct fs_struct *fs;                  // 当前目录

    struct files_struct *files;            // 指向文件描述符,该进程所有打开的文件会在这里面的一个指针数组里

    ........

  struct signal_struct *signal;          // 信号描述符

  struct sighand_struct *sighand;        // 信号处理函数描述符

  sigset_t blocked, real_blocked;
  /* sigset_t是一个位数组,每种信号对应一个位,linux中信号最大数是64
   * blocked: 被阻塞信号掩码
   * real_blocked: 被阻塞信号的临时掩码
   */  

  sigset_t saved_sigmask;                // set_restore_sigmask()被使用则恢复

  struct sigpending pending;             // 私有挂起信号队列 

    ........
}

  在一个进程描述符中,包含了这个进程的所有信息。在内核中,会有一个进程链表通过使用进程描述符中的tasks结构把所有进程的进程链表链接起来。

  PID为进程标识符,如同学生的学号一样,具有唯一性。一般情况下,PID编号为顺序的,但有一个上限,达到上限后开始循环查找空闲的PID值。

  进程之间的关系:

  一个进程大多有另一个进程创建,这些被创建的进程与创建它们的进程就为父子关系。

  如果一个进程创建了多个进程,则这些进程就为兄弟关系。

  而如果一个进程P0创建了进程P1、P2、P3,进程P3又创建了进程P4,它们整个链表情况是这样的:

  

2>Linux中进程的状态

  TASK_RUNNING:就绪态或者运行态,进程就绪可以运行,但是不一定正在占有CPU

  TASK_INTERRUPTIBLE:睡眠态,浅度睡眠,可以响应信号

  TASK_UNINTERRUPTIBLE:睡眠态,深度睡眠,不响应信号,典型场景是进程获取信号量阻塞

  TASK_STOPED:停止态,当进程接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号后进入

  TASK_ZOMBIE:僵尸态,进程已退出或者结束,但是父进程还不知道,没有回收时的状态

3>Linux中进程的转换

  Linux中是所有switch to()来完成进程从prev到next的切换

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:该参数存放的是当前进程描述符指针

  next:该参数存放的是需要被替换来的新进程的指针

  last:该参数存放的是当前进程之前所占用CPU的进程的指针

Linux进程转换图:

 

 4>进程的组织

  所有处于TASK_RUNNING的创建都会被放入CPU的运行队列中,它们可以在不同的CPU运行队列中。处于TASK_STOPED的进程没有建立专门的链表,但可以通过PID以及父子链表进行访问。处于TASK_INTERRUPTIBLE以及处于TASK_UNINTERRUPTIBLE状态的进程都会被放入相应的等待队列中,每个等待队列的唤醒条件不等。

三.Linux进程是如何调度的

1>Linux进程优先级

  Linux进程由调度优先级可分为两种:实时进程与普通进程。任何情况下,实时进程的优先级都高于普通进程。

2>实时进程的调度

  实时进程只有静态优先级,优先级高的总是比优先级低的先运行。当两个进程优先级相同时,则会按照队列上的顺序执行进程。

3>普通进程的调度

  普通进程通过动态优先级来进行调度。动态优先级是有静态优先级调整而来,二Liunx系统给了用户一个可以调节静态优先级的接口:nice值。静态优先级与nice值的关系为:

  static_prio=MAX_RT_PRIO +nice+ 20

  nice值的范围是-20~19,因而静态优先级范围在100~139之间。nice数值越大就使得static_prio越大,最终进程优先级就越低。

  动态优先级的计算公式为:

  dynamic_prio = max (100, min (static_prio - bonus + 5, 139))

  bouns为进程的平均睡眠时间

四.心得体会

  Linux作为一个具有代表性的操作系统,以及经历了多年的更新、优化。在此次作业中,我分析了操作系统中进程的组织、转换和调度,明白了Linux系统通过对进程的调整,以在不影响效果下达到CPU的最高的使用效率。通过此次作业,我了解了一个优秀的操作系统应该具有什么样的条件,对操作系统的理解更深了一层。

五.参考链接

  https://blog.csdn.net/kklvsports/article/details/52268085

  https://www.cnblogs.com/tolimit/p/4530370.html

  http://blog.chinaunix.net/uid-23253303-id-3952935.html

  http://www.cnblogs.com/zhaoyl/archive/2012/09/04/2671156.html

 

posted @ 2018-05-01 18:48  czh1999  阅读(181)  评论(0编辑  收藏  举报