Как хорошие приоритеты и политики планировщика связаны с идентификаторами процессов (потоков?) В Linux? - PullRequest
6 голосов
/ 27 июля 2011

Я исследую, как сделать мой рабочий стол Linux более плавным и интерактивным, в то время как я выполняю задачи с интенсивным использованием процессора в фоновом режиме. Вот пример программы (написанной на 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 <pid>. Однако я обнаружил, что это не сильно влияет. Вместо этого мне нужно арендовать все дочерние процессы с помощью чего-то вроде ls /proc/<pid>/task | xargs renice 20 -p --, что имеет гораздо больший эффект.

Я очень смущен этим, поскольку не ожидал, что у потоков будут свои собственные идентификаторы процессов. Даже если бы они это сделали, я бы ожидал, что renice повлияет на весь процесс, а не только на основной поток процесса.

У кого-нибудь есть четкое понимание того, что здесь происходит? Похоже, что каждый поток на самом деле является отдельным процессом (по крайней мере, у него есть действительный PID). Я знал, что исторически Linux работал так, но я верил, что NPTL исправили это много лет назад.

Я тестирую на RHEL 5.4 (ядро Linux 2.6.18).

(В качестве отступления. Я замечаю тот же эффект, если пытаюсь использовать sched_setscheduler(<pid>, SCHED_BATCH, ..), чтобы попытаться решить эту проблему интерактивности. Т.е. мне нужно сделать этот вызов для всех "дочерних" процессов, которые я вижу в /proc/<pid>/task , недостаточно выполнить его один раз на главном программном pid.)

Ответы [ 2 ]

2 голосов
/ 29 июля 2011

Насколько я понимаю, в Linux потоки и процессы - одно и то же; потоки просто оказываются процессами, которые совместно используют одну и ту же память, а не выполняют копирование при записи, а fork (2) и pthread_create (3) предположительно оба слоями на вызов clone (2) с разными аргументами.

Планирование очень сбивает с толку, потому что, например, страница руководства pthreads (7) начинается с того, что потоки Posix имеют общее приятное значение, но затем вам нужно перейти к

NPTL по-прежнему имеет несколько несоответствий с POSIX.1: потоки не поделиться общим хорошим значением

чтобы увидеть всю картину (и я уверен, что есть множество еще менее полезных справочных страниц).

Я написал приложения с графическим интерфейсом, которые порождают несколько вычислительных потоков из основного потока пользовательского интерфейса, и всегда находил ключ к тому, чтобы приложение оставалось очень отзывчивым, чтобы вызвать 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) и обнаружил, что он действительно применим ко всему процессу (ко всем потокам), что не было тем, что я хотел (но, возможно, вы делаете). Но это происходит уже много лет назад; с тех пор поведение могло измениться.

Один важный инструмент, когда вы играете с такими вещами: если вы нажмете 'H' (NB, а не 'h') в top (1) , он изменится с представления процесса на отображение всех темы и отдельные темы хорошие значения. Например, если я запускаю [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         
 ...
2 голосов
/ 28 июля 2011

Идентификаторы потоков происходят из того же пространства имен, что и идентификаторы PID. Это означает, что каждый поток невидимо адресуется своим TID - некоторые системные вызовы применяются ко всему процессу (например, kill), а другие - только к одному потоку.

Системные вызовы планировщика, как правило, относятся к последнему классу, поскольку это позволяет вам присваивать разным потокам внутри процесса разные атрибуты планировщика, что часто полезно.

...