Изменение планировщика Minix 3

Недавно я купил эту книгу, чтобы лучше понять, как работают операционные системы. Я нахожусь во второй главе, и я застрял в этой проблеме, и моя ОС не загружается с добавленным мной кодом. Приведенный ниже код был добавлен в proc.c в начале функции pic_proc, чтобы попытаться изменить планировщик.

Модификация.

int realtime;
clock_t recent_time[NR_TASKS + NR_PROCS];
clock_t stopwatch;

realtime = getuptime();
recent_time[proc_ptr->p_nr + NR_TASKS] = realtime - stopwatch;
stopwatch = realtime;

Оригинальный код:

    PRIVATE void pick_proc()
    {
    register struct proc *rp;                     /* process to run */
    int q;                                        /* iterate over queues */
    
    /* Modified Code here */

     for (q=0; q < NR_SCHED_QUEUES; q++) {
        if ( (rp = rdy_head[q]) != NIL_PROC) {
              next_ptr = rp;                      
              if (priv(rp)->s_flags & BILLABLE)
                  bill_ptr = rp; 
     return;
      }
}
}

Книга Проектирование и внедрение операционных систем. 3-е издание The Minix Book, стр. 219, № 45. Измените планировщик Minix 3, чтобы отслеживать, сколько процессорного времени было недавно отведено каждому пользовательскому процессу. Если ни одна задача или сервер не хочет запускаться, выберите пользовательский процесс с наименьшей долей ЦП.

Код постановки в очередь

Запланированная часть 1

Запланированная часть 2


person Warfujjii    schedule 23.03.2021    source источник
comment
Как он не загружается? Segfault, исключение защиты и т.д.? Является ли proc_ptr действительным при вызове вашего кода? Это может быть NULL. Находится ли proc_ptr->p_nr в диапазоне от 0 до NR_PROC - 1? Если нет, у вас есть UB [неопределенное поведение], и вы портите стек [я предполагаю, что recent_time находится в стеке], потому что вы индексируете за концом recent_time. Вы должны добавить if проверки операторов на достоверность и добавить [minux эквивалент] printk для отображения значений. Возможно, realtime должно быть unsigned [и/или clock_t, поскольку ваша настройка stopwatch соответствует его значению]. Может ли recent_time поместиться в стеке?   -  person Craig Estey    schedule 24.03.2021
comment
Я добавил функцию pic_proc, которую я изменил, если это поможет. После выполнения новой установки он скомпилирует новый образ. Когда я пытаюсь загрузиться с этого образа, он блокируется, и мне приходится выключать его и возвращаться к предыдущему файлу образа.   -  person Warfujjii    schedule 24.03.2021


Ответы (1)


Основываясь на вашем новом коде [и теле функции pick_proc], единственные возможные сбои — это те, которые я описал выше в своих главных комментариях.

То есть proc_ptr должно быть допустимым (т. е. отличным от NULL), а proc_ptr->p_nr должно находиться в диапазоне, чтобы предотвратить UB.

Каковы значения NR_TASKS и NR_PROCS? Может ли recent_time поместиться в стеке? Размер стека в ядре обычно очень ограничен. Например, под Linux у вас может быть только около 4 КБ стека.

Таким образом, если NR_PROCS было (например) 32767, то recent_time нельзя было не помещать в стек.

И я не вижу причин иметь независимый массив для этого в стеке, поскольку он не будет сохраняться после вызова.

Итак, чтобы помочь в отладке, я бы добавил static к определению recent_time.

Вам нужно будет добавить код отладки, чтобы увидеть, в чем проблема.

Вот пример. Отрегулируйте printf et. др. чтобы соответствовать механизму печати ядра minux [при условии, что вы можете печатать в состоянии, в котором вызывается ваш код]:

#ifdef DEBUG
#define dbgprt(_fmt...) \
    printf(_fmt)
#else
#define dbgprt(_fmt...) \
    do { } while (0)
#endif

