热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

第一次作业:基于Linux源代码分析进程模型

作业内容:挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容:操作系统是怎么组织进程的进程状态如何转换(给出进程状态转换图)进程是如何调度的谈谈自己对该操作系统进程模

作业内容:

挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容:

  • 操作系统是怎么组织进程的
  • 进程状态如何转换(给出进程状态转换图)
  • 进程是如何调度的
  • 谈谈自己对该操作系统进程模型的看法

 

1.进程

           进程的概念:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。每个进程是有生命周期的,在用户空间,进程是由PID标识。在生命周期中PID是唯一且不变的。在linux内核中,进程是由task_struct结构表示(linux内核存储进程信息的固定格式),此结构中包含此进程所需要的所有数据,如进程的状态、运行的时间、堆栈、父进程、执行的线程等。linux将多个进程放在任务队列的双向链表中。

         进程的创建:当启动linux时,加载完内核后,会创建第一进程init。之后所有的进程都是由init或父进程向内核发起调用fork()自身而来,再而克隆clone()自身的数据给子进程。

          进程的优先级:系统中同时有大量的进程需要执行时,是需要优先级来先后处理的。task_struct中是有记录优先级的,内核是如何知道哪个优先级高哪个低,遍历。内核2.6将进程优先级划分位固定的个数。

Linux环境下分别使用getpid()和getppid()函数来得到进程ID和父进程ID,分别使用getuid()和geteuid()函数来得到进程的用户ID和有效用户ID,分别使用getgid()和getegid()来获得进程的组ID和有效组ID,其函数原型如下:

#include
pid_t getpid(
void); //获取进程ID
pid_t getppid(void); //获取父进程ID

uid_t getuid(
void); //获取用户ID
uid_t geteuid(void); //获取有效用户ID

gid_t getgid(
void); //获取组ID
gid_t getegid(void); //获取有效组ID

 

      

2.查看进程

《第一次作业:基于Linux源代码分析进程模型》

 

3.操作系统是怎么组织进程

 

             task_struct的 real_parent成员指向父进程,parent成员指向“养父进程”;children成员表示该进程的子进程链表;sibling成员表示该进程的兄弟进程链表。系统启动后创建了第一个进程:进程0(swapper,也叫idle),进程0创建了第一个用户进程(进程1/sbin/init)和第一个内核进程(进程2kthreadd),之后进程0进入idle状态,当没有进程可以被调度的时候运行该进程,不做具体的事情。进程1的主要作用是处理僵尸进程。当某个父进程比子进程提前消亡时,父进程会给子进程重新寻找“养父进程”,一般就是进程1,由进程1负责处理该子进程的消亡。当创建一个新进程时,新进程的父进程为当前进程或者线程,并且把新进程加入到父进程的子链表中。如果使用CLONE_THREAD标志创建一个新进程时,新进程的父进程为当前进程或线程的父进程,并且把新进程加入到父进程的子链表中。CLONE_THREAD标志创建一个新线程时,新线程的父进程为当前线程组长的父进程,所以线程组看起来像是一个独立的进程。此时,因为创建的是线程,所以不需要把新线程加入到任何子链表中。

 

4.进程状态如何转换

          由于进程的不断创建,系统的资源已经不能满足进程运行的要求,这个时候就必须把某些进程挂起(suspend),对换到磁盘镜像区中,暂时不参与进程调度,起到平滑系统操作负荷的目的。 
