Linux0.11 kernel-process scheduling Analysis 2. scheduling, linux0.11 Kernel

Source: Internet
Author: User

Linux0.11 kernel-process scheduling Analysis 2. scheduling, linux0.11 Kernel

[All Rights Reserved. For more information, see the source. Source: http://www.cnblogs.com/joey-hua/p/5596830.html]

In the previous article, process scheduling is ultimately to call the timer_interrupt function, in system_call.s:

#### Int32 -- (int 0x20) clock interrupt handler. The interrupt frequency is set to 100Hz (include/linux/sched. h, 5). # The scheduled chip 8253/8254 is initialized at (kernel/sched. c, 406. Therefore, jiffies increases by 1 every 10 milliseconds. # This Code adds jiffies to 1, sends the end interrupt command to the 8259 controller, and then calls the # C function do_timer (long CPL) using the current privileged level as the parameter ). When the call returns, it is transferred to detect and process the signal .. Align 2_timer_interrupt: push % ds # save ds, es and put kernel data spacepush % es # into them. % fs is used by _ system_callpush % fspushl % edx # we save % eax, % ecx, % edx as gcc doesn 'tpushl % ecx # save those functions SS function CILS. % ebxpushl % ebx # is saved as we use that in ret_sys_callpushl % eaxmovl $0x10, % eax # ds, es is set to point to the kernel data segment. Mov % ax, % dsmov % ax, % esmovl $0x17, % eax # fs is set to point to a local data segment (the data segment of the error program ). Mov % ax, % fsincl _ jiffies # Because automatic EOI is not used when the interrupt control chip is initialized, You need to issue a command to end the hardware interruption. Movb $0x20, % al # EOI to interrupt controller #1 outb % al, $0x20 # The OCW2 Command sends port 0x20. # The following three statements extract the current privileged level (0 or 3) from the selector and press it into the stack as the do_timer parameter. Movl CS (% esp), % eaxandl $3, % eax # % eax is CPL (0 or 3, 0 = supervisor) pushl % eax # do_timer (CPL) execute task switching, timing, and other work in the kernel/shched. c, 305 rows. Call _ do_timer # 'do _ timer (long CPL) 'does everything fromaddl $4, % esp # task switching to accounting... jmp ret_from_sys_call

The previous pile of push commands Save the current Register and then pop up in ret_from_sys_call.

Movl $0x10, % eax assigns the segment Selection Sub-0x10, that is, the kernel data segment Selection Sub-value to eax, and then assigns it to ds and es;

Then _ jiffies plus 1. jiffies is defined in sched. h:

Extern long volatile jiffies; // The number of tick answers (10 ms/tick) starting from the start of the boot ).

The following three statements are critical:

movl CS(%esp),%eaxandl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)pushl %eax

Obtain the value of the cs register from the register pushed above, that is, select the sub-code segment. According to the selected structure, the 0-1 bit is privileged, andl $3, % eax is the value of 0-1 bits in eax, and then the eax pressure stack is passed as the do_timer parameter, four bytes.

Now, go to the do_timer function, in sched. c:

//// The clock interrupts the C function handler and the _ timer_interrupt (row 176) in kernel/system_call.s is called. // The cpl parameter is the current privileged level 0 or 3. 0 indicates that the kernel code is being executed. // When a process runs out of time slice, the task is switched. And perform a timing update. Void do_timer (long cpl) {extern int beepcount; // Number of speaker voice time tick (kernel/chr_drv/console. c, 697) extern void sysbeepstop (void); // close the speaker (kernel/chr_drv/console. c, 691) // If the voice count reaches, the voice is disabled. (Send commands to port 0x61, with the reset bits 0 and 1. Bit 0 controls 8253 // Counter 2, bit 1 controls the speaker ). If (beepcount) if (! -- Beepcount) sysbeepstop (); // if the current privileged level (cpl) is 0 (highest, indicating that the kernel program is working), the kernel program running time is increased by stime; // [Linus refers to the kernel program as a Super User (supervisor) program. For details, see the English comments on system_call.s, line 1] // If cpl> 0, it indicates that the user program is working and utime is added. If (cpl) current-> utime ++; else current-> stime ++; // if a user's timer exists, the value of the first timer in the linked list is reduced by 1. If the value is equal to 0, call the corresponding handler and set the handler pointer to null. Remove the timer. If (next_timer) {// next_timer is the head pointer of the timer linked list (see row 270 ). Next_timer-> jiffies --; while (next_timer & next_timer-> jiffies <= 0) {void (* fn) (void ); // a function pointer definition is inserted here !!!?? Fn = next_timer-> fn; next_timer-> fn = NULL; next_timer = next_timer-> next; (fn) (); // call the processing function .}} // If the start position of the motor in the digital output register of the current floppy disk controller FDC is set, execute the floppy disk Timer Program (Row 1 ). If (current_DOR & 0xf0) do_floppy_timer (); if (-- current-> counter)> 0) return; // exit if the process has not finished running time. Current-> counter = 0; if (! Cpl) return; // for Super User Programs (kernel-state programs), scheduling is not dependent on counter values. Schedule ();}

