Advanced Process Management 高级进程

2019-08-16  本文已影响0人  无无吴

Process Scheduling 进程调度

进程调度器(或者简单地说是调度程序)是内核的组件,它选择接下来要运行的进程。在决定哪些进程可以运行,什么时候运行时,调度器负责 支持最大限度地利用处理器,同时提供多个进程并行和无缝执行的错觉。
多任务操作系统有两种变体:协作式cooperative和先发制人式preemptive。
进程在调度程序抢占之前允许运行的时间长度,称为进程的时间间隔timeslice。

I/O - Versus Processor - Bound Processes

持续消耗所有可用时间的进程被认为是processor - bound。
最简单的例子是无限循环:

//100% processor-bound
while(1)
  ;

其他较不极端的例子包括科学计算、数学计算和图像处理。
与执行相比,花费更多时间阻塞等待某些资源的进程被认为是I/O - bound。
许多GUI应用程序,它们花费大量时间等待用户输入。
processor-bound的应用程序需要最大的时间点,允许它们最大限度地缓存hit rate(通过时间局部性),并尽可能快地完成他们的作业。

I/O-bound进程不一定需要大量时间,因为在发出I/O请求和阻塞某些内核资源之前,它们通常只运行很短的时间。
应用程序可以在阻塞和调度更多I/O请求之后重新启动的更快,那么就可以更好的是利用系统的硬件。此外,如果应用程序正在等待用户输入, 时间安排得越快,用户对无缝执行的感知就越强。

Preemptive Scheduling 抢先调度

在传统的UNIX进程调度中,所有可运行的进程都被分配了一个时间片。当一个进程耗尽它的timeslice时,内核将暂停它,并开始运行一个不同的进程。
系统上有更高优先级的进程-低优先级进程只能等待高优先级进程耗尽它们的时间或阻塞。
这种行为制定了Unix调度的一个重要但默契的规则:所有进程都必须向前推进。

The Completely Fair Scheduler

完全公平调度器(CFS)与传统的Unix进程调度程序有很大的不同。
在大多数Unix系统中,包括CFS推出之前的Linux,进程调度中有两个基本的进程变量:优先级和时间。
CFS引入了一种完全不同的算法,称为公平调度fair shecduling,它消除了时间片作为分配给处理器的访问权限的单元。
CFS为每个进程分配处理器时间的一部分,而不是时间间隔:

  1. CFS首先给N个进程分配1/N的处理器时间。
  2. CFS然后通过用每个进程的nice value加权每个进程的占用处理器时间的比重。nice value为零的进程的权重为1,因此它们的比例不变。具有较小的nice value(高优先级)的进程接收较大的权重,增加了占用处理器的时间比重,而具有较大的nice value(较低优先级)的进程则减少了占用处理器的时间比重。
  3. 为了确定每个进程运行的实际时间长度,CFS需要将比重划分成一个固定时间。这个时间被称为target latency,表示系统调用的latency。
    CFS引入了第二个关键变量,最小粒度the minimum granularity。
    The minimum granularity是任何进程运行的时间长度的最小单元。

Yielding the Processor

虽然linux是一个先发制人的多任务操作系统,但它也提供了一个系统调用,允许进程显式地产生执行并指示调度程序选择一个新进程执行。

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

对schedyfield()的调用导致当前正在运行的进程暂停,之后进程调度器选择要运行的新进程,

if (sched_yield ())
perror ("sched_yield");

Legitimate Uses

实际上,在适当的先发制人多任务处理系统(如Linux)上,sched_yield()很少有合法的使用。内核完全有能力自己处理这个调度,并且性能更好。
那么为什么POSIX要由这么以分系统调用呢?
答案在于应用程序必须等待外部事件,这些事件可能是由用户、硬件组件或其他进程引起的。

/* the consumer... */
do {
        while (producer_not_ready ()){
              sched_yield ();
          }
          process_data ();
} while (!time_to_quit ());