引起进程挂起的原因是多样的,主要有: 
1. 系统中的进程均处于等待状态,处理器空闲,此时需要把一些阻塞进程对换出去,以腾出足够的内存装入就绪进程运行。 
2. 进程竞争资源,导致系统资源不足,负荷过重,此时需要挂起部分进程以调整系统负荷 ,保证系统的实时性或让系统正常运行。 
3. 把一些定期执行的进程(如审计程序、监控程序、记账程序)对换出去,以减轻系统负荷。 
4. 用户要求挂起自己的进程,以便根据中间执行情况和中间结果进行某些调试、检查和改正。 
5. 父进程要求挂起自己的后代进程,以进行某些检查和改正。 
6. 操作系统需要挂起某些进程,检查运行中资源使用情况,以改善系统性能;或当系统出现故障或某些功能受到破坏时,需要挂起某些进程以排除故障。

        引起进程状态转换的具体原因如下:

  • 等待态—→挂起等待态:如果当前不存在就绪进程,那么至少有一个等待态进程将被对换出去成为挂起等待态;操作系统根据当前资源状况和性能要求,可以决定把等待态进程对换出去成为挂起等待态。
  • 挂起等待态—→挂起就绪态:引起进程等待的事件发生之后,相应的挂起等待态进程将转换为挂起就绪态。
  • 挂起就绪态—→就绪态:当内存中没有就绪态进程,或者挂起就绪态进程具有比就绪态进程更高的优先级,系统将把挂起就绪态进程转换成就绪态。
  • 就绪态—→挂起就绪态:操作系统根据当前资源状况和性能要求,也可以决定把就绪态进程对换出去成为挂起就绪态。
  • 挂起等待态—→等待态:当一个进程等待一个事件时,原则上不需要把它调入内存。但是在下面一种情况下,这一状态变化是可能的。当一个进程退出后,主存已经有了一大块自由空间,而某个挂起等待态进程具有较高的优先级并且操作系统已经得知导致它阻塞的事件即将结束,此时便发生了这一状态变化。
  • 运行态—→挂起就绪态:当一个具有较高优先级的挂起等待态进程的等待事件结束后,它需要抢占 CPU,,而此时主存空间不够,从而可能导致正在运行的进程转化为挂起就绪态。另外处于运行态的进程也可以自己挂起自己。
  • 新建态—→挂起就绪态:考虑到系统当前资源状况和性能要求,可以决定新建的进程将被对换出去成为挂起就绪态。
  • 《第一次作业:基于Linux源代码分析进程模型》

5.进程是如何调度的

 Linuz 中进程调试都是非强占式的,系统采用相当简单的基于优先级的调试算法。进程只能在核心态等待,当进程执行系统调用时它会从用户态“陷入”核心态,这时内核代表这个进程执行。当进程在核心态等待某事件时系统会将它挂起,而后让另一个进程运行。Liunz 的调度函数是schedule,定义在kernelfsched.c 中。Schedule 函数完成以下工作:

  (1)定义两个指针。prev 是正在运行的进程,next 是调度程序选择下一个将要运行的进程。这两个指针可能会相同。

  (2) 处理调度任务队列。首先检查任务是否在队列中,如果有等待处理的任务则顺序处理其中的每一个任务,然后将队列清空。

  (3) 检查任务是否在中断队列中,如果在,则不做本次调度,直接返回。   (4) 底半处理。就是中断处理的后半部分。

  (5) 调度运行进程队列。与Minix 不同的是Linuz 没有将可运行进程按其优先级分成多个队列,而是把系统中所有可运行进程(状态为TASK_RUNNING) 都排在同一个队列中,这实际上是一个双向链表。 (6) 选择下一个要运行的进程。从init_task.nezt开始,顺序搜索运行进程队列,对每一个进程计算其weight 值,选择weight 值最大的运行。weight 值的计算规则是: 1.如果当前进程声明要放弃CPU (进程调度策略中设置了SCHED_YIEID 位),则其weigt=0。2.如果进程是实时进程,则weight=rt_priority+1000。3.如果进程的counter’值为0,则weight=0。4.如果进程的counter不为0,则weight=countertpriority; 如果进程是当前进程,则其weight 值再加1,即当前进程优先。
(7) 切换。保存当前进程结构(task_struct )中的上下文,就是当前进程运行到调度程序结束时处理器的上下文。新加载进程的上下文也是它上次运行到调度地的快照,包括进程的程序计数器和各寄存器的内容。

 

1.创建一些局部变量

struct task_struct *prev, *next;//当前进程和一下个进程的进程结构体
unsigned long *switch_count;//进程切换次数
struct rq *rq;//就绪队列
int cpu;

 

2.关闭内核抢占,初始化一部分变量

