这一章的题目是--高级进程管理,这篇文章将以书中所叙的顺序进行讲解

1、让出处理器

Linux提供一个系统调用运行进程主动让出执行权:sched_yield。进程运行的好好的,为什么需要这个函数呢?有一种情况是用户空间线程的锁定。如果一个线程试图取得另一个线程所持有的锁,则新的线程应该让出处理器知道该锁变为可用。用户空间锁没有内核的支持,这是一个最间单、最有效率的做法。但是现在Linux线程实现引入一个使用futexes的优化解决方案。

  另一个情况是在有处理器密集型程序可用周期性调用sched_yield,试图将该进程对系统的冲击减到最小。不管怎么说,如何调度程序应该是系统的事情,而不是进程自己去管。eg:

int main(){
    int ret, i;
    ret = sched_yield();
    if(ret == -1){
    printf("调用sched_yield失败!\n");
    }
    return 0;
}

那该调用内核是如何实现的?2.6以前的版本sched_yield所造成的影响非常小,如果存在另一个可以运行的进程,内核就切换到该进程,把进行调用的进程放在可运行进程列表的结尾处。短期内内核会对该进程进行重新调度。这样的话可能出现“乒乓球”现象,也就是两个程序来回运行,直到他们都运行结束。2.6版本中做了一些改变:

  1. 如果进程是RR,把它放到可运行进程结尾,返回。
  2. 否则,把它从可运行进程列表移除,放到到期进程列表,这样在其他可运行进程时间片用完之前不会再运行该进程。
  3. 从可执行进程列表中找到另一个要执行的进程。

2、进程的优先级

  看过CFS中会看到进程的nice value会决定进程会运行多长时间,或者说是占用的百分比。可以通过系统调用nice来设置、获取进程的nice value。该值的范围是-20~19,越低的值越高的优先级(这个在计算虚拟时间的时候放在分母上),实时进程应该是负数,eg:

int main(){
    int ret, i;
    ret = nice(0);
    printf("当前进程的nice value:%d\n", ret);
    ret = nice(10);
    printf("当前进程的nice value:%d\n", ret);
    return 0;
}

因为ret本来就可以是-1,那么在判断是否系统调用失败的时候就要综合ret和errno。还有两个系统调用可以更灵活地设置,getpriority可以获得进程组、用户的任何进程中优先级最高的。setpriority将所指定的所有进程优先级设置为prio,eg:

int main(){
    int ret, i;
    ret = getpriority(PRIO_PROCESS, 0);
    printf("nice value:%d\n", ret);
    ret = setpriority(PRIO_PROCESS, 0, 10);
    ret = getpriority(PRIO_PROCESS, 0);
    printf("nice value:%d\n", ret);
    return 0;
}

进程有在处理器上执行的优先级,也有传输数据的优先级:I/O优先级。linux有额外的两个系统调用可用显示设置和取得I/O nice value,但是尚未导出:

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

3、处理器亲和性
  Linux支持具有多个处理器的单一系统。在SMP上,系统要决定每个处理器上要运行那些程序,这里有两项挑战:

  1. 调度程序必须想办法充分利用所有的处理器。
  2. 切换程序运行的处理器是需要代价的。

  进程会继承父进程的处理器亲和性,Linux提供两个系统调用用于获取和设定“硬亲和性”。eg:

int main(){
    int ret, i;
    cpu_set_t set;

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

    CPU_ZERO(&set);
    CPU_SET(0, &set);
    CPU_CLR(1, &set);
    ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);
    if(ret == -1)
    printf("调用失败!\n");
    for(i = 0; i < 10; i++){
    int cpu = CPU_ISSET(i, &set);
    printf("cpu=%i is %s\n", i, cpu?"set":"unset");
    }
    return 0;
}

4、Linux的调度策略与优先级
  关于Linux系统中对进程的几种调度方法和他们的区别就不在这里说了,这里关注的是如何获取、设置这些值。可以使用sched_getscheduler来获取进程的调度策略,eg:

int main(){
    int ret, i;
    struct sched_param sp;
    sp.sched_priority = 1;
    ret = sched_setscheduler(0, SCHED_RR, &sp);
    if(ret == -1)
    printf("sched_setscheduler failed.\n");
    if(errno == EPERM)
    printf("Process don't the ability.\n");

    ret = sched_getscheduler(0);
    switch(ret){
    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:
    printf("sched_getscheduler failed.\n");
    break;
    default:
    printf("Unknow policy\n");
    }
    return 0;
}

