六、高级进程管理

来源:互联网 发布:数据库系统教程施伯乐 编辑:程序博客网 时间:2024/05/19 03:16
进程调度简称调度器,是把有限的处理器资源分配给进程的内核子系统。在作出决策的过程中,调度器既要最大化处理器效率,又要让多个进程同时运行、互不影响。

就绪进程:该进程是非阻塞的。进行用户交互、大量读写文件、响应I/O和网路事件的进程会花费大量时间来等待资源可用,在相当长的事件内无法转换为就绪状态。
多任务操作系统分为两大类:协同式和抢占式。Linux实现了后一种形式的多任务。

时间片

Linux分配给进程的运行时间。时间片的长短对系统行为和性能非常重要。一些系统给予进程长时间片,希望最大化系统吞吐率和整体表现。另一些系统给予较短的时间片,希望获得优秀的交互性能。Linux通过动态分配进程时间片,期望在两方面都能做到最好

I/O约束进程 Vs. 处理器约束进程

持续消耗所有可用的时间片的进程称为“处理器约束进程”。多数时间处于等待资源的阻塞状态的进程称为“I/O约束进程”;I/O约束进程经常发起和等待文件I/O,阻塞在键盘输入,或者用户移动鼠标等。
两类进程得益于调度器对于不同程序类型所采用的不同行为。处理器可能得到尽可能多的时间片从而最大化缓存命中率,尽可能完成任务。而I/O约束进程不需要长时间片,因为它们一般在发出I/O请求和阻塞在内核资源前只会运行很短一段时间。

抢占调度

当一个时间片耗光的时候,调度器会中止其运行,开始运行一个新的进程。如果没有其他的就绪进程,内核会给予所有耗光时间片的进程新的时间片,继续运行。低优先级进程必须等待高优先级进程耗光时间片或者阻塞。
没有就绪进程,那么会运行空闲进程。空闲进程既不是一个进程,也不能实际运行。空闲进程是为了简化调度算法和统计方便而生的特殊例程。
系统中运行的进程一定是最高优先级的可运行进程。

线程

