Действительно ли SCHED_IDLE препятствует выполнению на незанятом ядре?
Я пытаюсь реализовать непривилегированный тестовый пример для неограниченной инверсии приоритетов в отсутствие мьютексов наследования приоритетов, используя SCHED_IDLE
, Тест работает с SCHED_FIFO
и различные приоритеты в реальном времени (взаимоблокировка для мьютекса не-PI, немедленное разрешение с помощью мьютекса PI), но для включения этого в набор тестов, который будет работать без прав в реальном времени, я хотел бы использовать SCHED_IDLE
вместо этого, причем потоки со "средним" и "высоким" приоритетом SCHED_OTHER
(в этом случае это не совсем приоритетная "инверсия", но концепция все еще должна работать - "средняя" должна препятствовать выполнению "низкой").
К сожалению, в тесте не проводится различие между мьютексами PI и не-PI; это продвигает прогресс в любом случае. Видимо SCHED_IDLE
задание выполняется, даже если есть другое выполняемое задание. Сходство с процессором было установлено так, чтобы связать их все с одним и тем же ядром, чтобы задача с низким приоритетом не могла перейти на другое ядро для запуска. И я знаю, что SCHED_IDLE
задачи должны выполняться с повышенными привилегиями в то время как в пространстве ядра, чтобы предотвратить инверсию приоритета пространства ядра, поэтому я попытался убедиться, что "низкий" поток не входит в пространство ядра, делая его занятым циклом в пользовательском пространстве, и strace
не показывает никаких признаков того, что он делает системный вызов в течение времени, когда он не должен продвигаться вперед.
Есть ли у Linux SCHED_IDLE
просто позволить запускать бездействующие задачи, когда ядро фактически не работает? Или я могу что-то упустить?
Вот тестовый код, слегка адаптированный для того, чтобы его можно было запустить в режиме реального времени или SCHED_IDLE
:
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
sem_t sem;
void *start1(void *p)
{
pthread_mutex_lock(p);
sem_post(&sem);
sem_post(&sem);
usleep(100000);
pthread_mutex_unlock(p);
return 0;
}
void *start2(void *p)
{
sem_wait(&sem);
time_t t0 = time(0);
while (pthread_mutex_trylock(p)) {
if (time(0)>t0+5) return 0;
}
pthread_mutex_unlock(p);
return 0;
}
void *start3(void *p)
{
sem_wait(&sem);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
int r;
if (r=pthread_mutex_timedlock(p, &ts)) {
printf("failed: %d %s\n", r, strerror(r));
} else {
pthread_mutex_unlock(p);
}
return 0;
}
int main(int argc, char **argv)
{
int policy = argc>1 ? SCHED_IDLE : SCHED_FIFO;
int a = sched_get_priority_min(policy);
pthread_attr_t attr;
pthread_t t1,t2,t3;
struct sched_param param = {0};
cpu_set_t set = {0};
CPU_ZERO(&set);
CPU_SET(0, &set);
pthread_setaffinity_np(pthread_self(), sizeof set, &set);
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, policy);
pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_INHERIT);
pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_t mtx;
pthread_mutex_init(&mtx, &ma);
sem_init(&sem, 0, 0);
param.sched_priority = a+1;
pthread_attr_setschedparam(&attr, ¶m);
if (pthread_create(&t2, policy==SCHED_IDLE ? 0 : &attr, start2, &mtx)) return 1;
param.sched_priority = a+2;
pthread_attr_setschedparam(&attr, ¶m);
if (pthread_create(&t3, policy==SCHED_IDLE ? 0 : &attr, start3, &mtx)) return 1;
param.sched_priority = a;
pthread_attr_setschedparam(&attr, ¶m);
if (pthread_create(&t1, &attr, start1, &mtx)) return 1;
pthread_join(t1, 0);
pthread_join(t2, 0);
pthread_join(t3, 0);
return 0;
}
1 ответ
Есть ли у Linux
SCHED_IDLE
просто позволить запускать бездействующие задачи, когда ядро фактически не работает? Или я могу что-то упустить?
Это правильно. SCHED_IDLE
дает задачам очень низкое, но ненулевое взвешивание - примерно на 70% меньше процессорного времени, чем у хорошей задачи 19.