Advanced Process Management 高級進程管理

Process Scheduling 進程調(diào)度

進程調(diào)度器(或者簡單地說是調(diào)度程序)是內(nèi)核的組件敢辩,它選擇接下來要運行的進程掘托。在決定哪些進程可以運行娜扇,什么時候運行時邑闺,調(diào)度器負責 支持最大限度地利用處理器泪勒,同時提供多個進程并行和無縫執(zhí)行的錯覺昼蛀。
多任務(wù)操作系統(tǒng)有兩種變體:協(xié)作式cooperative和先發(fā)制人式preemptive。
進程在調(diào)度程序搶占之前允許運行的時間長度圆存,稱為進程的時間間隔timeslice叼旋。

I/O - Versus Processor - Bound Processes

持續(xù)消耗所有可用時間的進程被認為是processor - bound。
最簡單的例子是無限循環(huán):

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

其他較不極端的例子包括科學計算沦辙、數(shù)學計算和圖像處理夫植。
與執(zhí)行相比,花費更多時間阻塞等待某些資源的進程被認為是I/O - bound油讯。
許多GUI應(yīng)用程序详民,它們花費大量時間等待用戶輸入。
processor-bound的應(yīng)用程序需要最大的時間點陌兑,允許它們最大限度地緩存hit rate(通過時間局部性)沈跨,并盡可能快地完成他們的作業(yè)。

I/O-bound進程不一定需要大量時間兔综,因為在發(fā)出I/O請求和阻塞某些內(nèi)核資源之前谒出,它們通常只運行很短的時間。
應(yīng)用程序可以在阻塞和調(diào)度更多I/O請求之后重新啟動的更快邻奠,那么就可以更好的是利用系統(tǒng)的硬件笤喳。此外,如果應(yīng)用程序正在等待用戶輸入碌宴, 時間安排得越快杀狡,用戶對無縫執(zhí)行的感知就越強。

Preemptive Scheduling 搶先調(diào)度

在傳統(tǒng)的UNIX進程調(diào)度中贰镣,所有可運行的進程都被分配了一個時間片呜象。當一個進程耗盡它的timeslice時膳凝,內(nèi)核將暫停它,并開始運行一個不同的進程恭陡。
系統(tǒng)上有更高優(yōu)先級的進程-低優(yōu)先級進程只能等待高優(yōu)先級進程耗盡它們的時間或阻塞蹬音。
這種行為制定了Unix調(diào)度的一個重要但默契的規(guī)則:所有進程都必須向前推進。

The Completely Fair Scheduler

完全公平調(diào)度器(CFS)與傳統(tǒng)的Unix進程調(diào)度程序有很大的不同休玩。
在大多數(shù)Unix系統(tǒng)中著淆,包括CFS推出之前的Linux,進程調(diào)度中有兩個基本的進程變量:優(yōu)先級和時間拴疤。
CFS引入了一種完全不同的算法永部,稱為公平調(diào)度fair shecduling,它消除了時間片作為分配給處理器的訪問權(quán)限的單元呐矾。
CFS為每個進程分配處理器時間的一部分苔埋,而不是時間間隔:

  1. CFS首先給N個進程分配1/N的處理器時間。
  2. CFS然后通過用每個進程的nice value加權(quán)每個進程的占用處理器時間的比重蜒犯。nice value為零的進程的權(quán)重為1组橄,因此它們的比例不變。具有較小的nice value(高優(yōu)先級)的進程接收較大的權(quán)重罚随,增加了占用處理器的時間比重晨炕,而具有較大的nice value(較低優(yōu)先級)的進程則減少了占用處理器的時間比重。
  3. 為了確定每個進程運行的實際時間長度毫炉,CFS需要將比重劃分成一個固定時間瓮栗。這個時間被稱為target latency,表示系統(tǒng)調(diào)用的latency瞄勾。
    CFS引入了第二個關(guān)鍵變量费奸,最小粒度the minimum granularity。
    The minimum granularity是任何進程運行的時間長度的最小單元进陡。

Yielding the Processor

雖然linux是一個先發(fā)制人的多任務(wù)操作系統(tǒng)愿阐,但它也提供了一個系統(tǒng)調(diào)用,允許進程顯式地產(chǎn)生執(zhí)行并指示調(diào)度程序選擇一個新進程執(zhí)行趾疚。

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

對schedyfield()的調(diào)用導(dǎo)致當前正在運行的進程暫停缨历,之后進程調(diào)度器選擇要運行的新進程,

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

Legitimate Uses