PRIVATE void
pick_proc()
{
    register struct proc *rp;           /* process to run */
    int q;                              /* iterate over queues */

    /* Modified Code here */
    int realtime;
    static clock_t recent_time[NR_TASKS + NR_PROCS];
    clock_t stopwatch;
    do {
        // bad pointer
        dbgprt("pick_proc: proc_ptr=%p\n",proc_ptr);
        if (proc_ptr == NULL)
            break;

        // out of range process number
        dbgprt("pick_proc: p_nr=%u\n",proc_ptr->p_nr);
        if (proc_ptr->p_nr >= NR_PROCS)
            break;

        realtime = getuptime();
        dbgprt("pick_proc: realtime=%d\n",realtime);
        recent_time[proc_ptr->p_nr + NR_TASKS] = realtime - stopwatch;
        stopwatch = realtime;
    } while (0);

    for (q = 0; q < NR_SCHED_QUEUES; q++) {
        if ((rp = rdy_head[q]) != NIL_PROC) {
            next_ptr = rp;
            if (priv(rp)->s_flags & BILLABLE)
                bill_ptr = rp;
            return;
        }
    }
}

ОБНОВЛЕНИЕ:

Я не могу не подчеркнуть важность операторов assert и/или if для предварительной проверки всего, что вы делаете для предотвращения UB. И отладка printf, например:

TRACE(VF_PICKPROC,printf("hello world\n"););

Кстати, я вытащил исходный код minix из github:

git clone https://github.com/Stichting-MINIX-Research-Foundation/minix.git

Похоже, что у вас может быть устаревший исходный код, потому что pick_proc в этом репозитории содержит больше кода, связанного с SMP.

NR_TASKS + NR_PROCS должны быть квантами времени, взятыми из таблицы процессов. Первоначально я объявил массив и секундомер в proc.h, но функция pic_proc не имела к ним доступа.

Вы действительно хотите либо использовать существующие поля в struct proc, либо добавить свои собственные вместо создания отдельного массива [индексированного по номеру процесса].

Я добавил несколько скриншотов кода, вызывающего pic_proc. Я думаю, мне нужно изменить sched и оставить pic_proc в покое. Массив в pic_proc должен получать кванты времени от каждого процесса из таблицы процессов.

Да, надеюсь, истекшее процессорное время для каждого процесса находится в struct proc. Если нет, вам придется добавить поле. Я подозреваю, что p_user_time [и/или p_sys_time] может быть тем, что вам нужно. И, может быть, p_cpu_time_left. Другие возможности: p_accounting.time_in_queue. Или p_cycles

pick_proc просто просматривает начало заданной очереди выполнения. Таким образом, вам может потребоваться изменить код, который фактически вставляет данный процесс в эту очередь. Это может быть enqueue и/или dequeue. Похоже, что они учитывают приоритет процесса [p_priority] и вытеснение.

Я пробежался глазами по коду ядра и предполагаю, что sched_proc может представлять интерес.

Но я действительно думаю, что вам нужно более внимательно изучить код ядра, чтобы увидеть, какие функции на самом деле добавляют процесс в данную очередь выполнения. И как они выбирают процесс и из какой очереди.

При добавлении процесса он может просто добавляться в хвост. Этот код должен будет сканировать очередь и [при условии, что приоритет такой же], вставлять на основе минимальной загрузки ЦП.

person Craig Estey    schedule 23.03.2021
comment
NB: в 2021 году большинство систем Linux имеют стек по умолчанию размером 8 КБ. (Который можно поднять) - person Antonin GAVREL; 24.03.2021
comment
@AntoninGAVREL Да. Я делал это по памяти [когда это было 4 КБ]. Но я хотел подчеркнуть ограниченность ресурса. И тот факт, что ядро ​​OP не загружается, по-видимому, указывает на то, что код вызывается в начале процесса загрузки. - person Craig Estey; 24.03.2021
comment
NR_TASKS + NR_PROCS должны быть квантами времени, взятыми из таблицы процессов. Первоначально я объявил массив и секундомер в proc.h, но функция pic_proc не имела к ним доступа. - person Warfujjii; 24.03.2021
comment
Я добавил несколько скриншотов кода, вызывающего pic_proc. Я думаю, мне нужно изменить sched и оставить pic_proc в покое. Массив в pic_proc должен получать кванты времени от каждого процесса из таблицы процессов. - person Warfujjii; 24.03.2021