第一次作业:基于Linux-0.12的进程分析

  这次作业主要基于Linux-0.12的源代码,分析Linux是如何组织进程,进程的状态之间是如何转换,以及进程是如何调度的。

一. 进程的概念:

  1.进程就是:程序在数据集合上的一次运行过程,是系统进行资源分配和调度的独立单位。

  2.对进程的静态描述为:是一个数据集合,以及在其上运行的程序。

  3.我本来认为进程与程序是差不多的东西,但发现他们其实并不一样,进程是一个动态的概念,不同于程序又依赖于程序,既有联系又有区别,进程具备程序不具备的特征,比如:

   1).动态特征:进程具有生命周期,创建之后才产生,调度时运行,得不到资源就发生阻塞,撤销之后就消亡。进程本身就是一个执行过程,程序,却仅仅是个静态文本(指令合集);

   2).并发特征:多个进程实体,同存于主存中,能在一段时间内同时运行。由此可见进程的并发特征是其第二基本特征,程序不具备并发性;

   3).独立特征:进程是系统进行资源分配和调度的一个基本单位,程序段是不可做为独立单位接收资源分配和调度的;

   4).结构特征(静态特征):为了描述进程的运动变化过程系统为每一个进程配置了一个进程控制块(PCB:Process Control Block),这样静态的看或从结构上看,进程就由正文段,数据集合(结构)以及PCB三部分组成,一般将这三部分组成结构成为进程映像:

进程映像

  

   5). 异步特征:各进程按照其各自独立的,不可预知的速度向前推进。

  In a word, 进程 == 可以和其他程序并发执行的 程序的 一次执行。

二. Linux操作系统是怎么组织进程的:

  1. Linux是一个多任务的开放式操作系统,进程就是许多分离的任务,每一个进程彼此独立,并通过进程间的通信机制实现进程之间的同步与互斥。在Linux系统中,进程与任务是相同的概念。

  2. 系统中有许多进程,Linux要对其进行管理和调度,就要通过存放在系统数据段中的进程控制信息,其中最重要的就是task_struct数据结构。

      • 正文段:是具有可再入性的代码组成的程序(可再入程序也称纯代码,可同时被多个进程或程序所共享的程序),因此对用不同的数据集,可以构成不同的进程,所以正文段是可以调用但是不可以修改的,一般情况下是由系统程序组成。
      • 进程控制块(PCB):PCB中记录了用于描述进程情况及控制进程运行所需的全部属性信息。系统通过PCB感知进程的存在,是进程进行控制存在的唯一标识。系统根据PCB来对并发执行的进程进行控制和管理。系统会在创建进程时就创建该进程的PCB,在撤销一个进程时就撤销其PCB。当操作系统要调用进程执行时。需要从该进程的PCB中查询其现行状态机优先级等调度参数,在调度到某进程后,要根据PCB中保存的处理机状态信息去设置进程回复运行的现场,并根据其PCB中的进程和数据的内存地址来找到程序和数据;进程在执行过程中,当需要与其它进程通信时,也需要访问其PCB;当进程因某种原因而暂停执行时,需要将断点的现场信息保存在其PCB中。
      • 数据集合:由数据所组成的集合。 
PCB通常包含的内容
进程描述信息 进程控制和管理信息 资源分配清单 处理机相关信息
进程标识符(PID) 进程当前状态 代码段指针 通用寄存器值
用户标识符(UID) 进程优先级 数据段指针 地址寄存器值  

  3. 在linux内核代码定义了task_struct 数据结构,包含了一个进程所有的信息:

 1 struct task_struct {
 2 /* these are hardcoded - don‘t touch */
 3     long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
 4     long counter;
 5     long priority;
 6     long signal;
 7     struct sigaction sigaction[32];
 8     long blocked;    /* bitmap of masked signals */
 9 /* various fields */
10     int exit_code;
11     unsigned long start_code,end_code,end_data,brk,start_stack;
12     long pid,pgrp,session,leader;
13     int    groups[NGROUPS];
14     /*
15      * pointers to parent process, youngest child, younger sibling,
16      * older sibling, respectively.  (p->father can be replaced with
17      * p->p_pptr->pid)
18      */
19     struct task_struct    *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
20     unsigned short uid,euid,suid;
21     unsigned short gid,egid,sgid;
22     unsigned long timeout,alarm;
23     long utime,stime,cutime,cstime,start_time;
24     struct rlimit rlim[RLIM_NLIMITS];
25     unsigned int flags;    /* per process flags, defined below */
26     unsigned short used_math;
27 /* file system info */
28     int tty;        /* -1 if no tty, so it must be signed */
29     unsigned short umask;
30     struct m_inode * pwd;
31     struct m_inode * root;
32     struct m_inode * executable;
33     struct m_inode * library;
34     unsigned long close_on_exec;
35     struct file * filp[NR_OPEN];
36 /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
37     struct desc_struct ldt[3];
38 /* tss for this task */
39     struct tss_struct tss;
40 }; 