實際上糙麦,在適當?shù)南劝l(fā)制人多任務(wù)處理系統(tǒng)(如Linux)上辛孵,sched_yield()很少有合法的使用。內(nèi)核完全有能力自己處理這個調(diào)度赡磅,并且性能更好魄缚。
那么為什么POSIX要由這么以分系統(tǒng)調(diào)用呢?
答案在于應(yīng)用程序必須等待外部事件,這些事件可能是由用戶、硬件組件或其他進程引起的冶匹。

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

值得慶幸的是习劫,UNIX程序員不傾向于編寫這樣的代碼。UNIX程序通常事件驅(qū)動嚼隘,并傾向于在消費者之間使用某種可阻止的機制(如管道)诽里。 和代替SCHED_LEVENT()的生產(chǎn)者。

Process Priorities

nice value 的范圍是[-20, 19] 默認值是0飞蛹。
nice value 的值越小谤狡, 優(yōu)先級越高,時間片越大桩皿。
nice value 的值越大豌汇, 優(yōu)先級越小幢炸,時間片越小泄隔。
Linux提供了幾個系統(tǒng)調(diào)用,用于檢索和設(shè)置進程的nice value宛徊。
最簡單的是nice():

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

對nice()的成功調(diào)用將由inc遞增進程的nice value佛嬉,并返回新更新的值。
也就是如果inc為正,那么就是在降低優(yōu)先級,r如果inc為負闸天,則是在增加優(yōu)先級暖呕。
錯位的時候返回-1。因為-1也是nice的一個成功調(diào)用苞氮,所以要檢測錯誤的話就得像下面這么用湾揽。

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()系統(tǒng)調(diào)用笼吟,它允許更多的控制库物,但在操作上更復(fù)雜:

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

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

參數(shù)which有如下參數(shù)選項:

  1. PRIO_PROCESS
  2. PRIO_PGRP
  3. PRIO_USER
    參數(shù)who有就是對相應(yīng)上面which參數(shù)的進程ID, 進程組ID贷帮, 用戶ID戚揭。
    who是0的時候,就表示處理的是當前的進程ID撵枢,進程組ID民晒,或用戶ID
    參數(shù)prio通nice()的參數(shù)。錯誤的處理方式也同nice锄禽。

Processor Affinity

一旦進程被調(diào)度在一個CPU上潜必,進程調(diào)度程序應(yīng)該在將來將它調(diào)度在同一個CPU上。這是有益的沃但,因為將進程從一個處理器遷移到另一個處理器是有代價的刮便。

有時,用戶或應(yīng)用程序希望實施進程到處理器的綁定绽慈。這通常是因為該過程是強緩存敏感的恨旱,并且希望保留在同一處理器上辈毯。
將進程連接到特定處理器并讓內(nèi)核強制執(zhí)行關(guān)系稱為設(shè)置hard affinity。

sched_getaffinity() and sched_setaffinity()

Linux提供了兩個系統(tǒng)調(diào)用搜贤,用于檢索和設(shè)置進程的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時谆沃,調(diào)用將檢索當前進程的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檢查系統(tǒng)中的給定處理器 i 是否綁定到此進程仪芒。

測試結(jié)果

我們只關(guān)注CPU#0唁影,#1, #2掂名, #3据沈,因為它們是這個系統(tǒng)上唯一的物理處理器。也許我們希望確保我們的進程只在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;
}
測試結(jié)果

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;
}
測試結(jié)果
//怎么設(shè)置進程的調(diào)度方式
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

每個進程都具有靜態(tài)優(yōu)先級,與nice value無關(guān)猾警。對于正常的應(yīng)用程序孔祸,這個優(yōu)先級總是0。對于實時過程发皿,它的范圍從1到99崔慧。
linux調(diào)度程序總是選擇要運行的最高優(yōu)先級進程(即具有最大數(shù)值靜態(tài)優(yōu)先級值的進程)均蜜。