线程是进程中的运行单元,所有进程都至少有一个线程。每个线程都独占一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。但是共享同一地址空间(同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。

让出处理器

虽然Linux是一个抢占式多任务操作系统,但是它也提供了一个系统调用来允许进程主动让出处理器,并通知调度器选择另一个进程来运行。

#include <sched.h>int sched_yiedld(void);

中断当前进程,运行一个新的进程,就和内核主动抢占一样。当没有其他就绪进程,让出的进程会直接恢复运行。
一般来说,Unix程序倾向于使用建立在一个可阻塞文件描述符基础上的事件驱动机制。有一种需要使用sche_yield():用户空间线程锁。让一个线程试图请求另一个线程已拥有的锁的时候,该线程会让出处理器直到锁可用。

让出处理器方法的过去和现状

在早期调用sched_yeild()如果就绪队列有其他进程就运行该进程,并把当前进程放到就绪队列的队尾。
在2.6内核做了调整
(1)进程是实时进程吗?如果是,放到就绪队列的队尾。如果不是,到下一步。
(2)把该进程从就绪队列移出,放到到期进程队列中。也就是说,只有当所有就绪进程运行,耗光时间片后,该进程才能和所有的到期进程一道重新进入就绪
(3)调度下一个就绪进程运行。

进程优先级

历史上,Unix把这个优先级称为“nice values”,因为低优先级的进程往往允许其他进程分享更多的处理器时间。
“nice values”是在进程运行的时候指定的,Linux调度器基于这样的原则来调度:高优先级的程序总是先运行。同时nice值也指明了进程的时间片长度。
合法的优先级在-20到19之间,默认为0。nice值越低优先级越高,时间片越长。增加一个进程的nice值意味着该进程对系统更友好。

nice()

#include <unistd.h>int nice(int inc);

成功调用将在现有的优先级上增加inc,并返回新值。只有root所有进程才能使用负值inc减少友好度,增加优先级。非root进程只能降低优先级。
遇到错误nice返回-1但也有可能是成功的返回值,所以用errno来区别。

int ret;errno = 0;ret = nice(10);if(ret == -1 && errno != 0)    perror("nice");else    printf("nice value is now %d\n", ret);

nice只有一种错误号EPERM,表面进程没有CAP_SYS_NICE能力。

更好的系统调用

#include <sys/time.h>#include <sys/resource.h>int getpriority(int which, int who);int setpriority(int which, int who, int prio);

which和who指定进程、进程组或者用户。其中which取值为PRIO_PROCESS、PRIO_PGRP、PRIO_USER。who说明了进程ID,进程组ID或者用户ID。当“who”是0时候,分别表示当前进程,当前进程组或者当前用户。
getpriority返回指定进程中的最高优先级,set将所有进程的优先级设为prio。

//获取当前进程优先级int ret;ret = getpriority(PRIO_PROCESS, 0);printf("nice value is %d\n", ret);//设置当前进程组所有进程优先级int ret;ret = setpriority(PRIO_PGRP, 10);if(ret == -1)    perror("setpriority");

I/O优先级

int ioprio_get(int which, int who);int ioprio_set(int which, int who, int ioprio);

处理器亲和度

在多处理机中,调度器必须充分利用系统的处理器,尽量避免处理器空闲。然而,如果一个进程曾在某一CPU上运行,进程调度器还应该尽量把它放在同一CPU上,因为处理器间的进程迁移为带来性能损失。
处理器亲和度表明一个进程停留在同一处理器上的可能性。软亲和度表面调度器持续调度进程到同一处理器上的自然倾向。硬亲和度描述了强制内核保证进程到处理器的绑定。

进程从父进程基础处理器亲和度。Linux提供两个系统调用来获取和设定进程的硬亲和度。

#define _GNU_SOURCE#include <sched.h>typedef struct cpu_set_t;size_t CPU_SETSIZE; //表示set可能表示的处理器数量void CPU_SET(unsigned long cpu, cpu_set_t *set);void CPU_CLR(unsigned long cpu, cpu_set_t *set);int CPU_ISSET(unsigned long cpu, cpu_set_t *set); //检查系统中的处理器是否被绑定到这个进程void CPU_ZERO(cpu_set_t *set); //清空所有二进制位int sched_setaffinity(pid_t pid, size_t setsize, const cpu_set_t *set);int sched_getaffinity(pid_t pid, size_t setsize, const cpu_set_t *set);

sched_getaffinity获得由pid指定的进程的处理器亲和度,存储在cpu_set_t类型中。pid为0则是当前进程的亲和度。setsize参数是cpu_set_t的大小

cpu_set_t set;int ret, i;CPU_ZERO(&set); //clear all CPUsCPU_SET(0, &set);  // allow CPU #0CPU_CLR(1, &set);  // forbid CPU #1ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);if(ret == -1)    perror("sched_setaffinity");for(i = 0, i < CPU_SETSIZE; ++i){    int cpu;    cpu = CPU_ISSET(i, &set);    printf("cpu=%i is %s\n", i, cpu ? "set" : "unset");}

上述调用会出现的错误这里写图片描述

实时系统

如果一个系统受到操作期限–请求和响应之间的最小量和命令次数–的支配,就称该系统是“实时”的。

软硬实时系统

硬实时系统对于操作期限要求非常严格,超过期限就会产生失败;软实时系统却不认为超过期限是一个严重的失败。

延时是指刺激发生直到响应运行的时间。在几次测量之间的偏差就是抖动。

Linux调度策略和优化级

在<sched.h>中的预定义定表示各个策略:分别为SCHED_FIFO、SCHED_RR、SCHED_OTHER。每个进程都有一个与nice值无关的静态优先级(1~99越大优先级越高),Linux调度始终选择最高优先级的进程运行。

FIFO先进先出

没有时间片的非常简单的实时策略。只要没有高优先级进程就绪,FIFO类型进程就会持续运行。
(1)一个就绪的FIFO型进程如果是系统中的最高优先级进程就会一直保持运行。
(2)FIFO型进程持续运行知道阻塞或者调用sched_yield(),或者高优先级进程就绪。
(3)当FIFO型进程阻塞时,调度器将其移除就绪队列。当它恢复时,被插到相同优先级进程的队列的末尾。因此,它必须等待高优先级或同等优先级进程停止运行。
(4)当FIFO型进程调用sched_yield()时,调度器将其放到同等优先级末尾。
(5)FIFO进程被抢占,它在优先级队列中的位置不变。
(6)当一个进程变为FIFO型或者进程静态优先级改变,它将会被放到相应优先级队列的头部。

RR轮转策略

调度器给每个RR进程分配一个时间片,时间片耗完调度器将其放到所在优先级队列的末尾。

普通调度策略。适用于默认的非实时进程。这些进程的静态优先级都为0,因此任意就绪的FIFO和RR进程都会抢占它们。

批调度策略

SCHED_BATCH是批调度或空闲调度的策略。只有系统中没有其他就绪进程时才会运行。

设置调度策略

#include <sched.h>struct sched_param{    /* ... */    int sched_priority;    /* ... */};int sched_getscheduler(pid_t pid);int sched_setscheduler(pid_t pid, int policy, const struct sched_param *sp);

设置调度参数

int sched_getparam(pid_t pid, struct sched_param *sp); //将pid的调度参数存储在sp中int sched_setparam(pid_t pid, const struct sched_param *sp);

函数错误码

这里写图片描述

确定有效优先级的范围

int sched_get_priority_min(int policy);int sched_get_priority_man(int policy);

返回policy关联的优先级最值

min = sched_get_priority_min(SCHED_RR);

获得进程时间片长度

#include <sched.h>struct timespec{    time_t tv_sec;    long tv_nsce;}int sched_rr_get_interval(pid_t pid, struct timespec *tp);

这个函数只能作用于SCHED_RR进程,可以获得时间片长短

资源限制

#include <sys/time.h>#include <sys/resource.h>struct rlimit{    rlimit_t rlimi_cur;    rlimit_t rlimi_max;}int getrlimit(int resource, struct rlimit *rlimi);int setrlimit(int resource, const struct rlimit *rlim);

对资源有两个上限:软限制和硬限制。
限制列表:具体看书P203

原创粉丝点击