三. 进程状态如何转换(给出进程状态转换图):

  1. 在多道程序系统中,多个进程都要在CPU上运行,有时还要申请使用其他资源,由于资源的宝贵性,使得并非每个进程都能立即得到资源,从而导致进程之间的竞争(竞争是由两个以上进程以显式或隐式的方式共享资源而引起的状态)。

  2. 一般情况下进程有三种状态,就绪(资源,CPU),运行(资源,CPU),阻塞(资源,CPU)。

  3. Linux在每个进程的task_struct结构中,定义了state域来描述进程的调度状态,共有五种,定义如下:

1 #define TASK_RUNNING        0
2 #define TASK_INTERRUPTIBLE    1
3 #define TASK_UNINTERRUPTIBLE    2
4 #define TASK_ZOMBIE        3
5 #define TASK_STOPPED        4
#define TASK_RUNNING        0

    1)运行态或可运行态:已经占有CPU正在运行,或者正处于运行队列中,等待着系统的进程调度程序schedule()将CPU分配给它。系统中有一个运行队列run_queue,容纳了所有处于可运行状态的进程,当前正在运行的进程也一直处于该队列中,由全局变量current指向。

    *该状态是Linux与一般操作系统的区别,在其他操作系统中,只有正在使用CPU的进程才处于运行状态,其他都处于就绪状态。

#define TASK_INTERRUPTIBLE    1

    2)进程可中断的睡眠态:因等待某一事件或某种资源而加入等待队列,等待资源有效时被唤醒。

#define TASK_UNINTERRUPTIBLE    2

    3)进程不可中断的睡眠态:此时的进程因为硬件条件的不满足而睡眠,处于等待队列中,在任何情况下都不能被打断,除非通过特定的方式来唤醒,比如通过唤醒函数wake_up()等。

#define TASK_ZOMBIE        3

    4)进程僵死状态(终结态):当进程使用系统调用exit自我消亡时,将state设为此状态。发送信号给父进程并释放占有的系统资源,但它的task_struct结构仍未释放。父进程通过系统调用wait收集其中包含的出口码及一些计时信息后,释放他的task_struct结构。

#define TASK_STOPPED        4

    5)进程被暂停运行状态:进程暂时停止运行来接受某种处理,通过其他进程的信号才能唤醒。

 4. 进程状态的转换:

    1).Linux中用户进程是由父进程执行系统调用fork()或者clone()等创建的。这些系统调用都通过调用do_fork()函数来完成子进程的创建。do_fork()函数创建一个新进程,为其创建一个task_struct结构,继承父进程现有的资源,子进程创建后的状态为TASK_RUNNING态,父进程将它挂入到运行队列run_queue中,等待处理器的分配。

    2).获得CPU而正在运行的进程如果申请不到某个资源,则调用函数sleep_on()或interruptible_sleep_on()转入睡眠,其task_struct结构从run_queue队列移入xiangying的等待队列。如果调用sleep_on(),则其状态转入不可中断的睡眠态TASK_UNINTERRUPTIBLE,如果调用interruptible_sleep_on(),则其状态转入可中断睡眠态TASK_INTERRUPTIBLE 。无论转入哪种睡眠状态,都将调用schedule()函数把睡眠进程释放的CPU重新分配给run_queue队列中的某个可运行的进程。

    3).转入TASK_INTERRUPTIBLE的睡眠进程在它申请的资源有效时将被唤醒(被某函数,信号或者中断),而转入TASK_UNINTERRUPTIBLE的睡眠进程只有在它申请的资源有效时被唤醒,不能被信号,定时器中断唤醒。这些被唤醒的进程都转入TASK_RUNNING状态, 并进入run_queue队列。

    4).当进程收到暂停或停止信号时,状态转入TASK_STOPPED ,暂停运行,CPU重新分配给run_queue队列中的其他可运行进程,只有通过其他进程发送恢复信号,才能把TASK_STOPPED 进程唤醒,重新进入run_queue队列。

    5).当进程运行完成,执行系统调用exit()或do_exit()时,进程转入僵死态TASK_ZOMBIE,释放所占有的系统资源,同时启动schedule()把CPU分配给run_queue队列中的其他进程。

  5. 进程状态转换图:

