【问题标题】:How are nice priorities and scheduler policies related to process (thread?) IDs in linux?与Linux中的进程(线程?)ID相关的优先级和调度程序策略如何?
【发布时间】:2011-07-27 13:05:01
【问题描述】:

我正在研究如何让我的 Linux 桌面体验在后台运行 CPU 密集型任务时保持流畅和交互。这是我用来模拟 CPU 负载的示例程序(用 Java 编写):

public class Spinner {
    public static void main(String[] args)
    {
        for (int i = 0; i < 100; i++) {
            (new Thread(new Runnable() {
                    public void run() {
                        while (true);
                    }
            })).start();
        }
    }
}

当我在命令行上运行它时,我注意到我的桌面应用程序(例如文本编辑器)的交互性显着下降。我有一个双核机器,所以我对此并不感到惊讶。

为了解决这个问题,我的第一个想法是使用renice -p 20 &lt;pid&gt;完善这个过程。但是我发现这并没有太大影响。相反,我必须使用 ls /proc/&lt;pid&gt;/task | xargs renice 20 -p -- 之类的东西来修改所有子进程,这会产生更大的影响。

我对此感到非常困惑,因为我不希望线程有自己的进程 ID。即使他们这样做了,我也希望renice 作用于整个进程,而不仅仅是进程的主线程。

有人对这里发生的事情有清楚的了解吗? 看来每个线程实际上是一个单独的进程(至少它有一个有效的 PID)。我知道过去 Linux 是这样工作的,但我相信 NPTL 早在几年前就修复了。

我正在 RHEL 5.4(Linux 内核 2.6.18)上进行测试。

(顺便说一句。如果我尝试使用sched_setscheduler(&lt;pid&gt;, SCHED_BATCH, ..) 来尝试解决这个交互性问题,我会注意到同样的效果。也就是说,我需要对我在/proc/&lt;pid&gt;/task 中看到的所有“子”进程进行此调用,在主程序pid上执行一次是不够的。)

【问题讨论】:

    标签: linux scheduling thread-priority


    【解决方案1】:

    线程 ID 与 PID 来自相同的命名空间。这意味着每个线程都可以通过其 TID 单独寻址 - 一些系统调用确实适用于整个进程(例如,kill),但其他系统调用仅适用于单个线程。

    调度器系统调用一般在后一类,因为这允许你在一个进程中为不同的线程赋予不同的调度器属性,这通常很有用。

    【讨论】:

    【解决方案2】:

    据我了解,Linux 上的线程和进程几乎是一回事;线程恰好是共享相同内存的进程,而不是执行 fork 的写时复制的事情,fork(2)pthread_create(3) 可能都只是分层调用具有不同参数的clone(2)

    调度的东西非常混乱,因为例如 pthreads(7) 手册页一开始告诉你 Posix 线程共享一个共同的好值,但你必须开始

    NPTL 仍有一些不符合 POSIX.1 的地方:线程不 分享一个共同的美好价值观

    查看全貌(我敢肯定还有很多帮助更少的手册页)。

    我编写了从主 UI 线程生成多个计算线程的 GUI 应用程序,并且始终发现让应用程序保持响应速度非常快的关键是在计算线程中调用 nice(2)(仅);将其增加 4 左右似乎效果很好。

    或者至少那是我记得所做的。我多年来第一次查看代码,发现我实际上做了这样的事情:

    // Note that this code relies on Linux NPTL's non-Posix-compliant
    // thread-specific nice value (although without a suitable replacement
    // per-thread priority mechanism it's just as well it's that way).
    // TODO: Should check some error codes,
    // but it's probably pretty harmless if it fails.
    
      const int current_priority=getpriority(PRIO_PROCESS,0);
      setpriority(PRIO_PROCESS,0,std::min(19u,current_priority+n)); 
    

    这很有趣。我可能尝试过 nice(2) 并发现它确实适用于整个进程(所有线程),这不是我想要的(但也许你会这样做)。但这已经追溯到几年前了。自那以后,行为可能已经改变。

    当你玩这类东西时,一个必不可少的工具:如果你在top(1) 中点击'H'(注意不是'h'),它会从进程视图变为显示所有线程和单个线程很好价值观。例如,如果我运行[evolvotron][7] -t 4 -n 5(4 个计算线程很好 5)我看到(我只是在一台旧的单核非 HT 机器上,所以这里的多线程实际上并没有多大意义):

    Tasks: 249 total,   5 running, 244 sleeping,   0 stopped,   0 zombie
    Cpu(s): 17.5%us,  6.3%sy, 76.2%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:   1025264k total,   984316k used,    40948k free,    96136k buffers
    Swap:  1646620k total,        0k used,  1646620k free,   388596k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
     4911 local     25   5 81096  23m  15m R 19.7  2.4   0:04.03 evolvotron         
     4912 local     25   5 81096  23m  15m R 19.7  2.4   0:04.20 evolvotron         
     4913 local     25   5 81096  23m  15m R 19.7  2.4   0:04.08 evolvotron         
     4914 local     25   5 81096  23m  15m R 19.7  2.4   0:04.19 evolvotron         
     4910 local     20   0 81096  23m  15m S  9.8  2.4   0:05.83 evolvotron         
     ...
    

    【讨论】:

    • 线程不仅仅是共享相同mm 和文件表的进程。在内部,它们是同一个任务组的成员,这具有其他效果——例如,进程导向的信号可以由任务组中的任何线程处理;如果任何线程调用exit_group() 系统调用(映射到glibc 中的exit() 包装器),则整个任务组退出;像SIGSEGV 这样的进程终止信号将终止整个任务组;只有线程组负责人在/proc 中获得顶级目录。
    • 非常感谢您的“顶级”技巧。这似乎证实了我看到的行为。遗憾的是,我没有看到 top 显示每个线程的调度程序策略的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-12
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多