sched_getparam和sched_setparam接口可用于取得、设定一个已经设定好的策略,这里不只是返回一个策略的ID,eg:

int main(){
    int ret, i;
    struct sched_param sp;

    sp.sched_priority = 1;
    ret = sched_setparam(0, &sp);
    if(ret == -1)
    printf("sched_setparam error.\n");

    ret = sched_getparam(0, &sp);
    if(ret == -1)
    printf("sched_getparam error.\n");

    printf("our priority is %d.\n", sp.sched_priority);
    return 0;
}

Linux提供两个用于取得有效优先值的范围的系统调用,分别返回最大值、最小值,eg:

int main(){
    int ret, i;
    struct sched_param sp;

    ret = sched_get_priority_min(SCHED_RR);
    if(ret == -1)
    printf("sched_get_priority_min error.\n");
    printf("The min nice value is %d.\n", ret);

    ret = sched_get_priority_max(SCHED_RR);
    if(ret == -1)
    printf("sched_get_priority_max error.\n");
    printf("The mmax nice value is %d.\n", ret);
    return 0;
}

关于时间片,这个概念可能在Linux中和传统的在操作系统的课程中学到的还是有很大的区别的,如果感兴趣的化可以看看CFS里面的。通过sched_rr_get_interval可以取到分配给pid的时间片的长度,eg:

int main(){
    int ret, i;
    struct timespec tp;
    ret = sched_rr_get_interval(0, &tp);
    if(ret == -1)
    printf("sched_rr_get_interval error.\n");
    printf("The time is %ds:%ldns.\n", (int)tp.tv_sec, tp.tv_nsec);
    return 0;
}

5、实时进程的预防措施
  由于实时进程的本质,开发者在开发和调试此类程序时应该谨慎行事,如果一个实时进程突然发脾气,系统的反应会突然变慢。任何一个CPU密集型循环在一个实时程序中会继续无止境地运行下去,只要没有优先级更高实时进程变成可运行的。因此设计实时程序的时候要谨慎,这类程序至高无上,可用轻易托跨整个系统,下面是一些要决与注意事项:

  1. 因为实时进程会好用系统上一切资源,小心不要让系统其他进程等不到处理时间。
  2. 循环可能会一直运行到结束。
  3. 小心忙碌等待,也就是实时进程等待一个优先级低的进程所占有的资源。
  4. 开发一个实时进程的时候,让一个终端保持开启状态,以更高的优先级来运行该实时进程,发生紧急情况终端机依然会有反应,允许你终止失控的实时进程。
  5. 使用chrt设置、取得实时属性。

6、资源限制

  Linux对进程加上了若干资源限制,这些限制是一个进程所能耗用的内核资源的上限。限制的类型如下:

  1. RLIMIT_AS:地址空间上限。
  2. RLIMIT_CORE:core文件大小上限。
  3. RLIMIT_CPU:可耗用CPU时间上限。
  4. RLIMIT_DATA:数据段与堆的上限。
  5. RLIMIT_FSIZE:所能创建文件的大小上限。
  6. RLIMIT_LOCKS:文件锁数目上限。
  7. RLIMIT_MEMLOCK:不具备CAP_SYS_IPC能力的进程最多将多少个字节锁进内存。
  8. RLIMIT_MSGQUEUE:可以在消息队列中分配多少字节。
  9. RLIMIT_NICE:最多可以将自己的友善值调多低。
  10. RLIMIT_NOFILE:文件描述符数目的上限。
  11. RLIMIT_NPROC:用户在系统上能运行进程数目上限。
  12. RLIMIT_RSS:内存中页面的数目的上线。
  13. RLIMIT_RTPRIO:不具备CAP_SYS_NICE能力进程所能请求的实时优先级的上限。
  14. RLIMIT_SIGPENDING:在队列中信号量的上限,Linux特有的限制。
  15. RLIMIT_STACK:堆栈大小的上限。
int main(){
    int ret, i;
    struct rlimit rlim;

    rlim.rlim_cur = 32*1024*1024;
    rlim.rlim_max = RLIM_INFINITY;
    ret = setrlimit(RLIMIT_CORE, &rlim);

    ret = getrlimit(RLIMIT_CORE, &rlim);
    if(ret == -1)
    printf("getrlimit error.\n");
    printf("RLIMIT_CORE limits: soft=%ld hard=%ld\n", rlim.rlim_cur, rlim.rlim_max);

    return 0;
}

