一.什么是进程
计算机上有许多可以运行的软件,其中也包括操作系统,这些软件运行时,就产生了一个或多个进程。
二.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