I was learning how to write an operating system (VI): scheduling process

Foreword

Since the introduction of the multi-process, in fact, is to switch back and forth between the process, there will be a scheduling problem between processes. But in reality is the allocation of limited resources kernel subsystem processor time between runnable processes.

A few simple CPU scheduling algorithm

  • First Come, First Served(FCFS)

It is actually a FIFO queue, the process that is first come, first implemented. When the CPU is idle, it will be allocated to the process is located in the head of the queue, and run this process is removed from the queue. FCFS scheduling coding simple and easily understood.

But the need for a process and user interaction, this scheduling algorithm will result in a very bad experience, because the turnaround time required to complete an entire task queue is very long

However, FCFS scheduling algorithm is non-preemptive. Once the CPU is assigned to a process, which will use the CPU until the CPU until the release, i.e., a program termination request or I / O.

  • Shortest Job First(SJF)

SJF scheduling algorithm would work refers to short or short process priority scheduling algorithm, each process will run its estimate associated with the shortest time to calculate the estimated time jobs into operation. This can reduce turnaround time

Shortest job first (SJF) scheduling algorithm, the length of its next CPU executes associate each process. When the CPU becomes idle, it will be assigned a process with the shortest executed by the CPU. So if FCFS can be handled by the CPU executes two processes have the same length.

  • RR

In this algorithm, a small unit of time is defined as the amount of time or time slice. Time slice size is usually 10 ~ 100ms. Ready queue as a circular queue. CPU entire ready queue of the scheduler loop, no more than one assigned CPU time slice for each process.

In order to implement RR scheduling, we will once again treated as ready queue FIFO queue process. The new process added to the tail of the ready queue. CPU scheduler selects the first process from the ready queue, set the timer interrupt after a time slice, and finally dispatching the process.

Compromise scheduling algorithm

In many processes running, and some are more concerned about the process response time, and some are more concerned with the process of turnaround time, so scheduling algorithms need to compromise, but compromise is a question of how.

Linux0.11 scheduling algorithm

schedule

Linux0.11 schedule is the most important in scheduling algorithm, but very simple

  • task_struct is used to describe a process of structure

    The counter task_struct scheduling algorithm is a key compromise, both used to represent the time slice allocated, and used to represent the priority of a process

  • First, from an array of tasks in a task starts last cycle detect some fields

    If the timing is too Alarm task settings, and has expired (alarm <jiffies), the center signal transmission SIGALARM SIGALRM signal bitmaps, namely task. Then clear alarm. There are some signals related to the amount will mention later

  • Find the maximum value of a couter, which is the minimum running time of a process, the process of switching to it

  • When completed the implementation of a back rotation is re-allocated a time slot, this time for rotation has been progress, time slice will be set to the initial value, but for those who blocked the process, will increase the time slice, which is carried out a compromise scheduling.

void schedule(void)
{
    int i,next,c;
    struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */

    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
        if (*p) {
            if ((*p)->alarm && (*p)->alarm < jiffies) {
                    (*p)->signal |= (1<<(SIGALRM-1));
                    (*p)->alarm = 0;
                }
            if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
            (*p)->state==TASK_INTERRUPTIBLE)
                (*p)->state=TASK_RUNNING;
        }

/* this is the scheduler proper: */

    while (1) {
        c = -1;
        next = 0;
        i = NR_TASKS;
        p = &task[NR_TASKS];
        while (--i) {
            if (!*--p)
                continue;
            if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
                c = (*p)->counter, next = i;
        }
        if (c) break;
        for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
            if (*p)
                (*p)->counter = ((*p)->counter >> 1) +
                        (*p)->priority;
    }
    switch_to(next);
}

init

In the way we look at sched_init, this initialization routine kernel scheduler function is to initialize some disruption and descriptors

  • First call set_tss_desc and set_ldt_desc set of processes tss and ldt 0

  • Clear mandate and an array descriptor table entry

  • After the initialization 8253 timer

  • Finally, set the clock interrupts and interrupt system calls

void sched_init(void)
{
    int i;
    struct desc_struct * p;

    if (sizeof(struct sigaction) != 16)
        panic("Struct sigaction MUST be 16 bytes");
    set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
    set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
    p = gdt+2+FIRST_TSS_ENTRY;
    for(i=1;i<NR_TASKS;i++) {
        task[i] = NULL;
        p->a=p->b=0;
        p++;
        p->a=p->b=0;
        p++;
    }
/* Clear NT, so that we won't have troubles with that later on */
    __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
    ltr(0);
    lldt(0);
    outb_p(0x36,0x43);      /* binary, mode 3, LSB/MSB, ch 0 */
    outb_p(LATCH & 0xff , 0x40);    /* LSB */
    outb(LATCH >> 8 , 0x40);    /* MSB */
    set_intr_gate(0x20,&timer_interrupt);
    outb(inb_p(0x21)&~0x01,0x21);
    set_system_gate(0x80,&system_call);
}

Set Descriptor

The action is actually _set_tssldt_desc each bit set descriptor

#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x82")

#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
    "movw %%ax,%2\n\t" \
    "rorl $16,%%eax\n\t" \
    "movb %%al,%3\n\t" \
    "movb $" type ",%4\n\t" \
    "movb $0x00,%5\n\t" \
    "movb %%ah,%6\n\t" \
    "rorl $16,%%eax" \
    ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
     "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
    )

Set interrupt

#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
    "movw %0,%%dx\n\t" \
    "movl %%eax,%1\n\t" \
    "movl %%edx,%2" \
    : \
    : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
    "o" (*((char *) (gate_addr))), \
    "o" (*(4+(char *) (gate_addr))), \
    "d" ((char *) (addr)),"a" (0x00080000))

#define set_intr_gate(n,addr) \
    _set_gate(&idt[n],14,0,addr)

#define set_system_gate(n,addr) \
    _set_gate(&idt[n],15,3,addr)    

summary

This is a major looked Linux0.11 in the scheduling algorithm, very simple, but it is to take care of the response time, but also take care of the turnaround time.

Then put a bit initialization routine kernel scheduler, in fact, according to previous set some say descriptors and interrupt handling

Guess you like

Origin www.cnblogs.com/secoding/p/11422525.html