值得庆幸的是,UNIX程序员不倾向于编写这样的代码。UNIX程序通常事件驱动,并倾向于在消费者之间使用某种可阻止的机制(如管道)。 和代替SCHED_LEVENT()的生产者。

Process Priorities

nice value 的范围是[-20, 19] 默认值是0。
nice value 的值越小, 优先级越高,时间片越大。
nice value 的值越大, 优先级越小,时间片越小。
Linux提供了几个系统调用,用于检索和设置进程的nice value。
最简单的是nice():

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

对nice()的成功调用将由inc递增进程的nice value,并返回新更新的值。
也就是如果inc为正,那么就是在降低优先级,r如果inc为负,则是在增加优先级。
错位的时候返回-1。因为-1也是nice的一个成功调用,所以要检测错误的话就得像下面这么用。

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

传值0给nice()是一个很简单的获取当前进程nice value的方法。

printf("nice value is currently %d\n", nice(0));
int ret, val;
/*get current nice value*/
val = nice (0)

/*we want a nice value of 10*/
val = 10 - val;
errno = 0;
rer = nice(val);
if(ret == -1 && errno != 0)
    perror("nice")
else
    printf("nice value is now %d\n", ret)

getpriority() and setpriority()

最好好的解决方案是使用getpriority()和setpriority()系统调用,它允许更多的控制,但在操作上更复杂:

#include <sys/time.h>
#include <sys/resource.h>

int getpriority(int which, int who);
int setpriority(int which, int who, int prio);

参数which有如下参数选项:

  1. PRIO_PROCESS
  2. PRIO_PGRP
  3. PRIO_USER
    参数who有就是对相应上面which参数的进程ID, 进程组ID, 用户ID。
    who是0的时候,就表示处理的是当前的进程ID,进程组ID,或用户ID
    参数prio通nice()的参数。错误的处理方式也同nice。

Processor Affinity

一旦进程被调度在一个CPU上,进程调度程序应该在将来将它调度在同一个CPU上。这是有益的,因为将进程从一个处理器迁移到另一个处理器是有代价的。

有时,用户或应用程序希望实施进程到处理器的绑定。这通常是因为该过程是强缓存敏感的,并且希望保留在同一处理器上。
将进程连接到特定处理器并让内核强制执行关系称为设置hard affinity。

sched_getaffinity() and sched_setaffinity()

Linux提供了两个系统调用,用于检索和设置进程的hard affinity:

#include _GNU_SOURCE
#include <sched.h>
typedef struct cpu_set_t;
size_t CPU_SETSIZE;
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,
      cpu_set_t *set);

如果pid为0时,调用将检索当前进程的affinity。

cpu_set_t set;
int ret , i;
CPU_ZERO(&set);
ret = sched_getaffinity(0, sizeof(cpu_set_t), &set);
if(ret == -1)
     perror("sched_getaffinity");
for(int i = 0; i < CPU_SETSIZE; ++i){
      int cpu;
      cpu = CPU_ISSET(i, &set);
      printf("cpu=%i is %s\n, i , cpu ? "set" : "unset");
}

我们使用CPU_ISSET检查系统中的给定处理器 i 是否绑定到此进程。

测试结果

我们只关注CPU#0,#1, #2, #3,因为它们是这个系统上唯一的物理处理器。也许我们希望确保我们的进程只在CPU#0上运行,而不是在其他三个处理器。


int processBindCpu() {
    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(0, &set);
    CPU_CLR(1, &set);
    CPU_CLR(2, &set);
    CPU_CLR(3, &set);
    int ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);
    if(ret == -1)
        perror("sched_setaffinity");
    for(int i = 0; i < CPU_SETSIZE; ++i){
        int cpu = CPU_ISSET(i, &set);
        printf("cpu=%i is %s\n", i, cpu?"set":"unset");
    }



    return 0;
}
测试结果

Real-Time Systems

Setting the Linux scheduling policy