need_resched:
preempt_disable();
//关闭内核抢占
cpu = smp_processor_id();
rq
= cpu_rq(cpu);//与CPU相关的runqueue保存在rq中
rcu_note_context_switch(cpu);
prev
= rq->curr;//将runqueue当前的值赋给prev

 

3.选择next进程

next = pick_next_task(rq, prev);//挑选一个优先级最高的任务排进队列
clear_tsk_need_resched(prev);//清除prev的TIF_NEED_RESCHED标志。
clear_preempt_need_resched();

 

4.完成进程的调度

if (likely(prev != next)) {
//如果prev和next是不同进程
rq->nr_switches++;//队列切换次数更新
rq->curr = next;
++*switch_count;//进程切换次数更新

context_switch(rq, prev, next);
/* unlocks the rq *///进程上下文的切换
/*
* The context switch have flipped the stack from under us
* and restored the local variables which were saved when
* this task called schedule() in the past. prev == current
* is still correct, but it can be moved to another cpu/rq.
*/
cpu
= smp_processor_id();
rq
= cpu_rq(cpu);
}
else//如果是同一个进程不需要切换
raw_spin_unlock_irq(&rq->lock);

static inline void
context_switch(
struct rq *rq, struct task_struct *prev,
struct task_struct *next)
{
struct mm_struct *mm, *oldmm;//初始化进程地址管理结构体mm和oldmm
prepare_task_switch(rq, prev, next);//完成进程切换的准备工作
mm = next->mm;
oldmm
= prev->active_mm;
/*完成mm_struct的切换*/
if (!mm) {
next
->active_mm = oldmm;
atomic_inc(
&oldmm->mm_count);
enter_lazy_tlb(oldmm, next);
}
else
switch_mm(oldmm, mm, next);
if (!prev->mm) {
prev
->active_mm = NULL;
rq
->prev_mm = oldmm;
}
switch_to(prev, next, prev);
//进程切换的核心代码
barrier();
finish_task_switch(this_rq(), prev);
}

 

5.我们看到在context_switch中使用switch_to(prev,next,prev)来切换进程。我们查看一下switch_to的代码。 switch_to是一个宏定义,完成进程从prev到next的切换,首先保存flags,然后保存当前进程的ebp,然后把当前进程的esp保存到prev->thread.sp中,然后把标号1:的地址保存到prev->thread.ip中。 然后把next->thread.ip压入堆栈。这里,如果之前B也被switch_to出去过,那么next->thread.ip里存的就是下面这个1f的标号,但如果next进程刚刚被创建,之前没有被switch_to出去过,那么next->thread.ip里存的将是ret_ftom_fork __switch_canqry应该是现代操作系统防止栈溢出攻击的金丝雀技术。 jmp __switch_to使用regparm call, 参数不是压入堆栈,而是使用寄存器传值,来调用__switch_to eax存放prev,edx存放next。这里为什么不用call __switch_to而用jmp,因为call会导致自动把下面这句话的地址(也就是1:)压栈,然后__switch_to()就必然只能re到这里,而无法根据需要ret到ret_from_fork 当一个进程再次被调度时,会从1:开始执行,把ebp弹出,然后把flags弹出。

 

#define switch_to(prev, next, last) \
do { \
/* \
* Context-switching clobbers all registers, so we clobber \
* them explicitly, via unused output variables. \
* (EAX and EBP is not listed because EBP is saved/restored \
* explicitly for wchan access and EAX is the return value of \
* __switch_to()) \
*/ \
unsigned
long ebx, ecx, edx, esi, edi; \
\
asm
volatile("pushfl\n\t" /* save flags */ \
"pushl %%ebp\n\t" /* save EBP */ \
"movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
"movl %[next_sp],%%esp\n\t" /* restore ESP */ \
"movl $1f,%[prev_ip]\n\t" /* save EIP */ \
"pushl %[next_ip]\n\t" /* restore EIP */ \
__switch_canary \
"jmp __switch_to\n" /* regparm call */ \
"1:\t" \
"popl %%ebp\n\t" /* restore EBP */ \
"popfl\n" /* restore flags */ \
\
/* output parameters */ \
: [prev_sp]
"=m" (prev->thread.sp), \
[prev_ip]
"=m" (prev->thread.ip), \
"=a" (last), \
\
/* clobbered output registers: */ \
"=b" (ebx), "=c" (ecx), "=d" (edx), \
"=S" (esi), "=D" (edi) \
\
__switch_canary_oparam \
\
/* input parameters: */ \
: [next_sp]
"m" (next->thread.sp), \
[next_ip]
"m" (next->thread.ip), \
\
/* regparm parameters for __switch_to(): */ \
[prev]
"a" (prev), \
[next]
"d" (next) \
\
__switch_canary_iparam \
\
:
/* reloaded segment registers */ \
"memory"); \
}
while (0)

