我有一个具有N(100)个线程的程序P1。最初,除线程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++;
}
}
我已经在程序P1中使用 timer_create()创建了一个计时器(所有线程都使用相同的计时器),将计时器设置为间隔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在同一个CPU内核中(所有线程)。
// 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正在释放CPU,应该安排P2并在计时器到期时唤醒下一个线程 在步骤3中,应根据CFS调度策略立即抢占P2(因为唤醒线程的vruntime与P2相比非常低)。 这意味着,当所有线程都处于阻塞状态,CPU处于空闲状态且尚未唤醒下一个线程时,我期望P2在Step1-Step3和Step3-Step5之间进行调度。
要获得上下文切换涉及的所有过程,我对内核进行了更改( kernel / sched / core.c ,并在context_switch函数内部添加了以下print语句)
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 Frequency = 2.2GHz的情况下,如果我将计时器间隔设置为1700ns或更短,则不会在两个线程之间调度P2。 为什么没有调度P2,即使没有其他进程/线程正在运行并且CPU空闲了1700 ns?
如果我将计时器间隔设置为1000ns甚至是CPU频率= 3.4GHz 更少,P2不在两个线程之间进行调度。 为什么即使没有其他进程正在运行并且CPU空闲1000 ns也没有计划P2?
在不同的CPU频率和不同的计时器间隔下,是否未计划其他进程P2?
CPU频率和计时器到期时间之间是否存在任何关系?它与上下文切换时间有关吗?
我的最后一个问题-如果正在进行一个上下文切换,并且一个新的更高优先级进程已准备就绪,当前运行的上下文切换是否将完成,执行一段时间,然后安排下一个更高优先级的进程,否则它将停止当前上下文切换并立即安排下一个更高优先级的过程?
我正在使用Core-i7 @ 3.40 GHz,Ubuntu-16.04,禁用了cgroup,P1和P2都在同一终端上运行。 cpupower用于设置用户频率。
谢谢。