Почему Free CPU не выделяется для обработки в Linux?
У меня есть одна программа P1, имеющая N (100) потоков. Первоначально все потоки находятся в состоянии блокировки (с использованием семафора), кроме потока 0.
// Program - P1
static void handler(int sig, siginfo_t *si, void *uc)
{
thread_no++;
ret = sem_post(&sem[(thread_no)%NUM_THREADS]);
if (ret)
{
printf("Error in Sem Post\n");
}
}
void *threadA(void *data_)
{
int turn = (intptr_t)data_;
cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(1, &my_set);
sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
while(1)
{
ret = sem_wait(&sem[turn]);
if (ret)
{
printf("Error in Sem Post\n");
}
// does some work here
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = DELAY1;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
ret = timer_settime(timerid, 0, &its, NULL);
if ( ret < 0 )
perror("timer_settime");
}
}
int main(int argc, char *argv[])
{
sa.sa_flags = SA_RESTART;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
err = sigaction(SIG, &sa, NULL);
if (0 != err) {
printf("sigaction failed\n"); }
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIG;
sev.sigev_value.sival_ptr = &timerid;
ret = timer_create(CLOCKID, &sev, &timerid);
if ( ret < 0 )
perror("timer_create");
sem_init(&sem[0], 0, 1);
for ( i = 1; i < NUM_THREADS; ++i)
{
sem_init(&sem[i], 0, 0);
}
data=0;
while(data < NUM_THREADS)
{
//create our threads
err = pthread_create(&tid[data], NULL, threadA, (void *)(intptr_t)data);
if(err != 0)
printf("\n can't create thread :[%s]", strerror(err));
data++;
}
}
Я создал один таймер с помощью timer_create() внутри программы P1 (и все потоки используют один и тот же таймер), установил таймер с интервалом T. Когда истекает интервал таймера, вызывается обработчик таймера, который отправляет сигнал следующему потоку i+1 для пробуждения.
Вот как работают мои программы
Step 1: Thread 0 does some work, sets timer ,goes into block state ( releasing CPU )
Step 2: On timer expiration, timer handler called
Step 3: Thread 1 is awaken, does some work, sets timer , goes into block state ( releasing CPU )
Step 4: On timer expiration, timer handler called
Step 5: Thread 2 is awaken, does some work, sets timer , goes into block state ( releasing CPU )
:
:
:
Step 2n-1: next thread Thread n-1 is awaken, does some work, sets timer , goes into block state ( releasing CPU )
Step 2n: On timer expiration, timer handler called
Step 2n+1: next thread Thread 0 is awaken, does some work, sets timer , goes into block state ( releasing CPU )
Это отлично работает, все мои потоки пробуждаются обработчиком таймера и работают в циклическом порядке.
У меня есть другая программа P2, работающая непрерывно в течение долгого времени (поэтому vruntime очень высока), даже до того, как P1 начинает работать, и P2 находится в одном ядре ЦП с P1 (со всеми потоками).
// Program - P2
int main(int argc, char *argv[])
{
cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(1, &my_set);
sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
while(1){
// does some task
}
}
Таким образом, когда не будет запущенных потоков, P2 должен быть запущен.
Итак, я ожидаю, что на шаге 1 поток 0 освобождает процессор, P2 должен быть запланирован, а момент времени истекает и следующий поток пробуждается на шаге 3, P2 должен быть немедленно вытеснен согласно политике планирования CFS (так как vruntime потока пробуждения очень низкий по сравнению с P2). Это означает, что я ожидаю, что P2 будет запланирован между Step1-Step3, Step3-Step5, когда все потоки находятся в состоянии блокировки, процессор свободен и следующий поток еще не пробужден.
Чтобы получить все процессы, связанные с переключением контекста, я внес изменения в ядро (kernel / sched / core.c и добавил следующую инструкцию print внутри функции context_switch)
trace_printk(KERN_INFO "**$$,context_switch,%d,%llu,%llu,%d,%llu,%llu\n", (int)(prev->pid),prev->se.vruntime,prev->se.sum_exec_runtime, (int)(next->pid),next->se.vruntime,next->se.sum_exec_runtime);
Так что я могу получить все детали процессов, которые запланированы и которые запланированы. Из вышеприведенной информации я вычислил, сколько времени P2 получает в каждом прогоне, прежде чем выгрузится.
Вот некоторые из моих открытий, которые я не могу понять, почему?
С частотой CPU =2,2 ГГц, если я установлю интервал таймера 1700 нс или даже меньше, P2 не будет запланирован между двумя потоками. Почему P2 не запланирован, даже если не запущен другой процесс / поток и процессор свободен в течение 1700 нс?
С частотой CPU =3,4 ГГц, если я установлю интервал таймера 1000 нс или даже меньше, P2 не будет запланирован между двумя потоками. Почему P2 не запланирован, даже если не запущен другой процесс и процессор свободен в течение 1000 нс?
С другой частотой ЦП и для другого интервала таймера другой процесс P2 не запланирован?
Есть ли связь между частотой процессора и временем истечения таймера? Это связано с синхронизацией переключения контекста?
Мой последний вопрос - если выполняется одно переключение контекста и готов новый процесс с более высоким приоритетом, будет ли выполнено текущее переключение контекста, какое-то время выполнено, затем запланирован следующий процесс с более высоким приоритетом или будет остановлено текущее переключение контекста и запланировано следующее процесс с более высоким приоритетом немедленно?
Я использую Core-i7 @3,40 ГГц, Ubuntu-16.04, cgroups отключены, и P1 и P2 работают в одном терминале. cpupower используется для установки частоты пользователя.
Заранее спасибо.