第一次作业:深入源码分析进程模型(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

原文地址:https://www.cnblogs.com/czh1999/p/8977053.html

时间: 2024-10-14 00:52:50

第一次作业:深入源码分析进程模型(linux)的相关文章

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

1.前言 本文基于Linux 2.6.32分析其进程模型,包括进程的概念.组织.转换.调度等内容,帮助对操作系统课程及Linux相关知识的理解和学习. 附Linux Kernel 2.6.32源码下载地址: https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.tar.gz 2.进程的概念 2.1什么是进程? 在正式开始结合源代码分析进程模型之前,我们首先需要搞清楚进程的究竟是什么. 维基百科上对于进程的定义如下:

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

一.前言 本文主要基于Linux0.01源代码分析进程模型.Linux 0.01虽然是Linux的第一个发行版本,但是却基本具备了操作系统中最重要的组成部分,同时Linux 0.01只有8500行左右的代码,对于初学者而言学习起来比较简单一点. Linux 0.01源代码下载地址: https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/ 二.进程的定义 进程是程序执行的基本单位.(其中,进程和程序的区别:程序指的是由若干函数组成的可执

第一次作业:基于Linux系统深入源码分析进程模型

1.前言 本文主要基于Linux 2.6源代码分析进程模型.源代码下载地址:https://elixir.bootlin.com/linux/v2.6.39/source 2.进程 定义:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. 3.Linux系统进程的组织 进程是由进程控制块(PCB).程序段.数据段三部分组成. 3.1 进程控制块 进程控制块(Processing Control Block),是操作系统核心中一种数据结构

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

1.进程的定义 从系统允许多个程序同时进入CPU那一天开始,我们才有了进程,进程的出现,解决了程序并发执行时对系统资源共享的描述问题,同时顺路解决了程序执行时动态特征的描述问题. 进程:一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位 进程四要素: 1.有一段程序供其执行,该程序不一定是一个进程独享,也可以和其他进程共享. 2.有进程专用的内核空间堆栈. 3.在内核中有一个名为"进程控制块"的task_struct,内核通过结构对进程进行

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

前言:          这是一篇关于linux操作系统的简单介绍.linux本身不能算是操作系统,只是一个内核,基于linux内核的操作系统有很多,比如流行的android,ubuntu,红旗linux等等.Linux以它的高效性和灵活性著称.它能够在PC计算机上实现全部的Unix特性,具有多任务.多用户的能力.Linux是在GNU公共许可权限下免费获得的,是一个符合POSIX标准的操作系统.Linux操作系统软件包不仅包括完整的Linux操作系统,而且还包括了文本编辑器.高级语言编译器等应用

深入源码分析进程模型

1.操作系统是怎么组织进程的 struct task_struct { ...... /* 进程状态 */ volatile long state; /* 指向内核栈 */ void *stack; /* 用于加入进程链表 */ struct list_head tasks; ...... /* 指向该进程的内存区描述符 */ struct mm_struct *mm, *active_mm; ........ /* 进程ID,每个进程(线程)的PID都不同 */ pid_t pid; /* 线

源码分析:动态分析 Linux 内核函数调用关系

源码分析:动态分析 Linux 内核函数调用关系 时间 2015-04-22 23:56:07  泰晓科技 原文  http://www.tinylab.org/source-code-analysis-dynamic-analysis-of-linux-kernel-function-calls/ 主题 Linux源码分析 By Falcon ofTinyLab.org 2015/04/18 缘由 源码分析是程序员离不开的话题. 无论是研究开源项目,还是平时做各类移植.开发,都避免不了对源码的

Netty源码分析--内存模型(上)(十一)

前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们来一起看下客户端接入完成之后,是怎么实现读写操作的?我们自己想一下,应该就是为刚刚读取的数据分配一块缓冲区,然后把channel中的信息写入到缓冲区中,然后传入到各个handler链上,分别进行处理.那Netty是怎么去分配一块缓冲区的呢?这个就涉及到了Netty的内存模型. 当然,我们在第一节的时

Netty源码分析--内存模型(下)(十二)

这一节我们一起看下分配过程 1 PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) { 2 PooledByteBuf<T> buf = newByteBuf(maxCapacity); // 初始化一块容量为 2^31 - 1的ByteBuf 3 allocate(cache, buf, reqCapacity); // reqCapacity = 1024 进入分配