5.开始抢占

sched_preempt_enable_no_resched();
if (need_resched())
goto need_resched;

参考文献:https://www.cnblogs.com/xiaomanon/p/4195327.html 

http://dict.youdao.com/w/%E8%BF%9B%E7%A8%8B%E6%A0%87%E8%AF%86%E7%AC%A6/ 

 

 

 

转:https://www.cnblogs.com/1014221651a/p/9008427.html


推荐阅读
  • 在 Red Hat 系统的启动过程中,首先运行的程序是 `/sbin/init`。该程序会读取 `/etc/inittab` 文件,并根据其中的配置进行系统的初始化工作。例如,它会根据设定的运行级别启动相应的服务和进程,确保系统能够顺利进入指定的运行状态。此外,`/sbin/init` 还负责管理系统关机和重启等操作,确保系统的稳定性和安全性。 ... [详细]
  • 内网渗透技术详解:PTH、PTT与PTK在域控环境中的应用及猫盘内网穿透配置
    本文深入探讨了内网渗透技术,特别是PTH、PTT与PTK在域控环境中的应用,并详细介绍了猫盘内网穿透的配置方法。通过这些技术,安全研究人员可以更有效地进行内网渗透测试,解决常见的渗透测试难题。此外,文章还提供了实用的配置示例和操作步骤,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 在Linux系统中Nginx环境下SSL证书的安装步骤与WordPress CDN的高级配置指南
    在Linux系统中,Nginx环境下安装SSL证书的具体步骤及WordPress CDN的高级配置指南。首先,安装SSL证书需要准备两个关键配置文件,并建议在操作前备份相关服务器配置文件,以确保数据安全。随后,本文将详细介绍如何在Nginx中正确配置SSL证书,以及如何优化WordPress的CDN设置,提升网站性能和安全性。 ... [详细]
  • 算术表达式分析与解析技术初探
    本文初步探讨了算术表达式的分析与解析技术,针对作者在职业转型过程中发现自身算法基础薄弱的问题,决定在接下来的三个月内,系统地学习和掌握常用数据结构与算法,以提升个人技术能力。研究内容不仅涵盖了基本的算术表达式解析方法,还深入讨论了其在实际应用中的优化策略,为相关领域的进一步研究奠定了基础。 ... [详细]
  • 如何在Linux系统上部署MySQL 5.7.28
    本文详细介绍了在Linux系统上部署MySQL 5.7.28的具体步骤。通过官方下载页面获取最新安装包后,按照提供的指南进行配置和安装。文章内容实用性强,适合初学者和有经验的管理员参考。 ... [详细]
  • 在Linux/WSL环境中,本文对Shell任务的并行处理进行了详细的测试与分析。通过多种并行处理技术,如GNU Parallel和xargs,探讨了如何有效提升任务执行效率和系统资源利用率。实验结果表明,合理配置并行参数能够显著缩短任务完成时间,提高系统整体性能。此外,文章还介绍了Shell脚本编写的基本原则和最佳实践,为读者提供了实用的参考。 ... [详细]
  • 在《算法竞赛高阶指导》第三章中,详细探讨了差分技术的原理及其在实际问题中的应用。本章节通过具体示例,如通过区间操作使序列中的所有元素相等的问题,深入解析了如何利用差分技术高效解决此类问题,并讨论了不同操作方式下的最优解法及其复杂度分析。题目链接:https://www.acwing.com/problem/content/102。该章节不仅提供了理论上的指导,还结合了丰富的实例,帮助读者更好地理解和掌握差分技术的应用技巧。 ... [详细]
  • 在《物联网内核与驱动开发基础课程》第五讲中,详细解析了平台总线技术的应用与开发。本讲内容不仅涵盖了平台总线的基本概念,还深入探讨了其在嵌入式系统中的实现方法及优化策略,为开发者提供了宝贵的实践指导。通过具体案例分析,帮助学员更好地理解平台总线在设备管理、资源分配等方面的关键作用。 ... [详细]
  • 在使用Eclipse IDE进行Java开发时,遇到了一个新编写的函数变量无法在调试模式下正确显示的问题。具体表现为,尽管函数内部已插入新的代码段,但这些新变量并未出现在“变量”视图中,仅能显示作为参数传递的变量。经过初步排查,发现这可能是由于IDE配置不当或项目构建路径设置错误所致。本文将详细探讨可能的原因,并提供相应的解决方案,以帮助开发者有效解决这一问题。 ... [详细]
  • H3C防火墙自动构建安全隧道
    实验拓扑结构:两端采用静态IP地址配置。H3C防火墙能够自动构建IPSec安全隧道,确保数据传输的安全性。通过配置防火墙的非信任区域,实现自动化安全连接的建立与维护,有效提升网络防护能力。 ... [详细]
  • 在Eclipse环境中部署Spring框架源码的过程中,本文详细探讨了Junit加载ApplicationContext的具体实现方法。通过使用`ClassPathXmlApplicationContext`类,可以有效地初始化Spring容器,从而便于单元测试的执行。此外,文章还深入分析了Spring框架的核心机制,包括依赖注入和AOP等方面的原理,为开发者提供了宝贵的实战经验和技术指导。 ... [详细]
  • 如何合理配置服务器系统盘容量?云服务器系统盘大小应如何选择?
    在选择云服务器系统盘容量时,需综合考虑操作系统、应用程序及未来扩展需求。对于已配置好的Linux云服务器,建议通过创建系统盘镜像来保存当前设置,以便于后续的快速部署与恢复。此外,合理的系统盘容量规划还能有效提升数据管理效率,减少不必要的存储成本。 ... [详细]
  • 利用命令行配置 ASP.NET Core 发布后的监听地址与环境变量设置
    通过命令行配置 ASP.NET Core 应用程序的发布设置,可以灵活地调整监听地址和环境变量。本文介绍如何在新建的 ASP.NET Core 项目中,通过修改 `Program.cs` 文件中的代码来实现这一功能。具体步骤包括在 `Program` 类的 `Main` 方法中添加相应的配置代码,以确保应用程序在不同环境中能够正确运行。此外,还将详细介绍如何使用命令行工具来设置和验证这些配置项,从而提高开发和部署的效率。 ... [详细]
  • 浏览器与服务器在网站访问过程中的数据交互分析
    本文分析了浏览器与服务器在网站访问过程中基于HTTP协议的数据交互机制。HTTP协议具有轻量级和高效通信的特点,主要通过GET、HEAD和POST方法进行数据传输。其“请求-响应”模式确保了数据交互的有序性和可靠性,同时支持多种数据格式和内容类型,为现代Web应用提供了坚实的基础。 ... [详细]
  • WinForms应用程序中的高效双缓冲技术优化方法
    在探讨WinForms应用程序中高效的双缓冲技术优化方法时,网络上的资料往往杂乱无章,缺乏清晰的解释。本文总结了多种优化方案,包括但不限于:第一种方案,通过设置控件的DoubleBuffered属性来减少屏幕闪烁;第二种方案,自定义绘图方法以提高性能;第三种方案,利用重载WndProc方法拦截绘制消息。此外,还结合实际代码示例,详细解析了每种方案的实现原理和应用场景,帮助开发者更好地理解和应用双缓冲技术。 ... [详细]
author-avatar
李慧颖yynd_613
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有