四. 进程是如何调度的:

  1. Linux主要采用了基于优先权的时间片轮转法为进程分配CPU。按照这种调度方法,系统给每个运行进程分配一个时间片,而优先权的大小又决定了哪个进程被调度运行(Linux的进程调度并不局限于某一种调度策略,它融合了基于优先权的轮转法调度,基于优先权的先进先出调度以及多级反馈轮转调度的策略,具有很高的综合性)。 

  2.

       long priority;

    1). 进程(实时和普通)的优先级反映了进程相对于其他进程的可选择度,也是系统每次允许进程运行的时间。

       long counter;

    2). 进程运行所剩余的时间片,每次时钟中断发生时,值-1,直到为0,counter = 0表示进程的时间片已经用完,要停止运行。

  3. Linux进程调度时机主要有:

1). 进程状态转换的时时。如进程终止、进程睡眠;

2). 当前进程的时间片用完时(current->counter=0),要重新选择一个进程;

3). 设备驱动程序,直接调用schedule();

4). 进程从中断、异常及系统调用处理后返回到用户态时。

  4. Linux-0.12 schedule()如下:

void schedule(void)
{
    int i,next,c;
    struct task_struct ** p;
/*有信号来时,唤醒进程*/
    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
        if (*p) {
            if ((*p)->timeout && (*p)->timeout < jiffies) {
                (*p)->timeout = 0;
                if ((*p)->state == TASK_INTERRUPTIBLE)
                    (*p)->state = TASK_RUNNING;
            }
            if ((*p)->alarm && (*p)->alarm < jiffies) {
                (*p)->signal |= (1<<(SIGALRM-1));
                (*p)->alarm = 0;
            }
            if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
            (*p)->state==TASK_INTERRUPTIBLE)
                (*p)->state=TASK_RUNNING;
        }
    while (1) {
        c = -1;
        next = 0;
        i = NR_TASKS;
        p = &task[NR_TASKS];
        while (--i) {
            if (!*--p)
                continue;
            if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
                c = (*p)->counter, next = i;
        }
        if (c) break;
        for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
            if (*p)
                (*p)->counter = ((*p)->counter >> 1) +
                        (*p)->priority;
    }
    switch_to(next);
}

五. 我对Linux操作系统进程模型的看法:

  Linux支持多进程,进程控制块task_struct结构包括进程标识,进程状态,进程调度,进程指针,文件管理和虚存管理等,Linux对普通进程采用的是优先级调度策略。尽量公平合理的进行各进程之间的调度。

六.  参考资料:

https://blog.csdn.net/hgnuxc_1993/article/details/54847732

http://www.docin.com/p-820504201.html

https://blog.csdn.net/songjinshi/article/details/23262923

操作系统原理与分析(第一版);曹聪,范廉明 著;科学出版社

原文地址:https://www.cnblogs.com/m-xxxxy/p/8971452.html

时间: 2024-10-27 12:23:49

第一次作业:基于Linux-0.12的进程分析的相关文章

第一次作业 基于Linux 0.12的进程模型分析

作业内容 挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容: 操作系统是怎么组织进程的 进程状态如何转换(给出进程状态转换图) 进程是如何调度的 谈谈自己对该操作系统进程模型的看法 1.操作系统是怎么组织进程的 1.1什么是进程 程序是一个可执行的文件,而进程是一个执行中的程序实例.Linux操作系统上利用分时技术,可同时运行多个进程.利用分时技术,在Linux操作系统上同时可以运行多个进程.分时技术的基本原理是把CPU的运行时间划分成一个个规定长度的时间片,让每个进程在一个时间

