1、操做系统是怎么组织进程的html
一、进程的概念
node
在进程模型中,计算机上全部可运行的软件,一般也包括操做系统,被组织成若干顺序进程,简称进程。一个进程应该包含以下内容:linux
(1)程序的代码,既然进程是一个正在运行的程序,天然须要程序的代码;算法
(2)程序的数据;windows
(3)CPU寄存器的值,包括通用寄存器,程序计数器;数组
(4)堆(heap)是用来保存进程运行时动态分配的内存空间;安全
(5)栈(stack)有两个用途,1保存运行的上下文信息。2在函数调用时保存被调用函数的形参或者局部变量;函数
(6)进程所占用的一组系统资源,如打开的文件。性能
二、进程的组织学习
系统为每一个进程维护了一个进程控制块(Process Control Block,PCB),用来保存与该进程有关的各类状态信息。PCB只是基本原理中的说法,对于一个真实的操做系统可能不叫PCB,就像我所要讲的Linux中,PCB叫作任务结构体(task struct)。
struct task_struct { //进程的运行时状态 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ void *stack; atomic_t usage; //进程当前的状态 /* 0x00000002表示进程正在被建立; 0x00000004表示进程正准备退出; 0x00000040 表示此进程被fork出,可是并无执行exec; 0x00000400表示此进程因为其余进程发送相关信号而被杀死 。 */ unsigned int flags; /* per process flags, defined below */ unsigned int ptrace; int on_rq; //表示此进程的运行优先级,prio表示动态优先级,根据static_prio和交互性奖罚算出,static_prio是进程的静态优先级,在进程建立时肯定,范围从-20到19,越小优先级越高。 int prio, static_prio, normal_prio; //进程的运行优先级 unsigned int rt_priority; //list_head结构体 struct list_head tasks; //mm_struct结构体,描述了进程内存的相关状况 struct mm_struct *mm, *active_mm; /* per-thread vma caching */ u32 vmacache_seqnum; struct vm_area_struct *vmacache[VMACACHE_SIZE]; /* task state */ //进程的状态参数 int exit_state; int exit_code, exit_signal; //父进程退出后信号被发送 int pdeath_signal; /* The signal sent when the parent dies */ /* scheduler bits, serialized by scheduler locks */ unsigned sched_reset_on_fork:1; unsigned sched_contributes_to_load:1; unsigned sched_migrated:1; unsigned sched_remote_wakeup:1; unsigned :0; /* force alignment to the next boundary */ /* unserialized, strictly 'current' */ unsigned in_execve:1; /* bit to tell LSMs we're in execve */ unsigned in_iowait:1; struct restart_block restart_block; //进程号 pid_t pid; //进程组号 pid_t tgid; //进程的亲身父亲 struct task_struct __rcu *real_parent; /* real parent process */ //进程的如今的父亲,可能为继父 struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */ //进程的孩子链表 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; struct list_head thread_node; //该进程使用cpu时间的信息,utime是在用户态下执行的时间,stime是在内核态下执行的时间。 cputime_t utime, stime; cputime_t gtime; struct prev_cputime prev_cputime; //启动时间,,只是时间基准不同 u64 start_time; /* monotonic time in nsec */ u64 real_start_time; /* boot based time in nsec */ struct task_cputime cputime_expires; //list_head的CPU时间 struct list_head cpu_timers[3]; //保存进程名字的数组,通常数组大小为15位 char comm[TASK_COMM_LEN]; /* file system info */ //文件系统信息 struct nameidata *nameidata; /* 文件系统信息计数*/ int link_count, total_link_count; /* filesystem information */ //文件系统相关信息结构体 struct fs_struct *fs; /* open file information */ //打开文件信息的结构体 struct files_struct *files; /* namespaces */ struct nsproxy *nsproxy; /* signal handlers */ //信号相关信息的句柄 struct signal_struct *signal; struct sighand_struct *sighand; struct callback_head *task_works; struct audit_context *audit_context; struct seccomp seccomp; /* Thread group tracking */ u32 parent_exec_id; u32 self_exec_id; /* journalling filesystem info */ void *journal_info; /* VM state */ struct reclaim_state *reclaim_state; struct backing_dev_info *backing_dev_info; struct io_context *io_context; unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ /* * time slack values; these are used to round up poll() and * select() etc timeout values. These are in nanoseconds. */ //松弛时间值,用来记录select和poll的超时时间,单位为ns u64 timer_slack_ns; u64 default_timer_slack_ns; /* CPU-specific state of this task */ //该进程在特定CPU下的状态 struct thread_struct thread; };
PID每一个常常都有本身的“身份证号码”,即PID号,PID是重要的系统资源,它是用以区分各个进程的基本依据,可使用ps来查看进程的PID。一个task struct对应一个PID。
2、进程状态如何转换(给出进程状态转换图)
状态 | 描述 |
TASK_RUNNING | 就绪态或者运行态,进程就绪能够运行,可是不必定正在占有CPU,对应进程状态的R |
TASK_INTERRUPTIBLE | 睡眠态,可是进程处于浅度睡眠,能够响应信号,通常是进程主动sleep进入的状态,对应进程状态S |
TASK_UNINTERRUPTIBLE | 睡眠态,深度睡眠,不响应信号,典型场景是进程获取信号量阻塞,对应进程状态D |
TASK_ZOMBIE | 僵尸态,进程已退出或者结束,可是父进程还不知道,没有回收时的状态,对应进程状态Z |
TASK_STOPED | 中止,调试状态,对应进程状态T |
(1)两状态进程模型
在该模型中,一个进程要么正在执行,要么没有在执行,没有其余状态,因此进程所处的状态有两种:运行态、未运行态。进程状态的转换方式如1.1图所示。
图1.1 两状态转换图
(2)三状态进程模型
在该模型中,进程所处的状态有三种:运行态、就绪态、和阻塞态。进程状态的转换方式如图1.2所示。
图1.2 三状态转换图
(3)五状态进程模型相较于三状态进程模型而言,在三状态进程模型的基础上另增长了两个状态:新建态和退出态。进程状态的转换方式如图1.3所示。
图1.3 五状态转换图
3、进程是如何调度的
在Linux操做系统中,有实时进程和普通今天之分,这里我想围绕普通进程的调度展开,对o(1)调度算法和CFS调度算法进行分析。
一、o(1)调度算法
在Linux2.6中,o(1)调度被采用,它是对普通进程进行调度的一种调度算法。由于Linux2.6版的调度算法与Linux2.4版相比在性能等方面的改进很是大,且它的时间复杂度为恒定的o(1),故把它称为o(1)调度算法。
renqueue结构体的部分定义以下:
struct runqueue { unsigned long nr_running; task_t *curr; prio_array_t *active,*expired,array[2]; }
上述结构体列举了一些比较重要的字段:
(1)nr_running:就绪进程的数目,等于活动进程和过时进程之和;
(2)curr:指向正在运行的进程;
(3)active:指向表示活动进程集的结构体;
(4)expired:指向表示过时进程集的结构体;
(5)arrays[2]:为active和expired分配静态空间。
调度的核心步骤:
(1)若当前处理器的运行队列上没有任何可运行态进程,那么为了实现多处理器间的负载平衡,则从其余处理器上调一些可运行态进程进来。若调完后当前处理器的运行队列上仍是没有进程可运行,则在处理器上运行idle进程来使处理器处于低功耗模式直到有可运行态进程出现。若运行队列上有可运行态进程则执行步骤(2)
(2)判断active队列是否为空。当active队列为空,即表示全部活动进程都运行完了,这时就要让expired队列的过时进程再次变为活动进程。可是并不须要将expired队列中的进程一个个移进active队列中去,只须要将active和expired的指针指向的地址互换就好了。具体内核实现代码以下:
struct prop_array *array=rq->active; if(array->nr_active!=0) { re->active=rq->expired; rq->expired=array; }
(3)步骤(2)执行成功后active队列中一定存在活动进程,这时经过active中的优先级位图bitmap来选择优先级最高且有可运行态进程的优先级队列,具体如图1.4所示。
图1.4 经过bitmap来选择优先级队列的示意图
(4)最后选择active的bitmap所指向链表中的第一个进程便可。
经过以上4个核心步骤,o(1)调度器基本完成了对于可运行态进程的一次调度过程。
二、CFS调度算法
从Linux2.6开始,考虑到o(1)调度的一些不足以及公平性方面的缺陷,因此改用彻底公平调度(CFS)算法。不一样于o(1)调度,CFS调度基于公平的理念,对进程一视同仁,再也不对交互式进程进行区别,也再也不根据进程的平均睡眠时间来肯定奖励bonus并以此来调整动态优先级。取而代之,CFS经过权重使每一个进程都能过得到公平的运行时间。更为关键的是,CFS调度算法的设计和实现都很简单,且实际性能很是优越。CFS调度器的总体结构如图1.5所示。
图1.5 CFS调度器的总体结构
相对比于o(1)调度,CFS调度没有用运行队列来维护可运行态进程,而是用来红黑树来组织普通进程。红黑树本质上是一颗二叉查找树,它具备如下五个特色:
(1)每一个叶结点都是空结点,而且他们都是黑色的;
(2)根结点是黑色的;
(3)红色结点的子结点一定是黑色的;
(4)对于任意结点而言,其到叶结点的每条路径上的黑色结点的书目都相同;
(5)每一个结点不是黑色就是红色;
这些特色决定了红黑树是自平衡的,虽然红黑树没有达到恒定o(1)的时间复杂度可是它最差的时间复杂度也为o(logn),这就决定了它能够在插入和删除等操做上表现的很是高效。CFS使用的红黑树是以时间为顺序的,它的结点由调度实体来描述,而进程的虚拟运行时间和权重也存放在这个结构中,图1.6描绘了CFS中红黑树的结果。
图1.6 CFS红黑树的结构
内核经过红黑树来对虚拟运行时间进行排序,红黑树的最左侧结点的虚拟运行时间最少,因此该结点所表示的进程将是下一个要被调度的进程。
三、两种算法的比较
经过上面对于o(1)调度和CFS调度的分析,咱们发现他们的区别归根结底在于二者的设计思想的不一样。
o(1)调度是经过优先级来实现对时间片的绝对映射,而CFS调度对于时间片的映射则是经过权重完成的,CFS调度相比o(1)调度的最主要优点在于它实现了进程间的调度的相对公平,而这也是调度算法设计中很是重要的一个部分。虽然o(1)调度的恒定的时间复杂o(1)也是一大特点,只是CFS的o(logn)的性能已经可以知足于系统的须要,并且CFS的设计与实现简单,实际运行高效,不像o(1)调度有那么多的复杂公式,弥补了o(1)调度的许多不足之处。因此,这些因素最终决定了CFS调度要取代o(1)调度的位置。可是也不能彻底否认o(1)调度算法,它对于之后进程调度算法的发展仍是有许多值得借鉴的地方。
四、谈谈本身对Linux操做系统的见解
我想经过和windows系统的比较来谈个人见解。
Linux有如下几个优势:
(1)追求免费的正版,微软公司开发的平台下的软件都是明码标价的,若是不购买,那么就是属于盗版,为了追求正版,因此只能选择linux,特别是对于公司,则是为了防止出现版权纠纷问题;
(2)学习,linux开放源代码,是学习系统和软件开发的平台,也是国外不少研究机构开发系统的盗版源泉;
(3)防毒,有许多网站挂马,给客户安全形成了影响,linux做为一个小众平台,感染病毒的几率比较低,所以为了放心地看大片,因此安装了它;
(4)最后,也是最重要的一点,装逼,linux是一个高大上的名词,使用linux就和高手挂上了勾,特别是compiz等软件,让windows的客户看得目瞪口呆,而后能够高大上地享受你们对高手地崇拜。
windows有如下几个优势:
(1)windows造成了良好地生态圈,有着丰富实用地软件支持,造成了良好地盈利模式,在windows平台下,你的须要能够获得贴心的维护,固然你要付出你的money;
(2)维护成本低,技术文档资料详实,不想linux什么都要靠本身、靠社区;
(3)最重要的是对咱们这些初学者比较友善。
说真的,做为一个计算机菜鸟,若是要我选择的话,我更喜欢windows,不过想成为大佬,仍是应该使用Linux。
五、参考资料
https://wenku.baidu.com/view/2003f071cbaedd3383c4bb4cf7ec4afe04a1b1c8.html?qq-pf-to=pcqq.c2c
https://blog.csdn.net/xxpresent/article/details/71023637