第一次作业:深入分析Linux系统进程
Posted an568m
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一次作业:深入分析Linux系统进程相关的知识,希望对你有一定的参考价值。
一:关于进程:
- 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体
- 进程的生命周期:进程是一个动态的实体,从创建到消亡,是一个进程的整个生命周期。
2.1 进程的创建:进程创建是操作系统执行程序的需要或者用户或进程要求创建一个新的进程。进程创建首先是在进程表中为进程建立一个进程控制块PCB,采用fork()系统调用将复制执行 进程的PCB块,U区和内存图像到新的进程。 主要内容包括:进程创建原语、fork()系统调用的编程举例和UNIX V6的fork()源码分析
2.2 进程撤销:当进程 [1] 完成任务或在执行的过程中发生异常时,系统将调用进程终止原语来终止该进程。根据被终止进程的标识符从PCB集合中查找到该进程的PCB,从中读出该进程的状态,终止该进程的执行;若干该进程还有子孙进程,应该讲其所有子孙进程终止,防止它们成为不可控进程;然后回收进程所拥有的资源,最后将被终止进程从所在队列中移出,等待其它程序来收集信息
二:进程的组织:
在操作系统中,系统进程的控制主要是通过一个数据结构,实际上这个结构就是我们在操作系统中所说的PCB(Process Control Block)在Linux中这个数据结构我们叫做task_struct,它实际上至少应该包括以下信息,比如优先级,它的运行状态,他所在的内存空间,它的文件访问权限等等。见下图:
task_struct:
在linux系统中,
所属头文件include\\linux\\sched.h。
每个进程都会被分配一个task_struct结构,它包含了这个进程的所有信息,
在任何时候操作系统都能跟踪这个结构的信息。
这个结构是linux内核汇总最重要的数据结构,下面我们会详细的介绍。
这个结构的主要信息:
1、进程状态 ,将纪录进程在等待,运行,或死锁
2、调度信息, 由哪个调度函数调度,怎样调度等
3、进程的通讯状况
4、因为要插入进程树,必须有联系父子兄弟的指针, 当然是task_struct型
5、时间信息, 比如计算好执行的时间, 以便cpu 分配
6、标号 ,决定改进程归属
7、可以读写打开的一些文件信息
8、 进程上下文和内核上下文
9、处理器上下文
10、内存信息
三:进程的状态
(1)运行态:进程正在使用CPU运行的状态。处于运行态的进程又称为当前进程(current process)。
(2)可运行态:进程已分配到除CPU外所需要的其它资源,等待系统把CPU分配给它之后即可投入运行。
(3)等待态:又称睡眠态,它是进程正在等待某个事件或某个资源时所处的状态。 等待态进一步分为可中断的等待态和不可中断的等待态。处于可中断等待态的进程可以由信号(signal)解除其等待态。处于不可中断等待态的进程,一般是直接或间接等待硬件条件。 它只能用特定的方式来解除,例如使用唤醒函数wake_up()等。
(4)暂停态:进程需要接受某种特殊处理而止运行所处的状态。通常进程在接受到外部进程的某个信号进入暂停态,例如,正在接受调试的进程就处于这种状态。
(5)僵死状态 :
进程虽然已经终止,但由于某种原因,父进程还没有执行wait()系统调用,终止进程的信息也还没有回收。顾名思义,处于该状态的进程就是死进程,这种进程实际上是系统中 的垃圾,必须进行相应处理以释放其占用的资源。
我们在设置这些状态的时候是可以直接用语句进行的比如:p—>state = TASK_RUNNING。同时内核也会使用set_task_state和set_current_state。
进程状态转换图:
四:进程调度
1. 调度的基本概念:在多道程序系统中,进程的数量往往多于处理机的个数,进程争用处理机的情况就在所难免。处理机调度是对处理机进行分配,就是从就绪队列中,按照一定的算法(公平、髙效)选择一个进程并将处理机分配给它运行,以实现进程并发地执行。
2. 调度的层次:一个作业从提交开始直到完成,往往要经历以下三级调度
1) 作业调度。又称高级调度,.其主要任务是按一定的原则从外存上处于后备状态的作业中挑选一个(或多个)作业,给它(们)分配内存、输入/输出设备等必要的资源,并建立相应的进程,以使它(们)获得竞争处理机的权利。简言之,就是内存与辅存之间的调度。对于每个作业只调入一次、调出一次。
多道批处理系统中大多配有作业调度,而其他系统中通常不需要配置作业调度。作业调度的执行频率较低,通常为几分钟一次。
2) 中级调度。又称内存调度。引入中级调度是为了提高内存利用率和系统吞吐量。为此,应使那些暂时不能运行的进程,调至外存等待,把此时的进程状态称为挂起状态。当它们已具备运行条件且内存又稍有空闲时,由中级调度来决定,把外存上的那些已具备运行条件的就绪进程,再重新调入内存,并修改其状态为就绪状态,挂在就绪队列上等待。
3) 进程调度。又称为低级调度,其主要任务是按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它。进程调度是操作系统中最基本的一种调度,在一般操作系统中都必须配置进程调度。进程调度的频率很高,一般几十毫秒一次。
3. 三级调度的联系
作业调度从外存的后备队列中选择一批作业进入内存,为它们建立进程,这些进程被送入就绪队列,进程调度从就绪队列中选出一个进程,并把其状态改为运行状态,把CPU分配给它。中级调度是为了提高内存的利用率,系统将那些暂时不能运行的进程挂起来。当内存空间宽松时,通过中级调度选择具备运行条件的进程,将其唤醒。
1) 作业调度为进程活动做准备,进程调度使进程正常活动起来,中级调度将暂时不能运行的进程挂起,中级调度处于作业调度和进程调度之间。
2) 作业调度次数少,中级调度次数略多,进程调度频率最高。
3) 进程调度是最基本的,不可或缺。
4.调度的方式:
1 ) 非剥夺调度方式,又称非抢占方式。是指当一个进程正在处理机上执行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在执行的进程继续执行,直到该进程完成或发生某种事件而进入阻塞状态时,才把处理机分配给更为重要或紧迫的进程。
在非剥夺调度方式下,一旦把CPU分配给一个进程,那么该进程就会保持CPU直到终止或转换到等待状态。这种方式的优点是实现简单、系统开销小,适用于大多数的批处理系统,但它不能用于分时系统和大多数的实时系统。
2) 剥夺调度方式,又称抢占方式。是指当一个进程正在处理机上执行时,若有某个更为重要或紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给这个更为重要或紧迫的进程。.
釆用剥夺式的调度,对提高系统吞吐率和响应效率都有明显的好处。但“剥夺”不是一种任意性行为,必须遵循一定的原则,主要有:优先权、短进程优先和时间片原则等。
5.调度的基本准则
不同的调度算法具有不同的特性,在选择调度算法时,必须考虑算法所具有的特性。为了比较处理机调度算法的性能,人们提出很多评价准则,下面介绍主要的几种:
1 ) CPU利用率。CPU是计算机系统中最重要和昂贵的资源之一,所以应尽可能使CPU 保持“忙”状态,使这一资源利用率最髙。
2) 系统吞吐量。表示单位时间内CPU完成作业的数量。长作业需要消耗较长的处理机时间,因此会降低系统的吞吐量。而对于短作业,它们所需要消耗的处理机时间较短,因此能提高系统的吞吐量。调度算法和方式的不同,也会对系统的吞吐量产生较大的影响。
3) 周转时间。是指从作业提交到作业完成所经历的时间,包括作业等待、在就绪队列中排队、在处迤机上运行以及进行输入/输出操作所花费时间的总和。
作业的周转时间可用公式表示如下:
周转时间 = 作业完成时间 - 作业提交时间
平均周转时间是指多个作业周转时间的平均值:
平均周转时间 = (作业1的周转时间 + … + 作业 n 的周转时间) / n
带权周转时间是指作业周转时间与作业实际运行时间的比值:
平均带权周转时间是指多个作业带权周转时间的平均值:
平均带权周转时间 = (作业1的带权周转时间 + … + 作业 n 的带权周转时间) / n
4) 等待时间。是指进程处于等处理机状态时间之和,等待时间越长,用户满意度越低。处理机调度算法实际上并不影响作业执行或输入/输出操作的时间,只影响作业在就 绪队列中等待所花的时间。因此,衡量一个调度算法优劣常常只需简单地考察等待时间。
5) 响应时间。是指从用户提交请求到系统首次产生响应所用的时间。在交互式系统中,周转时间不可能是最好的评价准则,一般釆用响应时间作为衡量调度算法的重要准则之一。从用户角度看,调度策略应尽量降低响应时间,使响应时间处在用户能接受的范围之内。
6.进程调度的实现:
调度程序内核函数
1 asmlinkage void schedule(void) 2 { 3 4 struct task_struct *prev, *next, *p; /* prev表示调度之前的进程, next表示调度之后的进程 */ 5 struct list_head *tmp; 6 int this_cpu, c; 7 8 if (!current->active_mm) BUG();/*如果当前进程的的active_mm为空,出错*/ 9 need_resched_back: 10 prev = current; /*让prev成为当前进程 */ 11 this_cpu = prev->processor; 12 13 if (in_interrupt()) {/*如果schedule是在中断服务程序内部执行, 14 就说明发生了错误*/ 15 printk("Scheduling in interrupt/n"); 16 BUG(); 17 } 18 release_kernel_lock(prev, this_cpu); /*释放全局内核锁,并开this_cpu的中断*/ 19 spin_lock_irq(&runqueue_lock); /*锁住运行队列,并且同时关中断*/ 20 if (prev->policy == SCHED_RR) /*将一个时间片用完的SCHED_RR实时 21 goto move_rr_last; 进程放到队列的末尾 */ 22 move_rr_back: 23 switch (prev->state) { /*根据prev的状态做相应的处理*/ 24 case TASK_INTERRUPTIBLE: /*此状态表明该进程可以被信号中断*/ 25 if (signal_pending(prev)) { /*如果该进程有未处理的信号,则让其变为可运行状态*/ 26 prev->state = TASK_RUNNING; 27 break; 28 } 29 default: /*如果为不可中断的等待状态或僵死状态*/ 30 del_from_runqueue(prev); /*从运行队列中删除*/ 31 case TASK_RUNNING:;/*如果为可运行状态,继续处理*/ 32 } 33 prev->need_resched = 0; 34 35 /*下面是调度程序的正文 */ 36 repeat_schedule: /*真正开始选择值得运行的进程*/ 37 next = idle_task(this_cpu); /*缺省选择空闲进程*/ 38 c = -1000; 39 if (prev->state == TASK_RUNNING) 40 goto still_running; 41 still_running_back: 42 list_for_each(tmp, &runqueue_head) { /*遍历运行队列*/ 43 p = list_entry(tmp, struct task_struct, run_list); 44 if (can_schedule(p, this_cpu)) { /*单CPU中,该函数总返回1*/ int weight = goodness(p, this_cpu, prev->active_mm); 45 if (weight > c) 46 c = weight, next = p; 47 } 48 } 49 50 /* 如果c为0,说明运行队列中所有进程的权值都为0,也就是分配给各个进程的 51 时间片都已用完,需重新计算各个进程的时间片 */ 52 if (!c) { 53 struct task_struct *p; 54 spin_unlock_irq(&runqueue_lock);/*锁住运行队列*/ 55 read_lock(&tasklist_lock); /* 锁住进程的双向链表*/ 56 for_each_task(p) /* 对系统中的每个进程*/ 57 p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice); 58 read_unlock(&tasklist_lock); 59 spin_lock_irq(&runqueue_lock); 60 goto repeat_schedule; 61 } 62 63 64 spin_unlock_irq(&runqueue_lock);/*对运行队列解锁,并开中断*/ 65 66 if (prev == next) { /*如果选中的进程就是原来的进程*/ 67 prev->policy &= ~SCHED_YIELD; 68 goto same_process; 69 } 70 71 /* 下面开始进行进程切换*/ 72 kstat.context_swtch++; /*统计上下文切换的次数*/ 73 74 { 75 struct mm_struct *mm = next->mm; 76 struct mm_struct *oldmm = prev->active_mm; 77 if (!mm) { /*如果是内核线程,则借用prev的地址空间*/ 78 if (next->active_mm) BUG(); 79 next->active_mm = oldmm; 80 81 } else { /*如果是一般进程,则切换到next的用户空间*/ 82 if (next->active_mm != mm) BUG(); 83 switch_mm(oldmm, mm, next, this_cpu); 84 } 85 86 if (!prev->mm) { /*如果切换出去的是内核线程*/ 87 prev->active_mm = NULL;/*归还它所借用的地址空间*/ 88 mmdrop(oldmm); /*mm_struct中的共享计数减1*/ 89 } 90 } 91 92 switch_to(prev, next, prev); /*进程的真正切换,即堆栈的切换*/ 93 __schedule_tail(prev); /*置prev->policy的SCHED_YIELD为0 */ 94 95 same_process: 96 reacquire_kernel_lock(current);/*针对SMP*/ 97 if (current->need_resched) /*如果调度标志被置位*/ 98 goto need_resched_back; /*重新开始调度*/ 99 return; 100 } 101
五、对Linux系统进程模型的看法
linux系统进程有很多的优点,但是想要满足所有的用户的需求的不可能的,还需要更多的深入研究,只能说去最大话去提高进程的效率
参考文献 https://blog.csdn.net/sailor_8318/article/details/2452983
https://blog.csdn.net/u013291303/article/details/68954599
https://blog.csdn.net/wodeqingtian1234/article/details/54178770
https://baike.baidu.com/item/进程/382503?fr=aladdin
https://baike.baidu.com/item/进程创建
https://blog.csdn.net/chengonghao/article/details/50981214
以上是关于第一次作业:深入分析Linux系统进程的主要内容,如果未能解决你的问题,请参考以下文章