Если я запускаю два строго привязанных к ЦП потока, привязанных к одному и тому же ЦП, и один из них — SCHED_IDLE, почемLinux

Ответить
Anonymous
 Если я запускаю два строго привязанных к ЦП потока, привязанных к одному и тому же ЦП, и один из них — SCHED_IDLE, почем

Сообщение Anonymous »

Если я запускаю два потока, привязанных к ЦП 0, где один из них — SCHED_IDLE, то почему он когда-либо запланирован, когда другой поток привязан к ЦП либо с обычным приоритетом, либо с SCHED_FIFO?
Для SCHED_FIFO я понимаю, согласно sched(7), описывающему меры по предотвращению блокировки системы («Ограничение использования ЦП процессами реального времени и процессами с крайним сроком»), но это не объясняет ни то, почему в SCHED_OTHER также запускается поток ожидания, ни то, почему эти средства защиты решили запланировать SCHED_IDLE, даже когда защита срабатывает.
И даже когда я отключаю защиту, она все равно не блокирует SCHED_IDLE
$ sudo sysctl -a | grep sched_rt_
kernel.sched_rt_period_us = 1000000
kernel.sched_rt_runtime_us = 1000000

Мне не нужно реальное время. Мне просто нужно, чтобы SCHED_IDLE строго никогда не планировался, если есть другие запланированные процессы. В этом, как я думал, и состоит весь смысл SCHED_IDLE.
Это дизассемблирование кода, который выполняется в SCHED_FIFO. Я не вижу причин, по которым его когда-либо следует вытеснять в пользу SCHED_IDLE. Этот код всегда должен быть работоспособным. Никаких системных вызовов и инструкций паузы нет.
Dump of assembler code for function spin_loop_hint:
0x00000000000011d9 : push %rbp
0x00000000000011da : mov %rsp,%rbp
0x00000000000011dd : nop
0x00000000000011de : nop
0x00000000000011df : pop %rbp
0x00000000000011e0 : ret
Dump of assembler code for function infinite_loop:
0x000000000000146d : push %rbp
0x000000000000146e : mov %rsp,%rbp
0x0000000000001471 : call 0x11d9
0x0000000000001476 : jmp 0x1471

Я могу сказать, что поток SCHED_IDLE находится в планировании, потому что он периодически выводит данные на стандартный вывод.
Я думал, что весь смысл SCHED_IDLE в том, чтобы никогда не планировать, когда есть что-то еще, что может быть запущено. Кроме того, этот SCHED_FIFO никогда не должен быть вытеснен (чем-либо, кроме другого потока реального времени с более высоким приоритетом). Действительно, в sched(7) говорится, что SCHED_FIFO — это простой алгоритм планирования без разделения времени. Но это вытесняется.
На самом деле на странице руководства даже написано:

No other events will move a thread scheduled under the SCHED_FIFO policy in
the wait list of runnable threads with equal static priority.

A SCHED_FIFO thread runs until either it is blocked by an I/O request, it
is preempted by a higher priority thread, or it calls sched_yield(2).

Полный код
#define _GNU_SOURCE

#include
#include
#include
#include
#include
#include
#include

static void spin_loop_hint(void) {
// Attempt at preventing optimization steps.
asm("nop");
}

static void pin_to_cpu0(void) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);

int ret = pthread_setaffinity_np(pthread_self(),
sizeof(cpu_set_t),
&cpuset);
if (ret != 0) {
fprintf(stderr, "pthread_setaffinity_np failed: %d\n", ret);
}
}

static void set_sched_idle(void) {
struct sched_param param;
param.sched_priority = 0; /* must be 0 for SCHED_IDLE */

int ret = sched_setscheduler(0, SCHED_IDLE, &param);
if (ret != 0) {
int err = errno;
fprintf(stderr,
"sched_setscheduler(SCHED_IDLE) failed: ret=%d, errno=%d\n",
ret, err);
}
}

static int lock_all_memory(void) {
int flags = MCL_CURRENT | MCL_FUTURE;
int ret = mlockall(flags);
if (ret != 0) {
/* mlockall returns -1 on error and sets errno */
perror("mlockall");
return -1;
}
return 0;
}

static void set_sched_fifo(void) {
struct sched_param param;
param.sched_priority = 99;

int ret = sched_setscheduler(0, SCHED_FIFO, &param);
if (ret != 0) {
int err = errno;
fprintf(stderr,
"sched_setscheduler(SCHED_FIFO) failed: ret=%d, errno=%d\n",
ret, err);
}
}

static void idle_loop() {
unsigned long long i = 0;
for (;;) {
spin_loop_hint();
i++;
if (i % 100000ULL == 0ULL) {
printf("Why is SCHED_IDLE ever given any CPU?\n");
}
}
}

static void *idle_thread_func(void *arg) {
(void)arg;

if (lock_all_memory() != 0) {
fprintf(stderr, "lock_all_memory() failed in idle thread\n");
exit(EXIT_FAILURE);
}

pin_to_cpu0();
set_sched_idle();

printf("SCHED_IDLE thread running on CPU 0\n");
idle_loop();

/* Unreachable, but required for pthread signature */
return NULL;
}

void infinite_loop() {
for (;;) {
spin_loop_hint();
}
}

int main(void) {
if (lock_all_memory() != 0) {
fprintf(stderr, "lock_all_memory() failed in main\n");
return EXIT_FAILURE;
}

/* SCHED_IDLE spinning thread, also on CPU 0 */
pthread_t idle;
int ret = pthread_create(&idle, NULL, idle_thread_func, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create failed: %d\n", ret);
return EXIT_FAILURE;
}

/* "Normal" spinning thread (actually SCHED_FIFO here), pinned to CPU 0 */
pin_to_cpu0();
set_sched_fifo();
printf("Normal thread running on CPU 0\n");
infinite_loop();

/* Unreachable */
return 0;
}

Выход:
$ gcc -g t.c -o t && sudo ./t
Normal thread running on CPU 0
SCHED_IDLE thread running on CPU 0
Why is SCHED_IDLE ever given any CPU?
Why is SCHED_IDLE ever given any CPU?
Why is SCHED_IDLE ever given any CPU?
Why is SCHED_IDLE ever given any CPU?
[… continues forever, in batches …]


Подробнее здесь: https://stackoverflow.com/questions/798 ... ne-is-sche
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Linux»