The function of the passed cpl parameter is to add 1 to stime if it is 0, indicating that it is a kernel program. Otherwise, it is a common user program, and utime is added to 1.

User timer and so on.

Next, determine the time slice counter in the process descriptor of sched. h:

Long counter; // long counter task running time count (descending) (number of tick answers), run time slice.

If there is a time slice, do not call the scheduling function schedule (), and then subtract 1 from the time slice and exit the function.

If the time slice is used up (<= 0), set the time slice to 0, and then determine the privileged level. If the time slice is a kernel-level program, the function is directly exited. Otherwise, enter the core scheduling function schedule:

/** 'Schedule () 'is the scheduling function. This is a good code! There is no reason to modify it because it can work in all environments (for example, it can handle good responses to IO-boundary ). There is only one thing worth noting, that is, the signal * processing code here. * Note !! Task 0 is an idle ('idle') task that is called only when no other task can be run. It cannot be killed or sleep. The state Information 'state' in task 0 is never used. */Void schedule (void) {int I, next, c; struct task_struct ** p; // pointer of the task structure pointer. /* Check alarm, wake up any interruptible tasks that have got a signal * // * checks alarm (the alarm timed value of the process ), wake up any interrupted task that has received a signal * // detect alarm from the last task in the task array. For (p = & LAST_TASK; p> & FIRST_TASK; -- p) if (* p) {// if the scheduled task value alarm is set, if the time has expired (alarm <jiffies), The SIGALRM signal is set in the signal bitmap, and // The SIGALARM signal is sent to the task. Then clear the alarm. The default operation for this signal is to terminate the process. // Jiffies indicates the number of tick answers (10 ms/tick answer) from the start of the system boot ). Defined in row sched. h 139th. If (* p)-> alarm & (* p)-> alarm <jiffies) {(* p) -> signal | = (1 <(SIGALRM-1); (* p)-> alarm = 0 ;} // if there are other signals in the signal bitmap except the blocked signal and the task is interrupted, set the task to ready. '~ (_ BLOCKABLE & (* p)-> blocked) 'is used to ignore blocked signals, but SIGKILL and SIGSTOP cannot be blocked. If (* p)-> signal &~ (_ BLOCKABLE & (* p)-> blocked) & (* p)-> state = TASK_INTERRUPTIBLE) (* p)-> state = TASK_RUNNING; // set to ready (executable. }/* This is the scheduler proper: * // * this is the main part of the scheduler */while (1) {c =-1; next = 0; I = NR_TASKS; p = & task [NR_TASKS]; // This Code also starts processing cyclically from the last task in the task array and skips the array slot without tasks. Compare the counter (descending tick count of task running time) value of each ready/status task. Which of the following values is large and the running time is not long, next is // The task number to which it points. While (-- I) {if (! * -- P) continue; if (* p)-> state = TASK_RUNNING & (* p)-> counter> c) c = (* p)-> counter, next = I;} // If the comparison result shows that the counter value is greater than 0, the loop starting from row 124 is exited and the task is switched (row 141 ). If (c) break; // otherwise, the counter value of each task is updated based on the priority value of each task, and then the counter value is returned to row 125 for comparison. // Counter value is calculated as counter = counter/2 + priority. [Counter = 0?] Here, the computing process does not consider the state of the process. For (p = & LAST_TASK; p> & FIRST_TASK; -- p) if (* p)-> counter = (* p)-> counter> 1) + (* p)-> priority;} // switch to the task running with the task number "next. The value of next in Row 3 is initialized to 0. Therefore, if the system does not have any other tasks // runtime, the next value is always 0. Therefore, the scheduling function executes task 0 when the system is idle. In this case, task 0 only executes the // pause () System Call and calls this function. Switch_to (next); // switch to the task whose task number is next and run it .}

The previous sections are easy to understand. The main part of direct analysis is to find the task with the largest time slice from all the tasks, which means that the running time is relatively small, next points to this task and jumps out of the loop to switch the task.

If the time slice of all tasks is 0, the counter value of the time slice of each task is updated based on the priority value of each task. Find next again, switch the task, and call switch_to (next ):

// Macro definition: Calculate the index number (selector) of the TSS descriptor of the n tasks in the global table ). # Define _ TSS (n) (unsigned long) n) <4) + (FIRST_TSS_ENTRY <3)/** switch_to (n) the current task is switched to task nr, that is, n. First, check that task n is not the current task. * If yes, do not quit. If the recently used math * coprocessor for the task we switched to (last run), we also need to reset the TS mark in the control register cr0. * // Input: % 0-offset address of the new TSS (* & __ tmp. a); % 1-store the selected TSS value (* & __ tmp. b); // dx-new task n selector; ecx-new task pointer task [n]. // In the temporary data structure _ tmp, the value of a is a 32-bit offset value, and B is the new TSS selector. When the task is switched, the value // is useless (ignored ). When determining whether a new task has used a coprocessor for the last execution, this is done by comparing the address of the new task status segment with the address of the task status segment that has used the coprocessor in the last_task_used_math variable. # Define switch_to (n) {\ struct {long a, B;} _ tmp; \__ asm _ ("cmpl % ecx, _ current \ n \ t "\ // is task n the current task? (Current = task [n]?) "Je 1f \ n \ t" \ // yes, then do nothing and exit. "Movw % dx, % 1 \ n \ t" // select the new task ?? * & __ Tmp. B. "Xchgl % ecx, _ current \ n \ t" \ // current = task [n]; ecx = the task to be switched out. "Ljmp % 0 \ n \ t" // The CEO jumps to * & __ tmp, causing task switching. // The following statement will not be executed until the task is switched back. "Cmpl % ecx, _ last_task_used_math \ n \ t" \ // have you ever used a coprocessor for a new task? "Jne 1f \ n \ t" \ // if no value exists, the system jumps and exits. "Clts \ n" \ // The TS mark of cr0 is cleared when the coprocessor is used last time for the new task. "1:": "m" (* & __ tmp. a), "m" (* & __ tmp. b), "d" (_ TSS (n), "c" (long) task [n]);}

Before analyzing this code, you must first know that in 32-bit protection mode, there are two ways to directly initiate Task Switching:

1. call 0x0010: 0x00000000

2. jmp 0x0010: 0x00000000

In both cases, the operands of the call and jmp commands are the task's TSS descriptor Selection Sub-or task gate. When the processor executes these two commands, first use the descriptor given in the command to select a sub-Access to GDT and analyze its descriptor type.. If it is a common code segment descriptor, it is executed according to the common inter-segment transfer rule; if it is a call door, it is executed according to the call door rule;If it is a TSS descriptor or a task gate, the task is switched. The 32-bit offset given in the command is ignored because the status of all processors can be obtained from the TSS During task switching..

When a task is switched, the content of the TR register also points to the TSS of the new task.During task switching, the task register tr is automatically loaded by the CPU.. This process is as follows: first, the processor saves the field information of the current task to the TSS pointed by the TR register; then, the TR register points to the TSS of the new task, and restore the site from the TSS of the new task.

Note: The task gate descriptor can be installed in the Interrupt Descriptor Table or GDT or LDT.

With theoretical knowledge, the above Code is not difficult to analyze. The key sentence is to assign the TSS Selection Sub-value of the new task to % 1, that is, * & _ tmp. b. The value of B is the TSS sub-selection. Note that ljmp % 0 is equivalent to ljmp * % 0, indicating indirect jump, which is equivalent to "ljmp * _ tmp. a ", that is, jump to the address & __ tmp. the 48-bit logical address contained in. According to the definition of struct _ tmp, this means _ tmp. a is the offset part of the Logical Address, __tmp. the lower 16bit of B is the seg_selector (the higher 16bit is useless) part.

The task is switched until the execution of this line of command is complete! Now the process scheduling analysis is complete.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.