好的优先级和调度程序策略与 Linux 中的进程(线程?)ID 有何关系?

发布于 2024-11-26 11:49:55 字数 1060 浏览 1 评论 0原文

我正在研究如何在后台运行 CPU 密集型任务时让 Linux 桌面体验保持流畅和交互。下面是我用来模拟 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优化进程。但我发现这并没有太大影响。相反,我必须使用 ls /proc//task | 之类的命令来重新启动所有子进程。 xargs renice 20 -p -- 效果更大。

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

有人清楚地了解这里发生了什么吗?看来每个线程实际上都是一个单独的进程(至少它有一个有效的PID)。我知道 Linux 历史上是这样工作的,但我相信 NPTL 几年前就解决了这个问题。

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

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

I am investigating how to have my Linux desktop experience remain smooth and interactive while I run CPU intensive tasks in the background. Here is the sample program (written in Java) which I am using to simulate CPU load:

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();
        }
    }
}

When I run this on the command line, I notice that the interactivity of my desktop applicaitons (e.g. text editor) drops significantly. I have a dual core machine, so I am not suprised by this.

To combat this my first thought was to nice the process with renice -p 20 <pid>. I found however that this doesn't have much affect. I instead have to renice all of the child processes with something like ls /proc/<pid>/task | xargs renice 20 -p -- which has a much greater effect.

I am very confused by this, as I would not expect threads to have their own process IDs. Even if they did I would expect renice to act on the entire process, not just the main thread of the process.

Does anyone have a clear understanding of what is happening here? It appears that each thread is actually a seperate process (at least it has a valid PID). I knew that historically Linux worked like this, but I believed NPTL fixed that years ago.

I am testing on RHEL 5.4 (linux kernel 2.6.18).

(As an aside. I notice the same effect if I try to use sched_setscheduler(<pid>, SCHED_BATCH, ..) to try to solve this interactivity problem. Ie, I need to make this call for all the "child" processes I seee in /proc/<pid>/task, it is not enough to perform it once on the main program pid.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

迷雾森÷林ヴ 2024-12-03 11:49:55

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

调度程序系统调用通常属于后一类,因为这允许您为进程中的不同线程提供不同的调度程序属性,这通常很有用。

Thread IDs come from the same namespace as PIDs. This means that each thread is invididually addressable by its TID - some system calls do apply to the entire process (for example, kill) but others apply only to a single thread.

The scheduler system calls are generally in the latter class, because this allows you to give different threads within a process different scheduler attributes, which is often useful.

你是年少的欢喜 2024-12-03 11:49:55

据我了解,在 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),它从进程视图变为显示所有线程和单个线程的nice值。例如,如果我运行 [evolvotron][7] -t 4 -n 5 (nice 5 处的 4 个计算线程)我明白(我只是在一台旧的单核非 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         
 ...

As I understand it, on Linux threads and processes are pretty much the same thing; threads just happen to be processes which share the same memory rather than doing fork's copy-on-write thing, and fork(2) and pthread_create(3) are presumably both just layered onto a call to clone(2) with different arguments.

The scheduling stuff is very confusing because e.g the pthreads(7) man page starts off by telling you Posix threads share a common nice value but then you have to get down to

NPTL still has a few non-conformances with POSIX.1: Threads do not
share a common nice value

to see the whole picture (and I'm sure there are plenty of even less helpful man pages).

I've written GUI apps which spawn multiple compute threads from a main UI thread, and have always found the key to getting the app to remain very responsive is to invoke nice(2) in the compute threads (only); increasing it by 4 or so seems to work well.

Or at least that's what I remembered doing. I just looked at the code for the first time in years and see what I actually did was this:

// 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)); 

Which is interesting. I probably tried nice(2) and found it did actually apply to the whole process (all threads), which wasn't what I wanted (but maybe you do). But this is going back years now; behaviour might have changed since.

One essential tool when you're playing with this sort of stuff: if you hit 'H' (NB not 'h') in top(1), it changes from process view to showing all the threads and the individual thread nice values. e.g If I run [evolvotron][7] -t 4 -n 5 (4 compute threads at nice 5) I see (I'm just on an old single core non-HT machine, so not actually much point in multiple threads here):

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         
 ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文