#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參數(shù)有如下:

  • RLIMIT_AS
    限制進程地址空間的最大大小(以字節(jié)為單位)须板。試圖增加超過此限制的地址空間的大小(通過調(diào)用mmap()和brk()將失敗并返回ENOMEM匣吊。如果 進程的堆棧体箕,根據(jù)需要自動增長宰僧,擴展超過這個限制考榨,內(nèi)核向進程發(fā)送SIGSEGV信號瘤袖。這個極限通常是RLIM_INFINITY测柠。
  • RLIMIT_CORE
    以字節(jié)為單位確定核心文件的最大大小誉结。如果不是零鹅士,則大于此限制的核心文件將被截斷為最大大小。如果為0惩坑,則永遠不會創(chuàng)建核心文件掉盅。
  • RLIMIT_CPU
    指示進程可消耗的最大CPU時間(以秒為單位)。如果進程運行的時間超過此限制以舒,內(nèi)核將向它發(fā)送SIGXCPU信號趾痘,進程可能會捕獲和處理該信號。
    但是蔓钟,Linux允許進程繼續(xù)執(zhí)行永票,并以一秒鐘的間隔繼續(xù)發(fā)送SIGXCPU信號。一旦進程達到hard limit,它將被發(fā)送一個SIGKILL并終止侣集。
  • RLIMIT_DATA
    控制進程數(shù)據(jù)段和堆的最大大小键俱,以字節(jié)為單位。 試圖通過 brk () 放大超過此限制的數(shù)據(jù)段將失敗并返回 ENOMEM
  • RLIMIT_FSIZE
    指定進程可創(chuàng)建的最大文件大小(以字節(jié)為單位)世分。如果進程擴展超過此大小的文件编振,內(nèi)核將向進程發(fā)送SIGXFSZ信號。默認情況下臭埋,此信號終止過程踪央。但是,一個進程可以選擇捕獲和處理這個信號瓢阴,在這種情況下畅蹂,違規(guī)的系統(tǒng)調(diào)用失敗并返回EFBIG。
  • RLIMIT_LOCKS
    控制進程可能持有的文件鎖的最大數(shù)量荣恐。一旦達到此限制液斜,進一步嘗試獲取其他文件鎖會失敗并返回ENOLCK。但是募胃,Linux內(nèi)核2.4.25刪除了此功能旗唁。在當前內(nèi)核中畦浓,此限制是可設(shè)置的痹束,但沒有任何效果。
  • RLIMIT_MSGQUEUE
    指定用戶可為POSIX消息隊列分配的最大字節(jié)數(shù)讶请。如果新創(chuàng)建的消息隊列將超過此限制祷嘶,mq_open()將失敗并返回ENOMEM;它是在內(nèi)核2.6.8中添加的夺溢,并且是Linux特有的论巍。
  • RLIMIT_NICE
  • RLIMIT_NOFILE
  • RLIMIT_NPROC
  • RLIMIT_RTTIME
    Specifies a limit (in microseconds) on CPU time that a real-time process may consume without issuing a blocking system call. Once the process makes a blocking system call, the CPU time is reset to zero.
  • RLIMIT_RTPRIO
  • RLIMIT_SIGPENDING
    指定可為此用戶排隊的最大信號數(shù)(標準信號和實時信號)
  • RLIMIT_STACK
    表示進程的棧的最大大小,單位為字節(jié)风响。超過此限制會導(dǎo)致SIGSEGV的傳遞嘉汰。
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; }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市状勤,隨后出現(xiàn)的幾起案子鞋怀,更是在濱河造成了極大的恐慌,老刑警劉巖持搜,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件密似,死亡現(xiàn)場離奇詭異,居然都是意外死亡葫盼,警方通過查閱死者的電腦和手機残腌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抛猫,你說我怎么就攤上這事蟆盹。” “怎么了闺金?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵日缨,是天一觀的道長。 經(jīng)常有香客問我掖看,道長匣距,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任哎壳,我火速辦了婚禮毅待,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘归榕。我一直安慰自己尸红,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布刹泄。 她就那樣靜靜地躺著外里,像睡著了一般。 火紅的嫁衣襯著肌膚如雪特石。 梳的紋絲不亂的頭發(fā)上盅蝗,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音姆蘸,去河邊找鬼墩莫。 笑死,一個胖子當著我的面吹牛逞敷,可吹牛的內(nèi)容都是我干的狂秦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼推捐,長吁一口氣:“原來是場噩夢啊……” “哼裂问!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起牛柒,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤堪簿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后焰络,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戴甩,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年闪彼,在試婚紗的時候發(fā)現(xiàn)自己被綠了甜孤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片协饲。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖缴川,靈堂內(nèi)的尸體忽然破棺而出茉稠,到底是詐尸還是另有隱情,我是刑警寧澤把夸,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布而线,位于F島的核電站,受9級特大地震影響恋日,放射性物質(zhì)發(fā)生泄漏膀篮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一岂膳、第九天 我趴在偏房一處隱蔽的房頂上張望誓竿。 院中可真熱鬧,春花似錦谈截、人聲如沸筷屡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毙死。三九已至,卻和暖如春喻鳄,著一層夾襖步出監(jiān)牢的瞬間扼倘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工诽表, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唉锌,地道東北人隅肥。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓竿奏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腥放。 傳聞我的和親對象是個殘疾皇子泛啸,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容