Linux操作系统简介

Linux拥有现代操作系统的功能,如真正的抢先式多任务处理,支持多用户内存,保护虚拟内存,支持SMP、UP,符合POSIX 标准联网、图形用户接口和桌面环境具有快速性、稳定性等特点。本文通过分析Linux内核源代码,说明了Linux作为操作系统内核是如何对进程组织,转换,调度的。

进程的组织

进程在内核中是以PCB的形式存在的,一个操作系统有很多进程,即就是很多PCB同时存在,如何有效的管理进程的组织方式,显然是一个很重要的问题,为了遍历内核中所有的进程,内核将这些PCB直接用双向链表链起来,task_struct 结构中的tasks字段就是用来连接所有进程描述符的。在这个链中,链表头是init_task描述符,它是所谓的0进程或者swapper进程的描述符,init_task.tasks.prev字段指向链表中最后插入的进程描述符的tasks字段。

struct task_struct {
struct list_head run_list;
struct list_head tasks;
struct task_struct *real_parent; /*real parent process (when being debugged) */
struct task_struct *parent; /* parent process */
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */
/* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX];
struct list_head thread_group;
};

从以上源代码中的字段可知Linux是如何组织进程的。

字段run_list:
在内核中有一个队列是可运行队列,即就是保存了进程状态为:TASK_RUNNING的进程PCB,这个队列就是run_list字段,内核为每个优先级都设置了一个可运行队列,即如果一个进程的优先级为k(取值范围是0到139),那么内核就将此进程链入到优先权为k的可运行队列中,这样CPU可以从高优先级可可运行队列去选择要运行的进程,这样提高了效率,但为此却要把所有可运行的进程队列拆分成140个不同的队列。这些队列有一个单独的数据结构prio_array_t数据结构 来实现,定义如下(摘自linux2.6.11):

typedef struct prio_array prio_array_t;
struct prio_array {
unsigned int nr_active;
unsigned long bitmap[BITMAP_SIZE];
struct list_head queue[MAX_PRIO];
};

字段 children、 sibling和thread_group:
这三个字段分别是进程的子进程链表、兄弟链表和线程组链表。可以根据children和sibling字段找到一个进程家族的进程。进程在创建后必须处于某一个组中,通过thread_group来形成一个进程组。

进程的转换

Linux中进程切换过程与函数调用堆栈基本类似

void my_schedule(void)
{
tPCB * next;
tPCB * prev; if(my_current_task == NULL
|| my_current_task->next == NULL)
{
return;
}
printk(KERN_NOTICE ">>>my_schedule<<<\n");
/* schedule */
next = my_current_task->next;
prev = my_current_task;
/*进程上下文的切换,与函数调用堆栈类似*/
if(next->state == )/* -1 unrunnable, 0 runnable, >0 stopped */
{
/* switch to next process */
asm volatile(
/*保存当前进程的ebp esp*/
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
/*构建下一个进程的esp*/
"movl %2,%%esp\n\t" /* restore esp */
/*$1f是指下面标号为1的位置,就是下一个进程启动的位置*/
"movl $1f,%1\n\t" /* save eip */
/*下一个进程eip压栈*/
"pushl %3\n\t"
/*恢复现场:eip指向下下个进程的地址ebp恢复为第一条指令压栈保存的ebp*/
"ret\n\t" /* restore eip */
"1:\t" /* next process start here */
"popl %%ebp\n\t"
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
}
else
{
next->state = ;//新的进程设置为正在运行状态
my_current_task = next;
printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
/* switch to new process */
asm volatile(
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
/*新的进程从来没有执行过,所以栈为空esp与ebp指向同一位置*/
"movl %2,%%esp\n\t" /* restore esp */
"movl %2,%%ebp\n\t" /* restore ebp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
return;
}

切换过程:

1.保存当前进程的堆栈环境(esp,eip,ebp),首先将当前进程的ebp压栈,并将esp和eip保存在当前进程的结构体中

2.构建下一进程的堆栈环境。然后下一条进程的thread.sp读出,赋给esp;thread.ip读出赋给eip。若下一条进程是新的进程,处于未运行状态,则新进程堆栈为空,esp与ebp指向同一地址,这种情况与函数调用堆栈更加类似。若下一条进程处于运行态,那么ebp与当前进程的ebp指向同一地址。

3.恢复堆栈环境,为下下个进程做准备。恢复eip,即把执行完成的进程的thread.ip(下一进程的指令地址)赋给eip,上例中所有进程的thread.ip设为my_process。

如图所示:

进程的调度

Linux系统进程提供了两种优先级,一种是普通的进程优先级,第二个是实时优先级。前者适用SCHED_NORMAL调度策略,后者可选SCHED_FIFO或SCHED_RR调度策略。任何时候,实时进程的优先级都高于普通进程,实时进程只会被更高级的实时进程抢占,同级实时进程之间是按照FIFO(一次机会做完)或者RR(多次轮转)规则调度的。

Linux进程状态机:

个人看法

在日常使用手机或者电脑的时候,经常在将应用关闭或退出后,仍然还有许多进程在后台运行,促使我对进程的存在产生了好奇心。现在明白,不仅仅在应用运行时会产生进程,计算机系统本身在运行过程中也会产生许多进程,就是这些进程使得计算机可以调用许多功能,与人进行交互。而通过对Linux操作系统进程的探索,使我对进程有了更进一步的了解,懂得了为什么计算机可以同时运行那么多程序,好的进程的组织,转换和调用模式可以极大地提升计算机的性能,帮助我们更好地完成任务。

Linux操作系统进程模型分析进程的更多相关文章

  1. linux RTC 驱动模型分析【转】

    转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...

  2. Linux RTC驱动模型分析之rtc-sysfs.c【转】

    转自:https://blog.csdn.net/longwang155069/article/details/52353408 版权声明:本文为博主原创文章,未经博主允许不得转载. https:// ...

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

    1.Linux操作系统的简易介绍 Linux系统一般有4个主要部分:内核.shell.文件系统和应用程序.内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使 ...

  4. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  5. Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  6. 《Linux内核--分析Linux内核创建一个新进程的过程 》 20135311傅冬菁

    20135311傅冬菁 分析Linux内核创建一个新进程的过程 一.学习内容 进程控制块——PCB  task_struct数据结构 PCB task_struct中包含: 进程状态.进程打开的文件. ...

  7. Linux内核分析-分析Linux内核创建一个新进程的过程

    作者:江军 ID:fuchen1994 实验题目:分析Linux内核创建一个新进程的过程 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/li ...

  8. 20135239 益西拉姆 linux内核分析 进程的切换和系统的一般执行过程

    week 8 进程的切换和系统的一般执行过程 [ 20135239 原文请转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

  9. linux内核分析作业6:分析Linux内核创建一个新进程的过程

    task_struct结构: struct task_struct {   volatile long state;进程状态  void *stack; 堆栈  pid_t pid; 进程标识符  u ...

随机推荐

  1. 彻底弄懂JS的事件冒泡和事件捕获

      先上结论:在事件执行流中有两种执行方式.一种是事件冒泡(即事件的执行顺序是从下往上执行的) ;  另一种是捕获(即事件的执行顺序是从上往下执行的); 阻止事件冒泡:   return false; ...

  2. ACE入门——ACE构建

    ACE(ADAPTIVE Communication Environment),ACE入门的第一课就是要学习怎么在自己的系统上构建ACE. ACE是跨平台的,这是它的一个很重要的特性,ACE支持很多的 ...

  3. Python selenium 三种等待方式详解

    1. 强制等待第一种也是最简单粗暴的一种办法就是强制等待sleep(xx),强制让闪电侠等xx时间,不管凹凸曼能不能跟上速度,还是已经提前到了,都必须等xx时间.看代码: # -*- coding: ...

  4. JVMGC机制

    GC 是JVM的垃圾回收器.与C/C++不同,java程序员无需考虑太多内存分配的位置,更不用考虑内存释放的机制,java对象内存的申请和释放都有JVM托管.JVM的内存释放机制就是GC. GC的过程 ...

  5. 算法题丨Next Permutation

    描述 Implement next permutation, which rearranges numbers into the lexicographically next greater perm ...

  6. 学习HTML的第三次课

    浏览器的地址栏中有字数限制,大约为200个字符. 1.表单:<form action="" method=""></form> 属性: ...

  7. C#在使用Assembly加载程序集时失败

    错误现象: 进行插件读取时出现错误:"尝试从一个网络位置加载程序集,在早期版本的 .NET Framework 中,这会导致对该程序集进行沙盒处理.此发行版的 .NET Framework ...

  8. [LeetCode] Print Binary Tree 打印二叉树

    Print a binary tree in an m*n 2D string array following these rules: The row number m should be equa ...

  9. react源代码重点难点分析

    网上已经有不少react源码分析文档,但都是分析主流程和主要功能函数,没有一个是从reactDOM.render()入口开始分析源码把流程走通尤其是把复杂重要的细节环节走通直到把组件template编 ...

  10. [SDOI2008]Cave 洞穴勘测

    题目描述 辉辉热衷于洞穴勘测. 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假 ...