#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 getProcessScheduler() {
    int policy;
    policy = sched_getscheduler(0);

    switch (policy){
        case SCHED_OTHER:
            printf("Policy is normal\n");
            break;
        case SCHED_RR:
            printf("Policy is round_robin\n");
            break;
        case SCHED_FIFO:
            printf("Policy is first-in, first-out\n");
            break;
        case -1:
            perror("sched_getscheduler");
            break;
        default:
            fprintf(stderr, "Unknown policy!\n");
    }

    return 0;
}
测试结果
//怎么设置进程的调度方式
struct sched_param sp = { .sched_priority = 1 };
int ret;
ret = sched_setscheduler (0, SCHED_RR, &sp);
if (ret == −1) {
perror ("sched_setscheduler");
return 1; }

Setting Scheduling Parameters

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

struct sched_param sp;
int ret;
ret = sched_getparam (0, &sp);
if (ret == −1) {
        perror ("sched_getparam");
        return 1; }
printf ("Our priority is %d\n", sp.sched_priority);

struct sched_param sp;
int ret;
sp.sched_priority = 1;
ret = sched_setparam (0, &sp);
if (ret == −1) {
        perror ("sched_setparam");
return 1; }

Determining the range of valid priority

每个进程都具有静态优先级,与nice value无关。对于正常的应用程序,这个优先级总是0。对于实时过程,它的范围从1到99。
linux调度程序总是选择要运行的最高优先级进程(即具有最大数值静态优先级值的进程)。

#include <sched.h>
int sched_get_priority_min (int policy);
int sched_get_priority_max (int policy);
int min, max;
min = sched_get_priority_min (SCHED_RR);
if (min == −1) {
      perror ("sched_get_priority_min");
      return 1; 
}
max = sched_get_priority_max (SCHED_RR);
if (max == −1) {
      perror ("sched_get_priority_max");
      return 1;
 }
printf ("SCHED_RR priority range is %d - %d\n", min, max);
/*
 * set_highest_priority - set the associated pid's scheduling
 * priority to the highest value allowed by its current
 * scheduling policy. If pid is zero, sets the current
 * process's priority.
 *
 * Returns zero on success.
 */
int set_highest_priority (pid_t pid) {
      struct sched_param sp;
      int policy, max, ret;
      policy = sched_getscheduler (pid);
      if (policy == −1)
          return −1;
      max = sched_get_priority_max (policy);
      if (max == −1)
          return −1;
      memset (&sp, 0, sizeof (struct sched_param));
      sp.sched_priority = max;
      ret = sched_setparam (pid, &sp);
      return ret;
 }
#include <sched.h>
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
int sched_rr_get_interval (pid_t pid, struct timespec *tp);
struct timespec tp;
int ret;
/* get the current task's timeslice length */
ret = sched_rr_get_interval (0, &tp);
if (ret == −1) {
    perror ("sched_rr_get_interval");
    return 1; 
}
/* convert the seconds and nanoseconds to milliseconds */
printf ("Our time quantum is %.2lf milliseconds\n", (tp.tv_sec * 1000.0f) + (tp.tv_nsec / 1000000.0f));

Resoucre Limits

#include <sys/time.h>
#include <sys/resource.h>
struct rlimit {
rlim_t rlim_cur; /* soft limit */
rlim_t rlim_max; /* hard limit */
};
int getrlimit (int resource, struct rlimit *rlim);
int setrlimit (int resource, const struct rlimit *rlim);

resource参数有如下:

Default soft and hard resource limits
struct rlimit rlim;
int ret;
/* get the limit on core sizes */
ret = getrlimit (RLIMIT_CORE, &rlim);
if (ret == −1) {
    perror ("getrlimit");
    return 1; 
}
printf ("RLIMIT_CORE limits: soft=%ld hard=%ld\n",
rlim.rlim_cur, rlim.rlim_max);
struct rlimit rlim;
int ret;
rlim.rlim_cur = 32 * 1024 * 1024; /* 32 MB */
rlim.rlim_max = RLIM_INFINITY; /* leave it alone */
ret = setrlimit (RLIMIT_CORE, &rlim);
if (ret == −1) {
perror ("setrlimit");
return 1; }
上一篇下一篇

猜你喜欢

热点阅读