第一次作业:Linux 2.6.28进程模型与CFS调度器分析

第一次作业 1.摘要 本文主要针对Linux Kernel 2.6.28内核版本,描述了进程的概念以及调用过程. Linux Kernel源码查阅地址:https://elixir.bootlin.com/linux/v4.6/source/include/linux/types.h 2. 何谓进程 2.1 进程的概念 进程的一种官方定义: 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,也是操作系统进行资源分配和调度的一个独立单位. 简而言之,进程是操作系统为正在运行的程序所建

Linux 0.12 内核管理存储器

其分段,用分段的机制把进程间的虚拟地址分隔开. 每一个进程都有一张段表LDT.整个系统有一张GDT表.且整个系统仅仅有一个总页表. 其地址翻译过程为: 程序中给出的32位地址(实际上被看做段内偏移地址),再依据代码段寄存器CS中的16位段选择子,可在GDT或LDT中查找对应的段描写叙述符.从段描写叙述符中提取段的基地址,与程序给出的32位地址相加.得到结果为线性地址. 依据此线性地址查找系统页文件夹表,再查二级或是多级页表,终于得到物理地址. 此方式系统仅仅有一个4G的线性地址空间由各进程共享(

Linux 0.12的编译与链接

昨天花了很长时间去编译链接linux 0.12版的kernel,发现在64位ubuntu下,这位兄台的文章写得最全最好,几乎涵盖了我遇到的所有问题,在此记录一下. 编译linux 0.12 链接linux 0.12

《linux 内核完全剖析》编译linux 0.12 内核 Ubuntu 64bits 环境

我×...终于好了,大概3 4个小时吧...各种毛刺问题.终究还是闯过来了.... [email protected]:~/Downloads/linux-0.00-050613/linux-0.00$ make ld -s -x -M head.o  -o system > System.map dd bs=32 if=boot of=Image skip=1 16+0 records in 16+0 records out 512 bytes (512 B) copied, 0.000605

Linux 0.12和Linux 0.11内核学习——Google邮件列表

亲,你在学习Linux 0.12或0.11内核吗?快来加入我们吧,就缺你了!!! 为什么选用邮件列表呢?因为赵炯博士那个论坛交流不是很方便,经常发了贴没人回,人气相比十年前论坛刚成立时弱了不少.很多人,很多元老级别的人物,消失了...再也没有出现过. 而QQ群很繁杂,比如你肯定会因为一些兴趣爱好加入一些QQ群,但是也就刚加进去或者自己有什么要问的时候说几句,之后就屏蔽了,因为每天都有人在聊天,什么内容都有,想退群却又怕以后有用,不退吧又很烦,只能屏蔽了潜水. 而邮件列表,你可以订阅主题,实时追踪

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

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

Linux 0.12内核与现代内核在内存管理上的区别

0.12内核的内存管理比较简单粗暴,内核只用了一个页目录,只能映射4G的线性空间,所以每个进程的虚拟空间(逻辑空间)只能给到64M,最多64个进程:每个进程都有对应的任务号nr,当一个进程需要分配进程空间时,只需要nr乘以64M就可以得出该进程空间的线性起始地址.然后该进程的代码段.数据段描述符里面的基址字段会被设定为(nr x 64M),同时可以为进程分配页目录项和页目录表用以承载映射关系. 之后如果进程要访问自己空间内的某个地址时就会首先用基地址与程序内32位偏移地址(逻辑地址)合成出线性地

《linux 内核全然剖析》编译linux 0.12 内核 Ubuntu 64bits 环境

我×.. . 最终好了,大概3 4个小时吧...各种毛刺问题.终究还是闯过来了.. .. [email protected]:~/Downloads/linux-0.00-050613/linux-0.00$ make ld -s -x -M head.o  -o system > System.map dd bs=32 if=boot of=Image skip=1 16+0 records in 16+0 records out 512 bytes (512 B) copied, 0.000