Linux System Programming --Chapter Six的更多相关文章

  1. Linux System Programming --Chapter Nine

    这一章的标题是 "信号" ,所以本文将对信号的各个方面进行介绍,由于Linux中的信号机制远比想象的要复杂,所以,本文不会讲的很全面... 信号机制是进程之间相互传递消息的一种方法 ...

  2. Linux System Programming --Chapter Eight

    内存管理 一.分配动态内存的几个函数 用户空间内存分配:malloc.calloc.realloc1.malloc原型如下:extern void *malloc(unsigned int num_b ...

  3. Linux System Programming --Chapter Seven

    文件和目录管理 一.文件与其元数据 我们首先看一下一个简单的文本文件是怎么保存的: 打开vim,编辑一段文本: [root@localhost ~]# vim hello.txt 编辑内容如下: op ...

  4. Linux System Programming note 8 ——File and Directory Management

    1. The Stat Family #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> ...

  5. Linux System Programming 学习笔记(十一) 时间

    1. 内核提供三种不同的方式来记录时间 Wall time (or real time):actual time and date in the real world Process time:the ...

  6. Linux System Programming 学习笔记(七) 线程

    1. Threading is the creation and management of multiple units of execution within a single process 二 ...

  7. Linux System Programming 学习笔记(六) 进程调度

    1. 进程调度 the process scheduler is the component of a kernel that selects which process to run next. 进 ...

  8. Linux System Programming 学习笔记(四) 高级I/O

    1. Scatter/Gather I/O a single system call  to  read or write data between single data stream and mu ...

  9. Linux System Programming 学习笔记(二) 文件I/O

    1.每个Linux进程都有一个最大打开文件数,默认情况下,最大值是1024 文件描述符不仅可以引用普通文件,也可以引用套接字socket,目录,管道(everything is a file) 默认情 ...

随机推荐

  1. 一个环形公路,上面有N个站点,A1, ..., AN,其中Ai和Ai+1之间的距离为Di,AN和A1之间的距离为D0。 高效的求第i和第j个站点之间的距离,空间复杂度不超过O(N)。

    //点数 #define N 10 //点间距 int D[N]; //A1到每个Ai的距离 int A1ToX[N]; void preprocess() { srand(time(0)); //随 ...

  2. SpringMVC基础配置(通过注解配置,非xml配置)

    SpringMVC是什么,有多火,我这里就不再啰嗦了,SpringMVC比Struts2好用太多,我在学校的时候私下里两种都接触过,对比之后果断选择了SpringMVC,后来在做Android应用开发 ...

  3. Android fragment(片段)构建灵活的UI

    在以支持多种屏幕尺寸为目标设计应用时,您可以在不同的布局配置中重复使用您的fragment 从而根据可用的屏幕空间优化用户体验. 例如,在手机设备上,由于采用单窗格用户界面,因此可能更适合一次只显示一 ...

  4. Android之Animation动画的使用(一)

    我们在使用一些控件时候,难免会设置一些进入和退出的动画效果,比如popupwindow.listview的item动画.按钮.图片等等,要使这些控件有动画效果,当然需要用到Animation了. 下面 ...

  5. 【移动开发】 Android隐藏输入法软键盘的一些说明

    刚刚在写一个仿微信的Android聊天软件,在编写的过程中,发现一个严重的BUG---当用户点击输入框用软键盘输入文本的时候点击了"返回好友列表"的按钮,返回到好友列表时软键盘无法 ...

  6. RocketMQ,JStorm与Tair使用笔记

    关于RocketMQ 启动mq nohup sh mqnamesrv -n 10.150.0.94:9876 &  nohup sh mqbroker -n 10.150.0.94:9876 ...

  7. 剑指Offer——Trie树(字典树)

    剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...

  8. 14 fragment传值

    两个fragment传值 方式一 布局文件代码: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/and ...

  9. UE4使用C++创建枚举变量适用于C++与蓝图

    这个月勉勉强强才写了一篇,都快月底了,都还没有写第二篇博客的冲动,证明这个月确实收获甚少,有点状态不佳,懒毒入骨啊.刚刚看了这个月其实已经写了三篇,然而事实是这博客还有另外一个人也在更新文章,博主并没 ...

  10. Android 5.1 添加硬件抽象层(HAL)和JNI接口总结

    点击打开链接