1

我在具有 Ubuntu-12.04 操作系统、3.18.26 内核的 Core2Duo、2.20GHz 系统中工作。

我对 Linux 内核源代码进行了一些更改。

为了让上下文切换中涉及的所有进程(被调度和取消调度),我在内核中进行了更改(kernel/sched/core.c 并在 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);`

我在同一个 CPU 内核中运行两个不同的进程 P1(具有 100 个线程 - T0、T1、...、T99)和 P2。P2 已经运行了很长时间,所以它的 vruntime 很高。

在 P1 内部,前 100 个线程被创建,除 T0 之外的所有线程都处于阻塞状态(等待信号量)。

  1. T0 执行一些任务,然后设置一个持续时间为 2000 纳秒的计时器并自愿释放 CPU。由于没有可用的线程,P2 被调度。
  2. 2000 纳秒后,定时器超时,它唤醒下一个线程 T1,该线程立即抢占 P2。
  3. T1 执行一些任务,然后设置一个持续时间为 2000 纳秒的计时器并主动释放 CPU。由于没有可用的线程,P2 被调度。
  4. 2000 纳秒后,定时器超时,它唤醒下一个线程 T2,该线程立即抢占 P2。

这重复并且线程 T0,T1,...T99 以循环方式执行。

所以,执行顺序如下

T0-P2-T1-P2-T2-P2-T3-......T99-P2-T0-P2.....

我的实验结果表明,

当我将计时器间隔设置为 1800 纳秒时,P2 进程平均为 1450 纳秒。

当我将计时器间隔设置为 2000 纳秒时,P2 进程平均为 1600 纳秒。

当我将计时器间隔设置为 2500 纳秒时,P2 进程的平均时间为 2050 纳秒。

当我将定时器间隔设置为 3000 纳秒时,P2 进程平均为 2600 纳秒。

因此,我得出结论,在我的 Core2Duo 系统中,上下文切换时间约为 350-450ns。我这样说对吗?

另一个观察结果是,当我将计时器间隔设置为 1600 纳秒或 1700 纳秒时,尽管 CPU 空闲,但 P2 进程不会在两个线程之间调度 - 这意味着尽管 P2 已准备好,但 CPU 在大约 1200 -1300 纳秒内空闲排队,准备运行。为什么会这样?

这是我的代码片段:

// 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
   }
}



    // Program - P1
// timer handler awakening next thread
        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++;
    }
}

内核跟踪显示,CPU 空闲,有足够的时间可用于上下文切换 - 线程 Ti 到 P2,仍然没有安排 P2,稍后在 Ti 和 T(i+1) 之间发生上下文切换。 为什么在这种情况下,当计时器持续时间小于 1700 纳秒时,linux CFS 不选择下一个进程进行调度?

4

0 回答 0